[nibeheatpump] Added PRODINo ESP32 Ethernet v1 board support (#13190)

* [nibeheatpump] Added PRODINo ESP32 Ethernet v1 board support

Also added:
- telnet debug support (UDP debug removed)
- new configuration model
- OTA update support for PRODINo ESP32 Ethernet v1 board

Signed-off-by: Pauli Anttila <pauli.anttila@gmail.com>
pull/13230/head
pali 2022-08-07 12:35:16 +03:00 committed by GitHub
parent 11cf3ca86b
commit e53e1a5e99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 679 additions and 264 deletions

View File

@ -59,14 +59,15 @@ The Nibe Heat Pump binding will listen to a UDP port and parse register data fro
### Arduino ### Arduino
An Arduino-based solution has been tested with Arduino uno + RS485 and Ethernet shields. 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. [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.
A ProDiNo has an Ethernet and RS-485 port on the board. 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 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. Arduino code can be build via Arduino IDE.
For more details see [www.arduino.cc](https://www.arduino.cc/en/Main/Software). 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. 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) ### Raspberry Pi (or other Linux/Unix based boards)

View File

@ -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

View File

@ -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

View File

@ -20,337 +20,465 @@
* 27.6.2014 v1.02 Fixed compile error and added Ethernet initialization delay. * 27.6.2014 v1.02 Fixed compile error and added Ethernet initialization delay.
* 29.6.2015 v2.00 Bidirectional support. * 29.6.2015 v2.00 Bidirectional support.
* 18.2.2017 v3.00 Redesigned. * 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 "5.00"
#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.h>
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
// ######### INCLUDES ####################### // ######### INCLUDES #######################
#ifdef TRANSPORT_ETH_ENC28J60 #include "Config.h"
#include <UIPEthernet.h>
#elif defined STM32_F429ZI_BOARD #if defined(PRODINO_BOARD)
#include <LwIP.h>
#include <STM32Ethernet.h>
#include <EthernetUdp.h>
#elif defined PRODINO_BOARD
#include <SPI.h>
#include "KmpDinoEthernet.h" #include "KmpDinoEthernet.h"
#include "KMPCommon.h" #include "KMPCommon.h"
#include "Ethernet/utility/w5100.h" #include "Ethernet/utility/w5100.h"
#elif defined(PRODINO_BOARD_ESP32)
#include <esp_task_wdt.h>
#include "KMPProDinoESP32.h"
#include "KMPCommon.h"
#elif defined(TRANSPORT_ETH_ENC28J60)
#include <UIPEthernet.h>
#else #else
#include <SPI.h> #include <SPI.h>
#include <Ethernet.h> #include <Ethernet.h>
#include <EthernetUdp.h> #include <EthernetUdp.h>
#endif #endif
#ifdef STM32_F429ZI_BOARD #if !defined(PRODINO_BOARD_ESP32)
#include <IWatchdog.h>
#else
#include <avr/wdt.h> #include <avr/wdt.h>
#endif #endif
#include "NibeGw.h" #include "NibeGw.h"
#include "Debug.h"
// ######### VARIABLES ####################### // ######### VARIABLES #######################
// The media access control (ethernet hardware) address for the shield boolean ethInitialized = false;
byte mac[] = BOARD_MAC;
//The IP address for the shield IPAddress targetIp;
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);
EthernetUDP udp; EthernetUDP udp;
EthernetUDP udp4writeCmnds; EthernetUDP udp4writeCmnds;
NibeGw nibegw(&RS485_PORT, RS485_DIRECTION_PIN); #if defined(PRODINO_BOARD_ESP32)
HardwareSerial RS485_PORT(1);
// ######### DEBUG ####################### NibeGw nibegw(&RS485_PORT, RS485_DIRECTION_PIN, RS485_RX_PIN, RS485_TX_PIN);
#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); }}
#else #else
#define DEBUG_PRINT(level, message) NibeGw nibegw(&RS485_PORT, RS485_DIRECTION_PIN);
#define DEBUG_PRINTDATA(level, message, data)
#define DEBUG_PRINTARRAY(level, data, len)
#endif #endif
#ifdef ENABLE_DEBUG #if defined(PRODINO_BOARD_ESP32) && defined(ENABLE_DYNAMIC_CONFIG)
char verbose = VERBOSE_LEVEL; boolean dynamicConfigStarted = false;
char debugBuf[DEBUG_BUFFER_SIZE];
void debugPrint(char* data) class ConfigObserver: public ConfigurationObserver {
{ public:
#ifdef ENABLE_UDP_DEBUG void onConfigurationChanged(const ConfigurationPropertyChange value) {
if (ethernetInitialized) DEBUG_PRINT_VARS(0, "Configuration parameter '%s' changed from '%s' to '%s'\n",
{ String(value.key).c_str(),
udp.beginPacket(targetIp, TARGET_DEBUG_PORT); String(value.oldValue).c_str(),
udp.write(data); String(value.newValue).c_str());
udp.endPacket();
} }
};
#endif #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 ####################### // ######### 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 // Start watchdog
#ifdef STM32_F429ZI_BOARD #if defined(PRODINO_BOARD_ESP32)
IWatchdog.begin(2000000); // 2 sec esp_task_wdt_init(WDT_TIMEOUT, true);
#else esp_task_wdt_add(NULL);
wdt_enable (WDTO_2S); #else
#endif wdt_enable(WDTO_2S);
#endif
nibegw.setCallback(nibeCallbackMsgReceived, nibeCallbackTokenReceived); nibegw.setCallback(nibeCallbackMsgReceived, nibeCallbackTokenReceived);
nibegw.setAckModbus40Address(ACK_MODBUS40); nibegw.setSendAcknowledge(config.nibe.ack.sendAck);
nibegw.setAckSms40Address(ACK_SMS40); nibegw.setAckModbus40Address(config.nibe.ack.modbus40);
nibegw.setAckRmu40Address(ACK_RMU40); nibegw.setAckSms40Address(config.nibe.ack.sms40);
nibegw.setSendAcknowledge(SEND_ACK); nibegw.setAckRmu40Address(config.nibe.ack.rmu40);
#ifdef ENABLE_NIBE_DEBUG #ifdef ENABLE_NIBE_DEBUG
nibegw.setDebugCallback(nibeDebugCallback); nibegw.setDebugCallback(nibeDebugCallback);
nibegw.setVerboseLevel(VERBOSE_LEVEL); nibegw.setVerboseLevel(config.debug.level);
#endif #endif
#ifdef PRODINO_BOARD targetIp.fromString(config.nibe.targetIp);
DinoInit();
Serial.begin(115200, SERIAL_8N1);
#endif
DEBUG_PRINTDATA(0, "%s ", BOARD_NAME); DEBUG_PRINT_VARS(0, "%s version %s Started\n", config.boardName.c_str(), VERSION);
DEBUG_PRINTDATA(0, "version %s\n", VERSION);
DEBUG_PRINT(0, "Started\n");
} }
#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 ####################### // ######### MAIN LOOP #######################
void loop() void loop() {
{ #if defined(PRODINO_BOARD_ESP32) && defined(ENABLE_DYNAMIC_CONFIG)
#ifdef STM32_F429ZI_BOARD if (dynamicConfigStarted) {
IWatchdog.reload(); loopDynamicConfigMode();
#else } else {
loopNormalMode();
}
#else
loopNormalMode();
#endif
}
void loopNormalMode() {
#if defined(PRODINO_BOARD_ESP32)
esp_task_wdt_reset();
#else
wdt_reset(); wdt_reset();
#endif #endif
long now = millis() / 1000; long now = millis() / 1000;
if (!nibegw.connected()) if (!nibegw.connected()) {
{
nibegw.connect(); nibegw.connect();
} } else {
else do {
{
do
{
nibegw.loop(); nibegw.loop();
#ifdef TRANSPORT_ETH_ENC28J60 #ifdef TRANSPORT_ETH_ENC28J60
Ethernet.maintain(); Ethernet.maintain();
#endif #endif
} while (nibegw.messageStillOnProgress()); } while (nibegw.messageStillOnProgress());
} }
if (!ethernetInitialized && now >= ETH_INIT_DELAY) if (!ethInitialized && now >= config.eth.initDelay) {
{
initializeEthernet(); 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 ####################### // ######### FUNCTIONS #######################
void initializeEthernet() void initializeEthernet() {
{ DEBUG_PRINT_MSG(1, "Initializing Ethernet\n");
DEBUG_PRINT(1, "Initializing Ethernet\n");
Ethernet.begin(mac, ip, gw, mask);
#ifdef PRODINO_BOARD 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);
#if defined(PRODINO_BOARD_ESP32)
Ethernet.setRetransmissionCount(1);
Ethernet.setRetransmissionTimeout(50);
#elif defined(PRODINO_BOARD)
W5100.setRetransmissionCount(1); W5100.setRetransmissionCount(1);
#endif W5100.setRetransmissionTime(50);
#endif
ethernetInitialized = true; ethInitialized = true;
udp.begin(INCOMING_PORT_READCMDS); udp.begin(config.nibe.readCmdsPort);
udp4writeCmnds.begin(INCOMING_PORT_WRITECMDS); udp4writeCmnds.begin(config.nibe.writeCmdsPort);
#ifdef ENABLE_DEBUG printInfo();
DEBUG_PRINTDATA(0, "%s ", BOARD_NAME);
DEBUG_PRINTDATA(0, "version %s\n", VERSION); #if defined(PRODINO_BOARD_ESP32)
sprintf(debugBuf, "MAC=%02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); KMPProDinoESP32.offStatusLed();
DEBUG_PRINT(0, debugBuf); #endif
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
} }
void nibeCallbackMsgReceived(const byte* const data, int len) void nibeCallbackMsgReceived(const byte* const data, int len) {
{ #if defined(PRODINO_BOARD_ESP32)
if (ethernetInitialized) KMPProDinoESP32.setStatusLed(green);
{ #endif
if (ethInitialized) {
sendUdpPacket(data, len); 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; int len = 0;
if (ethernetInitialized) if (ethInitialized) {
{ if (token == READ_TOKEN) {
if (token == READ_TOKEN) DEBUG_PRINT_MSG(3, "Read token received from nibe\n");
{
DEBUG_PRINT(3, "Read token received from nibe\n");
int packetSize = udp.parsePacket(); int packetSize = udp.parsePacket();
if (packetSize) { if (packetSize) {
#if defined(PRODINO_BOARD_ESP32)
KMPProDinoESP32.setStatusLed(white);
#endif
targetIp = udp.remoteIP();
len = udp.read(data, packetSize); len = udp.read(data, packetSize);
DEBUG_PRINTDATA(2, "Send read command to nibe, len=%d, ", len); DEBUG_PRINT_VARS(2, "Send read command to nibe, len=%d, ", len);
DEBUG_PRINT(1, " data in: "); DEBUG_PRINT_MSG(1, " data in: ");
DEBUG_PRINTARRAY(1, data, len) DEBUG_PRINT_ARRAY(1, data, len)
DEBUG_PRINT(1, "\n"); DEBUG_PRINT_MSG(1, "\n");
#ifdef TRANSPORT_ETH_ENC28J60
udp.flush(); #if defined(TRANSPORT_ETH_ENC28J60)
udp.stop(); udp4readCmnds.flush();
udp.begin(INCOMING_PORT_READCMDS); udp4readCmnds.stop();
#endif udp4readCmnds.begin(config.nibe.readCmdsPort);
#endif
} }
} } else if (token == WRITE_TOKEN) {
else if (token == WRITE_TOKEN) DEBUG_PRINT_MSG(3, "Write token received from nibe\n");
{
DEBUG_PRINT(3, "Write token received from nibe\n");
int packetSize = udp4writeCmnds.parsePacket(); int packetSize = udp4writeCmnds.parsePacket();
if (packetSize) { if (packetSize) {
#if defined(PRODINO_BOARD_ESP32)
KMPProDinoESP32.setStatusLed(orange);
#endif
targetIp = udp.remoteIP();
len = udp4writeCmnds.read(data, packetSize); len = udp4writeCmnds.read(data, packetSize);
DEBUG_PRINTDATA(2, "Send write command to nibe, len=%d, ", len); DEBUG_PRINT_VARS(2, "Send write command to nibe, len=%d, ", len);
DEBUG_PRINT(1, " data in: "); DEBUG_PRINT_MSG(1, " data in: ");
DEBUG_PRINTARRAY(1, data, len) DEBUG_PRINT_ARRAY(1, data, len)
DEBUG_PRINT(1, "\n"); DEBUG_PRINT_MSG(1, "\n");
#ifdef TRANSPORT_ETH_ENC28J60
#if defined(TRANSPORT_ETH_ENC28J60)
udp4writeCmnds.flush(); udp4writeCmnds.flush();
udp4writeCmnds.stop(); udp4writeCmnds.stop();
udp4writeCmnds.begin(INCOMING_PORT_WRITECMDS); udp4writeCmnds.begin(config.nibe.writeCmdsPort);
#endif #endif
} }
} }
#if defined(PRODINO_BOARD_ESP32)
KMPProDinoESP32.offStatusLed();
#endif
} }
return len; return len;
} }
void nibeDebugCallback(byte verbose, char* data) void nibeDebugCallback(byte level, char* data) {
{ DEBUG_PRINT_MSG(level, data);
DEBUG_PRINT(verbose, data);
} }
void sendUdpPacket(const byte * const data, int len) void sendUdpPacket(const byte* const data, int len) {
{ #ifdef ENABLE_DEBUG
#ifdef ENABLE_DEBUG DEBUG_PRINT_VARS(2, "Sending UDP packet to %s:%d, len=%d", IPtoString(targetIp).c_str(), config.nibe.targetPort, len);
sprintf(debugBuf, "Sending UDP packet to %d.%d.%d.%d:", TARGET_IP); DEBUG_PRINT_MSG(1, " data out: ");
DEBUG_PRINT(2, debugBuf); DEBUG_PRINT_ARRAY(1, data, len)
DEBUG_PRINTDATA(2, "%d", TARGET_PORT); DEBUG_PRINT_MSG(1, "\n");
DEBUG_PRINTDATA(2, ", len=%d, ", len); #endif
DEBUG_PRINT(1, "data out: ");
DEBUG_PRINTARRAY(1, data, len) #if defined(PRODINO_BOARD_ESP32)
DEBUG_PRINT(1, "\n"); EthernetLinkStatus linkStatus = Ethernet.linkStatus();
#endif 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.beginPacket(targetIp, TARGET_PORT);
udp.write(data, len); udp.write(data, len);
int retval = udp.endPacket(); 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

View File

@ -19,12 +19,18 @@
#include "NibeGw.h" #include "NibeGw.h"
#include "Arduino.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) NibeGw::NibeGw(HardwareSerial* serial, int RS485DirectionPin)
#else #else
NibeGw::NibeGw(Serial_* serial, int RS485DirectionPin) NibeGw::NibeGw(Serial_* serial, int RS485DirectionPin)
#endif #endif
{ {
#if defined(HARDWARE_SERIAL_WITH_PINS)
this->RS485RxPin = RS485RxPin;
this->RS485TxPin = RS485TxPin;
#endif
verbose = 0; verbose = 0;
ackModbus40 = true; ackModbus40 = true;
ackSms40 = false; ackSms40 = false;
@ -44,7 +50,13 @@ void NibeGw::connect()
if (!connectionState) if (!connectionState)
{ {
state = STATE_WAIT_START; state = STATE_WAIT_START;
#if defined(HARDWARE_SERIAL_WITH_PINS)
RS485->begin(9600, SERIAL_8N1, RS485RxPin, RS485TxPin);
#else
RS485->begin(9600, SERIAL_8N1); RS485->begin(9600, SERIAL_8N1);
#endif
connectionState = true; connectionState = true;
} }
} }
@ -377,4 +389,3 @@ boolean NibeGw::shouldAckNakSend(byte address)
return false; return false;
} }

View File

@ -38,7 +38,9 @@
#include <Arduino.h> #include <Arduino.h>
#define HARDWARE_SERIAL #define HARDWARE_SERIAL_WITH_PINS
//#define HARDWARE_SERIAL
//#define ENABLE_NIBE_DEBUG //#define ENABLE_NIBE_DEBUG
// state machine states // state machine states
@ -78,7 +80,11 @@ class NibeGw
byte directionPin; byte directionPin;
byte buffer[MAX_DATA_LEN]; byte buffer[MAX_DATA_LEN];
byte index; byte index;
#ifdef HARDWARE_SERIAL #if defined(HARDWARE_SERIAL_WITH_PINS)
HardwareSerial* RS485;
int RS485RxPin;
int RS485TxPin;
#elif defined(HARDWARE_SERIAL)
HardwareSerial* RS485; HardwareSerial* RS485;
#else #else
Serial_* RS485; Serial_* RS485;
@ -103,7 +109,9 @@ class NibeGw
#endif #endif
public: public:
#ifdef HARDWARE_SERIAL #if defined(HARDWARE_SERIAL_WITH_PINS)
NibeGw(HardwareSerial* serial, int RS485DirectionPin, int RS485RxPin, int RS485TxPin);
#elif defined(HARDWARE_SERIAL)
NibeGw(HardwareSerial* serial, int RS485DirectionPin); NibeGw(HardwareSerial* serial, int RS485DirectionPin);
#else #else
NibeGw(Serial_* serial, int RS485DirectionPin); NibeGw(Serial_* serial, int RS485DirectionPin);