diff --git a/bundles/org.openhab.binding.nibeheatpump/README.md b/bundles/org.openhab.binding.nibeheatpump/README.md index 2352f6470da..d8aff84a8eb 100644 --- a/bundles/org.openhab.binding.nibeheatpump/README.md +++ b/bundles/org.openhab.binding.nibeheatpump/README.md @@ -59,14 +59,15 @@ The Nibe Heat Pump binding will listen to a UDP port and parse register data fro ### Arduino An Arduino-based solution has been tested with Arduino uno + RS485 and Ethernet shields. -The [ProDiNo](https://www.kmpelectronics.eu/en-us/products/prodinoethernet.aspx) NetBoards are also supported. -A ProDiNo has an Ethernet and RS-485 port on the board. +[PRODINo ESP32 Ethernet v1](https://kmpelectronics.eu/products/prodino-esp32-ethernet-v1/) and [ProDiNo Ethernet V2](https://kmpelectronics.eu/products/prodino-ethernet-v2/) boards are also supported. +PRODINo boards have built-in Ethernet and RS-485 ports. Arduino code is available [here](https://github.com/openhab/openhab-addons/tree/main/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW). Arduino code can be build via Arduino IDE. For more details see [www.arduino.cc](https://www.arduino.cc/en/Main/Software). NibeGW configuration (such IP addresses, ports, etc) can be adapted directly by editing the code files. +PRODINo ESP32 Ethernet v1 also supports dynamic configuration and OTA updates via Wi-Fi access point. ### Raspberry Pi (or other Linux/Unix based boards) diff --git a/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/Config.h b/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/Config.h new file mode 100644 index 00000000000..79e05baa677 --- /dev/null +++ b/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/Config.h @@ -0,0 +1,220 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + * + * Author: pauli.anttila@gmail.com + * + */ +#ifndef Config_h +#define Config_h + +// ######### BOARD SELECTION ####################### + +// Enable if you use ProDiNo NetBoard V2.1 board +//#define PRODINO_BOARD + +// Enable if you use PRODINo ESP32 Ethernet v1 (Enable also HARDWARE_SERIAL_WITH_PINS in NibeGW.h) +#define PRODINO_BOARD_ESP32 + +// Enable if ENC28J60 LAN module is used +//#define TRANSPORT_ETH_ENC28J60 + +// ######### CONFIGURATION ####################### + +// Enable dynamic configuration mode via WiFi connection (supported only by the PRODINO_BOARD_ESP32 board) +// Dynamic configuration mode is loaded if input 0 is ON during boot +// When dynamic configuration mode is activated, login to the 'Bleeper' WiFi Access point: +// 1. Configuration page is available on IP 192.168.4.1 port 80 (http://192.168.4.1). +// 2. OTA update page is available on IP 192.168.4.1 port 8080 (http://192.168.4.1:8080/update). +// Install following libraries via Arduino IDE library manager: +// 1. Bleeper (tested with version 1.1.0) +// 2. ElegantOTA (tested with version 2.2.9) +#define ENABLE_DYNAMIC_CONFIG + +// Enable debug printouts. +#define ENABLE_DEBUG +#define VERBOSE_LEVEL 1 +#define ENABLE_SERIAL_DEBUG +#define ENABLE_REMOTE_DEBUG // Remote debug is available in telnet port 23 + +#define BOARD_NAME "Arduino NibeGW" + +// Ethernet configuration +#define BOARD_MAC "DE:AD:BE:EF:FE:ED" +#define BOARD_IP "192.168.1.100" +#define DNS_SERVER "192.168.1.1" +#define GATEWAY_IP "192.168.1.1" +#define NETWORK_MASK "255.255.255.0" + +// UDP ports for incoming messages +#define INCOMING_PORT_READCMDS 9999 +#define INCOMING_PORT_WRITECMDS 10000 + +// Target IP address and port where Nibe UDP packets are send +#define TARGET_IP "192.168.1.101" +#define TARGET_PORT 9999 + +// Delay before initialize ethernet on startup in seconds +#define ETH_INIT_DELAY 5 + +// Send acknowledge PDU's to Nibe +#define SEND_ACK true + +// Ack following periperial messages +#define ACK_MODBUS40 true +#define ACK_SMS40 false +#define ACK_RMU40 false + + + +// Used serial port and direction change pin for RS-485 port +#if defined(PRODINO_BOARD) + #define RS485_PORT Serial1 + #define RS485_DIRECTION_PIN 3 +#elif defined(PRODINO_BOARD_ESP32) + #define WDT_TIMEOUT 2 + #define RS485_RX_PIN 4 + #define RS485_TX_PIN 16 + #define RS485_DIRECTION_PIN 2 +#else + #define RS485_PORT Serial + #define RS485_DIRECTION_PIN 2 +#endif + + + + + +// ######### VARIABLES ####################### + +#if defined(PRODINO_BOARD_ESP32) && defined(ENABLE_DYNAMIC_CONFIG) + + #include "Bleeper.h" // https://github.com/workilabs/Bleeper + #include "ElegantOTA.h" // https://github.com/ayushsharma82/ElegantOTA + + WebServer otaServer(8080); + + class EthConfig: public Configuration { + public: + persistentStringVar(mac, BOARD_MAC); + persistentStringVar(ip, BOARD_IP); + persistentStringVar(dns, DNS_SERVER); + persistentStringVar(gateway, GATEWAY_IP); + persistentStringVar(mask, NETWORK_MASK); + persistentIntVar(initDelay, ETH_INIT_DELAY); + }; + + class NibeAckConfig: public Configuration { + public: + persistentIntVar(sendAck, SEND_ACK); + persistentIntVar(modbus40, ACK_MODBUS40); + persistentIntVar(sms40, ACK_SMS40); + persistentIntVar(rmu40, ACK_RMU40); + }; + + class NibeConfig: public Configuration { + public: + persistentStringVar(targetIp, TARGET_IP); + persistentIntVar(targetPort, TARGET_PORT); + persistentIntVar(readCmdsPort, INCOMING_PORT_READCMDS); + persistentIntVar(writeCmdsPort, INCOMING_PORT_WRITECMDS); + subconfig(NibeAckConfig, ack); + }; + + #ifdef ENABLE_DEBUG + class DebugConfig: public Configuration { + public: + persistentIntVar(verboseLevel, VERBOSE_LEVEL); + }; + #endif + + class Config: public RootConfiguration { + public: + persistentStringVar(boardName, BOARD_NAME); + subconfig(EthConfig, eth); + subconfig(NibeConfig, nibe); + #ifdef ENABLE_DEBUG + subconfig(DebugConfig, debug); + #endif + } config; + +#else + + typedef struct Config { + String boardName; + struct { + String mac; + String ip; + String dns; + String gateway; + String mask; + uint16_t initDelay; + } eth; + + struct { + String targetIp; + uint16_t targetPort; + uint16_t readCmdsPort; + uint16_t writeCmdsPort; + struct { + uint8_t sendAck; + uint8_t modbus40; + uint8_t sms40; + uint8_t rmu40; + } ack; + } nibe; + + + #ifdef ENABLE_DEBUG + struct { + uint8_t verboseLevel; + } debug; + #endif + + }; + + Config config = { + BOARD_NAME, + + { + BOARD_MAC, + BOARD_IP, + DNS_SERVER, + GATEWAY_IP, + NETWORK_MASK, + ETH_INIT_DELAY + }, + + { + TARGET_IP, + TARGET_PORT, + INCOMING_PORT_READCMDS, + INCOMING_PORT_WRITECMDS, + + { + SEND_ACK, + ACK_MODBUS40, + ACK_SMS40, + ACK_RMU40, + }, + }, + + + #ifdef ENABLE_DEBUG + { + VERBOSE_LEVEL + } + #endif + + }; +#endif + +#endif diff --git a/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/Debug.h b/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/Debug.h new file mode 100644 index 00000000000..7b119101f56 --- /dev/null +++ b/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/Debug.h @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + * + * Author: pauli.anttila@gmail.com + * + */ +#ifndef Debug_h +#define Debug_h + +#ifdef ENABLE_DEBUG + #define DEBUG_PRINT_MSG(level, message) if (config.debug.verboseLevel >= level) { debugPrint(message); } + #define DEBUG_PRINT_VARS(level, message, ...) if (config.debug.verboseLevel >= level) { sprintf(debugBuf, message, __VA_ARGS__); debugPrint(debugBuf); } + #define DEBUG_PRINT_ARRAY(level, data, len) if (config.debug.verboseLevel >= level) { for (int i = 0; i < len; i++) { sprintf(debugBuf, "%02X", data[i]); debugPrint(debugBuf); }} + + #define DEBUG_BUFFER_SIZE 80 + char debugBuf[DEBUG_BUFFER_SIZE]; + + #ifdef ENABLE_REMOTE_DEBUG + EthernetServer telnet(23); + #endif + + void debugPrint(char* data) { + #ifdef ENABLE_SERIAL_DEBUG + Serial.print(data); + #endif + + #ifdef ENABLE_REMOTE_DEBUG + telnet.print(data); + #endif + } + +#else + #define DEBUG_PRINT_MSG(level, message) + #define DEBUG_PRINT_VARS(level, message, ...) + #define DEBUG_PRINT_ARRAY(level, data, len) +#endif + +#endif diff --git a/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/NibeGW.ino b/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/NibeGW.ino index 899f9b7a3ff..68c84f37523 100644 --- a/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/NibeGW.ino +++ b/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/NibeGW.ino @@ -20,337 +20,465 @@ * 27.6.2014 v1.02 Fixed compile error and added Ethernet initialization delay. * 29.6.2015 v2.00 Bidirectional support. * 18.2.2017 v3.00 Redesigned. - * 14.3.2021 v3.01 Fix Prodino build + fixed UDP issue + debug improvements. + * 14.3.2021 v3.01 Fix Prodino build + fixed UDP issue + debug improvements + * 3.7.2022 v4.00 Send messages to IP address received from the UDP messages + * 13.7.2022 v4.01 Fixed target IP address issue + * 29.7.2022 v5.00 New configuration model and PRODINo ESP32 Ethernet v1 support with OTA update */ -// ######### CONFIGURATION ####################### - -#define VERSION "3.01" - -// Enable if you use ProDiNo board -// Have been tested with KMPDinoEthernet v1.6.1 (https://github.com/kmpelectronics/Arduino/tree/master/KMPDinoEthernet/Releases) -//#define PRODINO_BOARD - -// Enable if ENC28J60 LAN module is used -//#define TRANSPORT_ETH_ENC28J60 - -// Enable if you use STM32 NUCLEO-F429ZI -//#define STM32_F429ZI_BOARD - - -// Enable debug printouts -//#define ENABLE_DEBUG - -// Enable UDP debug printouts, listen printouts e.g. via netcat (nc -l -u 50000) -//#define ENABLE_UDP_DEBUG - -#define VERBOSE_LEVEL 1 - -#define BOARD_NAME "Arduino NibeGW" -#define BOARD_MAC { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED } -#define BOARD_IP { 192, 168, 1, 50 } -#define GATEWAY_IP { 192, 168, 1, 1 } -#define NETWORK_MASK { 255, 255, 255, 0 } -#define INCOMING_PORT_READCMDS 9999 -#define INCOMING_PORT_WRITECMDS 10000 - -#define TARGET_IP 192, 168, 1, 19 -#define TARGET_PORT 9999 -#define TARGET_DEBUG_PORT 50000 - -// Delay before initialize ethernet on startup in seconds -#define ETH_INIT_DELAY 5 - -// Used serial port and direction change pin for RS-485 port -// Note! Select if Serial is SW or HW serial port in NibeGw.h -#ifdef PRODINO_BOARD - #define RS485_PORT Serial1 - #define RS485_DIRECTION_PIN 3 -#elif defined STM32_F429ZI_BOARD - #include - HardwareSerial Serial1(PG9,PG14); - #define RS485_PORT Serial1 - #define RS485_DIRECTION_PIN PF15 -#else - #define RS485_PORT Serial - #define RS485_DIRECTION_PIN 2 -#endif - -#define ACK_MODBUS40 true -#define ACK_SMS40 false -#define ACK_RMU40 false -#define SEND_ACK true - -#define DEBUG_BUFFER_SIZE 80 +#define VERSION "5.00" // ######### INCLUDES ####################### -#ifdef TRANSPORT_ETH_ENC28J60 +#include "Config.h" + +#if defined(PRODINO_BOARD) + #include "KmpDinoEthernet.h" + #include "KMPCommon.h" + #include "Ethernet/utility/w5100.h" +#elif defined(PRODINO_BOARD_ESP32) + #include + #include "KMPProDinoESP32.h" + #include "KMPCommon.h" +#elif defined(TRANSPORT_ETH_ENC28J60) #include -#elif defined STM32_F429ZI_BOARD - #include - #include - #include -#elif defined PRODINO_BOARD - #include - #include "KmpDinoEthernet.h" - #include "KMPCommon.h" - #include "Ethernet/utility/w5100.h" #else #include #include #include #endif - -#ifdef STM32_F429ZI_BOARD - #include -#else - #include + +#if !defined(PRODINO_BOARD_ESP32) + #include #endif #include "NibeGw.h" +#include "Debug.h" // ######### VARIABLES ####################### -// The media access control (ethernet hardware) address for the shield -byte mac[] = BOARD_MAC; +boolean ethInitialized = false; -//The IP address for the shield -byte ip[] = BOARD_IP; - -//The IP address of the gateway -byte gw[] = GATEWAY_IP; - -//The network mask -byte mask[] = NETWORK_MASK; - -boolean ethernetInitialized = false; - -// Target IP address and port where Nibe UDP packets are send -IPAddress targetIp(TARGET_IP); +IPAddress targetIp; EthernetUDP udp; EthernetUDP udp4writeCmnds; -NibeGw nibegw(&RS485_PORT, RS485_DIRECTION_PIN); - -// ######### DEBUG ####################### - -#define DEBUG_BUFFER_SIZE 80 - -#ifdef ENABLE_DEBUG - #define DEBUG_PRINT(level, message) if (verbose >= level) { debugPrint(message); } - #define DEBUG_PRINTDATA(level, message, data) if (verbose >= level) { sprintf(debugBuf, message, data); debugPrint(debugBuf); } - #define DEBUG_PRINTARRAY(level, data, len) if (verbose >= level) { for (int i = 0; i < len; i++) { sprintf(debugBuf, "%02X", data[i]); debugPrint(debugBuf); }} +#if defined(PRODINO_BOARD_ESP32) + HardwareSerial RS485_PORT(1); + NibeGw nibegw(&RS485_PORT, RS485_DIRECTION_PIN, RS485_RX_PIN, RS485_TX_PIN); #else - #define DEBUG_PRINT(level, message) - #define DEBUG_PRINTDATA(level, message, data) - #define DEBUG_PRINTARRAY(level, data, len) + NibeGw nibegw(&RS485_PORT, RS485_DIRECTION_PIN); #endif -#ifdef ENABLE_DEBUG -char verbose = VERBOSE_LEVEL; -char debugBuf[DEBUG_BUFFER_SIZE]; +#if defined(PRODINO_BOARD_ESP32) && defined(ENABLE_DYNAMIC_CONFIG) + boolean dynamicConfigStarted = false; + + class ConfigObserver: public ConfigurationObserver { + public: + void onConfigurationChanged(const ConfigurationPropertyChange value) { + DEBUG_PRINT_VARS(0, "Configuration parameter '%s' changed from '%s' to '%s'\n", + String(value.key).c_str(), + String(value.oldValue).c_str(), + String(value.newValue).c_str()); + } + }; -void debugPrint(char* data) -{ -#ifdef ENABLE_UDP_DEBUG - if (ethernetInitialized) - { - udp.beginPacket(targetIp, TARGET_DEBUG_PORT); - udp.write(data); - udp.endPacket(); - } #endif -#ifdef PRODINO_BOARD - Serial.print(data); -#endif -} -#endif - -// ######### FUNCTION DEFINITION ###################### - -void nibeCallbackMsgReceived(const byte* const data, int len); -int nibeCallbackTokenReceived(eTokenType token, byte* data); -void sendUdpPacket(const byte * const data, int len); -void initializeEthernet(); - - // ######### SETUP ####################### -void setup() -{ +void setup() { + #if defined(PRODINO_BOARD_ESP32) + KMPProDinoESP32.begin(ProDino_ESP32_Ethernet); + KMPProDinoESP32.setStatusLed(red); + #endif + + #if defined(PRODINO_BOARD_ESP32) && defined(ENABLE_DYNAMIC_CONFIG) + if (isDynamicConfigModeActivated()) { + setupDynamicConfigMode(); + } else { + setupStaticConfigMode(); + } + #else + setupStaticConfigMode(); + #endif + +} + +void setupStaticConfigMode() { + #if defined(PRODINO_BOARD_ESP32) && defined(ENABLE_DYNAMIC_CONFIG) + // Use temporarily longer wathdog time as possible flash formating might take a while + esp_task_wdt_init(60, true); + esp_task_wdt_add(NULL); + KMPProDinoESP32.setStatusLed(white); + Bleeper + .configuration + .set(&config) + .done() + .storage + .set(new SPIFFSStorage()) + .done() + .init(); + esp_task_wdt_reset(); + KMPProDinoESP32.setStatusLed(red); + #endif + + #if defined(PRODINO_BOARD_ESP32) + Serial.begin(115200, SERIAL_8N1); + #elif defined(PRODINO_BOARD) + Serial.begin(115200, SERIAL_8N1); + DinoInit(); + #endif + // Start watchdog -#ifdef STM32_F429ZI_BOARD - IWatchdog.begin(2000000); // 2 sec -#else - wdt_enable (WDTO_2S); -#endif + #if defined(PRODINO_BOARD_ESP32) + esp_task_wdt_init(WDT_TIMEOUT, true); + esp_task_wdt_add(NULL); + #else + wdt_enable(WDTO_2S); + #endif nibegw.setCallback(nibeCallbackMsgReceived, nibeCallbackTokenReceived); - nibegw.setAckModbus40Address(ACK_MODBUS40); - nibegw.setAckSms40Address(ACK_SMS40); - nibegw.setAckRmu40Address(ACK_RMU40); - nibegw.setSendAcknowledge(SEND_ACK); + nibegw.setSendAcknowledge(config.nibe.ack.sendAck); + nibegw.setAckModbus40Address(config.nibe.ack.modbus40); + nibegw.setAckSms40Address(config.nibe.ack.sms40); + nibegw.setAckRmu40Address(config.nibe.ack.rmu40); -#ifdef ENABLE_NIBE_DEBUG - nibegw.setDebugCallback(nibeDebugCallback); - nibegw.setVerboseLevel(VERBOSE_LEVEL); -#endif + #ifdef ENABLE_NIBE_DEBUG + nibegw.setDebugCallback(nibeDebugCallback); + nibegw.setVerboseLevel(config.debug.level); + #endif -#ifdef PRODINO_BOARD - DinoInit(); - Serial.begin(115200, SERIAL_8N1); -#endif + targetIp.fromString(config.nibe.targetIp); - DEBUG_PRINTDATA(0, "%s ", BOARD_NAME); - DEBUG_PRINTDATA(0, "version %s\n", VERSION); - DEBUG_PRINT(0, "Started\n"); + DEBUG_PRINT_VARS(0, "%s version %s Started\n", config.boardName.c_str(), VERSION); } +#if defined(PRODINO_BOARD_ESP32) && defined(ENABLE_DYNAMIC_CONFIG) + +boolean isDynamicConfigModeActivated() { + if (KMPProDinoESP32.getOptoInState(0)) { + delay(50); + if (KMPProDinoESP32.getOptoInState(0)) { + return true; + } + } + return false; +} + +void setupDynamicConfigMode() { + KMPProDinoESP32.setStatusLed(white); + Bleeper + .verbose(115200) + .configuration + .set(&config) + .addObserver(new ConfigObserver(), {}) + .done() + .configurationInterface + .addDefaultWebServer() + .done() + .connection + .setSingleConnectionFromPriorityList({ + new AP() + }) + .done() + .storage + .set(new SPIFFSStorage()) + .done() + .init(); + + ElegantOTA.begin(&otaServer); + otaServer.begin(); + + dynamicConfigStarted = true; + KMPProDinoESP32.setStatusLed(blue); +} +#endif + // ######### MAIN LOOP ####################### -void loop() -{ -#ifdef STM32_F429ZI_BOARD - IWatchdog.reload(); -#else - wdt_reset(); -#endif +void loop() { + #if defined(PRODINO_BOARD_ESP32) && defined(ENABLE_DYNAMIC_CONFIG) + if (dynamicConfigStarted) { + loopDynamicConfigMode(); + } else { + loopNormalMode(); + } + #else + loopNormalMode(); + #endif +} +void loopNormalMode() { + #if defined(PRODINO_BOARD_ESP32) + esp_task_wdt_reset(); + #else + wdt_reset(); + #endif + long now = millis() / 1000; - if (!nibegw.connected()) - { + if (!nibegw.connected()) { nibegw.connect(); - } - else - { - do - { + } else { + do { nibegw.loop(); -#ifdef TRANSPORT_ETH_ENC28J60 - Ethernet.maintain(); -#endif + #ifdef TRANSPORT_ETH_ENC28J60 + Ethernet.maintain(); + #endif } while (nibegw.messageStillOnProgress()); } - if (!ethernetInitialized && now >= ETH_INIT_DELAY) - { + if (!ethInitialized && now >= config.eth.initDelay) { initializeEthernet(); + #ifdef ENABLE_DEBUG + telnet.begin(); + #endif } + + #if defined(ENABLE_DEBUG) && defined(ENABLE_REMOTE_DEBUG) + if (ethInitialized) { + handleTelnet(); + } + #endif } +#if defined(PRODINO_BOARD_ESP32) && defined(ENABLE_DYNAMIC_CONFIG) +void loopDynamicConfigMode() { + Bleeper.handle(); + otaServer.handleClient(); +} +#endif + // ######### FUNCTIONS ####################### -void initializeEthernet() -{ - DEBUG_PRINT(1, "Initializing Ethernet\n"); - Ethernet.begin(mac, ip, gw, mask); +void initializeEthernet() { + DEBUG_PRINT_MSG(1, "Initializing Ethernet\n"); -#ifdef PRODINO_BOARD - W5100.setRetransmissionCount(1); -#endif + uint8_t mac[6]; + sscanf(config.eth.mac.c_str(), "%x:%x:%x:%x:%x:%x", mac, mac+1, mac+2, mac+3, mac+4, mac+5); + + IPAddress ip; + IPAddress dns; + IPAddress gw; + IPAddress mask; + + ip.fromString(config.eth.ip); + dns.fromString(config.eth.dns); + gw.fromString(config.eth.gateway); + mask.fromString(config.eth.mask); + + Ethernet.begin(mac, ip, dns, gw, mask); - ethernetInitialized = true; - udp.begin(INCOMING_PORT_READCMDS); - udp4writeCmnds.begin(INCOMING_PORT_WRITECMDS); + #if defined(PRODINO_BOARD_ESP32) + Ethernet.setRetransmissionCount(1); + Ethernet.setRetransmissionTimeout(50); + #elif defined(PRODINO_BOARD) + W5100.setRetransmissionCount(1); + W5100.setRetransmissionTime(50); + #endif + + ethInitialized = true; + udp.begin(config.nibe.readCmdsPort); + udp4writeCmnds.begin(config.nibe.writeCmdsPort); -#ifdef ENABLE_DEBUG - DEBUG_PRINTDATA(0, "%s ", BOARD_NAME); - DEBUG_PRINTDATA(0, "version %s\n", VERSION); - sprintf(debugBuf, "MAC=%02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - DEBUG_PRINT(0, debugBuf); - sprintf(debugBuf, "IP=%d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); - DEBUG_PRINT(0, debugBuf); - sprintf(debugBuf, "GW=%d.%d.%d.%d\n", gw[0], gw[1], gw[2], gw[3]); - DEBUG_PRINT(0, debugBuf); - sprintf(debugBuf, "TARGET IP=%d.%d.%d.%d\n", TARGET_IP); - DEBUG_PRINT(0, debugBuf); - DEBUG_PRINTDATA(0, "TARGET PORT=%d\n", TARGET_PORT); - DEBUG_PRINTDATA(0, "INCOMING_PORT_READCMDS=%d\n", INCOMING_PORT_READCMDS); - DEBUG_PRINTDATA(0, "INCOMING_PORT_WRITECMDS=%d\n", INCOMING_PORT_WRITECMDS); - DEBUG_PRINTDATA(0, "TARGET PORT=%d\n", TARGET_PORT); - DEBUG_PRINTDATA(0, "ACK_MODBUS40=%s\n", ACK_MODBUS40 ? "true" : "false"); - DEBUG_PRINTDATA(0, "ACK_SMS40=%s\n", ACK_SMS40 ? "true" : "false"); - DEBUG_PRINTDATA(0, "ACK_RMU40=%s\n", ACK_RMU40 ? "true" : "false"); - DEBUG_PRINTDATA(0, "SEND_ACK=%s\n", SEND_ACK ? "true" : "false"); - DEBUG_PRINTDATA(0, "ETH_INIT_DELAY=%d\n", ETH_INIT_DELAY); - DEBUG_PRINTDATA(0, "RS485_DIRECTION_PIN=%d\n", RS485_DIRECTION_PIN); -#endif + printInfo(); + + #if defined(PRODINO_BOARD_ESP32) + KMPProDinoESP32.offStatusLed(); + #endif } -void nibeCallbackMsgReceived(const byte* const data, int len) -{ - if (ethernetInitialized) - { +void nibeCallbackMsgReceived(const byte* const data, int len) { + #if defined(PRODINO_BOARD_ESP32) + KMPProDinoESP32.setStatusLed(green); + #endif + + if (ethInitialized) { sendUdpPacket(data, len); } + + #if defined(PRODINO_BOARD_ESP32) + KMPProDinoESP32.offStatusLed(); + #endif } - -int nibeCallbackTokenReceived(eTokenType token, byte* data) -{ +int nibeCallbackTokenReceived(eTokenType token, byte* data) { int len = 0; - if (ethernetInitialized) - { - if (token == READ_TOKEN) - { - DEBUG_PRINT(3, "Read token received from nibe\n"); + if (ethInitialized) { + if (token == READ_TOKEN) { + DEBUG_PRINT_MSG(3, "Read token received from nibe\n"); int packetSize = udp.parsePacket(); if (packetSize) { + #if defined(PRODINO_BOARD_ESP32) + KMPProDinoESP32.setStatusLed(white); + #endif + targetIp = udp.remoteIP(); len = udp.read(data, packetSize); - DEBUG_PRINTDATA(2, "Send read command to nibe, len=%d, ", len); - DEBUG_PRINT(1, " data in: "); - DEBUG_PRINTARRAY(1, data, len) - DEBUG_PRINT(1, "\n"); -#ifdef TRANSPORT_ETH_ENC28J60 - udp.flush(); - udp.stop(); - udp.begin(INCOMING_PORT_READCMDS); -#endif + DEBUG_PRINT_VARS(2, "Send read command to nibe, len=%d, ", len); + DEBUG_PRINT_MSG(1, " data in: "); + DEBUG_PRINT_ARRAY(1, data, len) + DEBUG_PRINT_MSG(1, "\n"); + + #if defined(TRANSPORT_ETH_ENC28J60) + udp4readCmnds.flush(); + udp4readCmnds.stop(); + udp4readCmnds.begin(config.nibe.readCmdsPort); + #endif } - } - else if (token == WRITE_TOKEN) - { - DEBUG_PRINT(3, "Write token received from nibe\n"); + } else if (token == WRITE_TOKEN) { + DEBUG_PRINT_MSG(3, "Write token received from nibe\n"); int packetSize = udp4writeCmnds.parsePacket(); if (packetSize) { + #if defined(PRODINO_BOARD_ESP32) + KMPProDinoESP32.setStatusLed(orange); + #endif + targetIp = udp.remoteIP(); len = udp4writeCmnds.read(data, packetSize); - DEBUG_PRINTDATA(2, "Send write command to nibe, len=%d, ", len); - DEBUG_PRINT(1, " data in: "); - DEBUG_PRINTARRAY(1, data, len) - DEBUG_PRINT(1, "\n"); -#ifdef TRANSPORT_ETH_ENC28J60 - udp4writeCmnds.flush(); - udp4writeCmnds.stop(); - udp4writeCmnds.begin(INCOMING_PORT_WRITECMDS); -#endif + DEBUG_PRINT_VARS(2, "Send write command to nibe, len=%d, ", len); + DEBUG_PRINT_MSG(1, " data in: "); + DEBUG_PRINT_ARRAY(1, data, len) + DEBUG_PRINT_MSG(1, "\n"); + + #if defined(TRANSPORT_ETH_ENC28J60) + udp4writeCmnds.flush(); + udp4writeCmnds.stop(); + udp4writeCmnds.begin(config.nibe.writeCmdsPort); + #endif } } + + #if defined(PRODINO_BOARD_ESP32) + KMPProDinoESP32.offStatusLed(); + #endif } return len; } -void nibeDebugCallback(byte verbose, char* data) -{ - DEBUG_PRINT(verbose, data); +void nibeDebugCallback(byte level, char* data) { + DEBUG_PRINT_MSG(level, data); } -void sendUdpPacket(const byte * const data, int len) -{ -#ifdef ENABLE_DEBUG - sprintf(debugBuf, "Sending UDP packet to %d.%d.%d.%d:", TARGET_IP); - DEBUG_PRINT(2, debugBuf); - DEBUG_PRINTDATA(2, "%d", TARGET_PORT); - DEBUG_PRINTDATA(2, ", len=%d, ", len); - DEBUG_PRINT(1, "data out: "); - DEBUG_PRINTARRAY(1, data, len) - DEBUG_PRINT(1, "\n"); -#endif +void sendUdpPacket(const byte* const data, int len) { + #ifdef ENABLE_DEBUG + DEBUG_PRINT_VARS(2, "Sending UDP packet to %s:%d, len=%d", IPtoString(targetIp).c_str(), config.nibe.targetPort, len); + DEBUG_PRINT_MSG(1, " data out: "); + DEBUG_PRINT_ARRAY(1, data, len) + DEBUG_PRINT_MSG(1, "\n"); + #endif - udp.beginPacket(targetIp, TARGET_PORT); + #if defined(PRODINO_BOARD_ESP32) + EthernetLinkStatus linkStatus = Ethernet.linkStatus(); + if (linkStatus != LinkON) { + DEBUG_PRINT_VARS(0, "Ethernet link is down, link status = %d\n", linkStatus); + return; + } + #endif + + udp.beginPacket(targetIp, config.nibe.targetPort); + udp.write(data, len); int retval = udp.endPacket(); - DEBUG_PRINTDATA(3, "UDP packet sent %s\n", retval == 0 ? "failed" : "succeed"); + if (retval) { + DEBUG_PRINT_MSG(3, "UDP packet sent succeed\n"); + } else { + DEBUG_PRINT_MSG(1, "UDP packet sent failed\n"); + } } + +String IPtoString(const IPAddress& address) { + return String() + address[0] + "." + address[1] + "." + address[2] + "." + address[3]; +} + +void printInfo() { + #ifdef ENABLE_DEBUG + DEBUG_PRINT_VARS(0, "%s version %s\nUsing configuration:\n", config.boardName.c_str(), VERSION); + DEBUG_PRINT_VARS(0, "MAC=%s\n", config.eth.mac.c_str()); + DEBUG_PRINT_VARS(0, "IP=%s\n", config.eth.ip.c_str()); + DEBUG_PRINT_VARS(0, "DNS=%s\n", config.eth.dns.c_str()); + DEBUG_PRINT_VARS(0, "MASK=%s\n", config.eth.mask.c_str()); + DEBUG_PRINT_VARS(0, "GATEWAY=%s\n", config.eth.gateway.c_str()); + DEBUG_PRINT_VARS(0, "ETH_INIT_DELAY=%d\n", config.eth.initDelay); + DEBUG_PRINT_VARS(0, "TARGET_IP=%s\n", IPtoString(targetIp).c_str()); + DEBUG_PRINT_VARS(0, "TARGET_PORT=%d\n", config.nibe.targetPort); + DEBUG_PRINT_VARS(0, "INCOMING_PORT_READCMDS=%d\n", config.nibe.readCmdsPort); + DEBUG_PRINT_VARS(0, "INCOMING_PORT_WRITECMDS=%d\n", config.nibe.writeCmdsPort); + DEBUG_PRINT_VARS(0, "SEND_ACK=%s\n", config.nibe.ack.sendAck ? "true" : "false"); + if (config.nibe.ack.sendAck) { + DEBUG_PRINT_VARS(0, "ACK_MODBUS40=%s\n", config.nibe.ack.modbus40 ? "true" : "false"); + DEBUG_PRINT_VARS(0, "ACK_SMS40=%s\n", config.nibe.ack.sms40 ? "true" : "false"); + DEBUG_PRINT_VARS(0, "ACK_RMU40=%s\n", config.nibe.ack.rmu40 ? "true" : "false"); + } + #endif + DEBUG_PRINT_VARS(0, "VERBOSE_LEVEL=%d\n", config.debug.verboseLevel); + + #if defined(ENABLE_DEBUG) && defined(ENABLE_REMOTE_DEBUG) + DEBUG_PRINT_MSG(0, "REMOTE_DEBUG_ENABLED=true\n"); + #else + DEBUG_PRINT_MSG(0, "REMOTE_DEBUG_ENABLED=false\n"); + #endif + + #if defined(PRODINO_BOARD_ESP32) && defined(ENABLE_DYNAMIC_CONFIG) + DEBUG_PRINT_MSG(0, "DYNAMIC_CONFIG_ENABLED=true\n"); + #else + DEBUG_PRINT_MSG(0, "DYNAMIC_CONFIG_ENABLED=false\n"); + #endif +} + +#if defined(ENABLE_DEBUG) && defined(ENABLE_REMOTE_DEBUG) +void handleTelnet() { + EthernetClient client = telnet.available(); + + if (client) { + char c = client.read(); + + switch (c) { + + case '?': + case 'h': + client.println(config.boardName.c_str()); + client.println("Commands:"); + client.println(" E -> exit"); + client.println(" i -> info"); + #ifdef ENABLE_DEBUG + client.println(" 1 -> set verbose level to 1"); + client.println(" 2 -> set verbose level to 2"); + client.println(" 3 -> set verbose level to 3"); + client.println(" 4 -> set verbose level to 4"); + client.println(" 5 -> set verbose level to 5"); + #endif + break; + + case 'i': + printInfo(); + break; + + case 'E': + client.println("Connection closed"); + client.flush(); + client.stop(); + break; + + #ifdef ENABLE_DEBUG + case '1': + case '2': + case '3': + case '4': + case '5': + client.print("Setting verbose level to "); + client.println(c); + config.debug.verboseLevel = c - 0x30; + break; + #endif + + case '\n': + case '\r': + break; + + default: + client.print("Unknown command "); + client.println(c); + } + } +} +#endif diff --git a/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/NibeGw.cpp b/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/NibeGw.cpp index b59e44fbfa5..fe5ca0960c8 100644 --- a/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/NibeGw.cpp +++ b/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/NibeGw.cpp @@ -19,12 +19,18 @@ #include "NibeGw.h" #include "Arduino.h" -#ifdef HARDWARE_SERIAL +#if defined(HARDWARE_SERIAL_WITH_PINS) +NibeGw::NibeGw(HardwareSerial* serial, int RS485DirectionPin, int RS485RxPin, int RS485TxPin) +#elif defined(HARDWARE_SERIAL) NibeGw::NibeGw(HardwareSerial* serial, int RS485DirectionPin) #else NibeGw::NibeGw(Serial_* serial, int RS485DirectionPin) #endif { + #if defined(HARDWARE_SERIAL_WITH_PINS) + this->RS485RxPin = RS485RxPin; + this->RS485TxPin = RS485TxPin; + #endif verbose = 0; ackModbus40 = true; ackSms40 = false; @@ -44,7 +50,13 @@ void NibeGw::connect() if (!connectionState) { state = STATE_WAIT_START; - RS485->begin(9600, SERIAL_8N1); + + #if defined(HARDWARE_SERIAL_WITH_PINS) + RS485->begin(9600, SERIAL_8N1, RS485RxPin, RS485TxPin); + #else + RS485->begin(9600, SERIAL_8N1); + #endif + connectionState = true; } } @@ -377,4 +389,3 @@ boolean NibeGw::shouldAckNakSend(byte address) return false; } - diff --git a/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/NibeGw.h b/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/NibeGw.h index 2b18e0df078..2a9abe6a185 100644 --- a/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/NibeGw.h +++ b/bundles/org.openhab.binding.nibeheatpump/contrib/NibeGW/Arduino/NibeGW/NibeGw.h @@ -38,7 +38,9 @@ #include -#define HARDWARE_SERIAL +#define HARDWARE_SERIAL_WITH_PINS +//#define HARDWARE_SERIAL + //#define ENABLE_NIBE_DEBUG // state machine states @@ -78,10 +80,14 @@ class NibeGw byte directionPin; byte buffer[MAX_DATA_LEN]; byte index; - #ifdef HARDWARE_SERIAL - HardwareSerial* RS485; + #if defined(HARDWARE_SERIAL_WITH_PINS) + HardwareSerial* RS485; + int RS485RxPin; + int RS485TxPin; + #elif defined(HARDWARE_SERIAL) + HardwareSerial* RS485; #else - Serial_* RS485; + Serial_* RS485; #endif NIBE_CALLBACK_MSG_RECEIVED; NIBE_CALLBACK_MSG_RECEIVED_TOKEN; @@ -103,10 +109,12 @@ class NibeGw #endif public: - #ifdef HARDWARE_SERIAL - NibeGw(HardwareSerial* serial, int RS485DirectionPin); + #if defined(HARDWARE_SERIAL_WITH_PINS) + NibeGw(HardwareSerial* serial, int RS485DirectionPin, int RS485RxPin, int RS485TxPin); + #elif defined(HARDWARE_SERIAL) + NibeGw(HardwareSerial* serial, int RS485DirectionPin); #else - NibeGw(Serial_* serial, int RS485DirectionPin); + NibeGw(Serial_* serial, int RS485DirectionPin); #endif NibeGw& setCallback(NIBE_CALLBACK_MSG_RECEIVED, NIBE_CALLBACK_MSG_RECEIVED_TOKEN);