Merge pull request #16075 from jeroenst/ModbusTCP

Adding modbus bridge TCP
This commit is contained in:
Theo Arends 2022-07-25 16:07:09 +02:00 committed by GitHub
commit a0d6670a5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 347 additions and 66 deletions

View File

@ -754,6 +754,7 @@
//#define USE_DYP // Add support for DYP ME-007 ultrasonic distance sensor, serial port version (+0k5 code) //#define USE_DYP // Add support for DYP ME-007 ultrasonic distance sensor, serial port version (+0k5 code)
#define USE_SERIAL_BRIDGE // Add support for software Serial Bridge (+0k8 code) #define USE_SERIAL_BRIDGE // Add support for software Serial Bridge (+0k8 code)
//#define USE_MODBUS_BRIDGE // Add support for software Modbus Bridge (+3k code) //#define USE_MODBUS_BRIDGE // Add support for software Modbus Bridge (+3k code)
//#define USE_MODBUS_BRIDGE_TCP // Add support for software Modbus TCP Bridge (Also enable Modbus Bridge!) (? code)
//#define USE_TCP_BRIDGE // Add support for Serial to TCP bridge (+1.3k code) //#define USE_TCP_BRIDGE // Add support for Serial to TCP bridge (+1.3k code)
//#define USE_MP3_PLAYER // Use of the DFPlayer Mini MP3 Player RB-DFR-562 commands: play, pause, stop, track, volume and reset //#define USE_MP3_PLAYER // Use of the DFPlayer Mini MP3 Player RB-DFR-562 commands: play, pause, stop, track, volume and reset
#define MP3_VOLUME 30 // Set the startup volume on init, the range can be 0..100(max) #define MP3_VOLUME 30 // Set the startup volume on init, the range can be 0..100(max)

View File

@ -17,19 +17,24 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifdef USE_MODBUS_BRIDGE #if defined(USE_MODBUS_BRIDGE)
/*********************************************************************************************\ /*********************************************************************************************\
* Modbus Bridge using Modbus library (TasmotaModbus) * Modbus Bridge using Modbus library (TasmotaModbus)
* *
* Can be used trough web/mqtt commands and also via direct TCP connection (when defined)
*
* When USE_MODBUS_BRIDGE_TCP is also defined, this bridge can also be used as an ModbusTCP
* bridge.
*
* Example Command: * Example Command:
* ModbusSend {"deviceaddress": 1, "functioncode": 3, "startaddress": 1, "type":"uint8", "count":4} * ModbusSend {"deviceaddress": 1, "functioncode": 3, "startaddress": 1, "type":"uint16", "count":2}
\*********************************************************************************************/ \*********************************************************************************************/
#define XDRV_63 63 #define XDRV_63 63
#define MBR_MAX_VALUE_LENGTH 30 #define MBR_MAX_VALUE_LENGTH 30
#define MBR_SPEED TM_MODBUS_BAUDRATE #define MBR_SPEED TM_MODBUS_BAUDRATE
#define MBR_MAX_REGISTERS 64 #define MBR_MAX_REGISTERS 64
#define D_CMND_MODBUS_SEND "Send" #define D_CMND_MODBUS_SEND "Send"
#define D_CMND_MODBUS_SETBAUDRATE "Baudrate" #define D_CMND_MODBUS_SETBAUDRATE "Baudrate"
@ -45,11 +50,41 @@
#define D_JSON_MODBUS_VALUES "Values" #define D_JSON_MODBUS_VALUES "Values"
#define D_JSON_MODBUS_LENGTH "Length" #define D_JSON_MODBUS_LENGTH "Length"
#ifndef USE_MODBUS_BRIDGE_TCP
const char kModbusBridgeCommands[] PROGMEM = "Modbus|" // Prefix const char kModbusBridgeCommands[] PROGMEM = "Modbus|" // Prefix
D_CMND_MODBUS_SEND "|" D_CMND_MODBUS_SETBAUDRATE "|" D_CMND_MODBUS_SETSERIALCONFIG; D_CMND_MODBUS_SEND "|" D_CMND_MODBUS_SETBAUDRATE "|" D_CMND_MODBUS_SETSERIALCONFIG;
void (*const ModbusBridgeCommand[])(void) PROGMEM = { void (*const ModbusBridgeCommand[])(void) PROGMEM = {
&CmndModbusBridgeSend, &CmndModbusBridgeSetBaudrate, &CmndModbusBridgeSetConfig}; &CmndModbusBridgeSend, &CmndModbusBridgeSetBaudrate, &CmndModbusBridgeSetConfig};
#endif
#ifdef USE_MODBUS_BRIDGE_TCP
#define MODBUS_BRIDGE_TCP_CONNECTIONS 1 // number of maximum parallel connections, only 1 supported with modbus
#define MODBUS_BRIDGE_TCP_BUF_SIZE 255 // size of the buffer, above 132 required for efficient XMODEM
#define D_CMND_MODBUS_TCP_START "TCPStart"
#define D_CMND_MODBUS_TCP_CONNECT "TCPConnect"
const char kModbusBridgeCommands[] PROGMEM = "Modbus|" // Prefix
D_CMND_MODBUS_TCP_START "|" D_CMND_MODBUS_TCP_CONNECT "|" D_CMND_MODBUS_SEND "|" D_CMND_MODBUS_SETBAUDRATE "|" D_CMND_MODBUS_SETSERIALCONFIG;
void (*const ModbusBridgeCommand[])(void) PROGMEM = {
&CmndModbusTCPStart, &CmndModbusTCPConnect,
&CmndModbusBridgeSend, &CmndModbusBridgeSetBaudrate, &CmndModbusBridgeSetConfig};
struct ModbusBridgeTCP
{
WiFiServer *server_tcp = nullptr;
WiFiClient client_tcp[MODBUS_BRIDGE_TCP_CONNECTIONS];
uint8_t client_next = 0;
uint8_t *tcp_buf = nullptr; // data transfer buffer
IPAddress ip_filter;
uint16_t tcp_transaction_id = 0;
};
ModbusBridgeTCP modbusBridgeTCP;
#endif
#include <TasmotaModbus.h> #include <TasmotaModbus.h>
TasmotaModbus *tasmotaModbus = nullptr; TasmotaModbus *tasmotaModbus = nullptr;
@ -80,15 +115,13 @@ enum class ModbusBridgeFunctionCode
enum class ModbusBridgeType enum class ModbusBridgeType
{ {
mb_undefined, mb_undefined,
mb_uint8,
mb_uint16, mb_uint16,
mb_uint32, mb_uint32,
mb_int8,
mb_int16, mb_int16,
mb_int32, mb_int32,
mb_float, mb_float,
mb_raw, mb_raw,
mb_bit8 mb_bit
}; };
enum class ModbusBridgeEndian enum class ModbusBridgeEndian
@ -116,10 +149,21 @@ struct ModbusBridge
ModbusBridge modbusBridge; ModbusBridge modbusBridge;
/********************************************************************************************/ /********************************************************************************************/
//
bool SetModbusBridgeBegin(void) // Applies serial configuration to modbus serial port
//
bool ModbusBridgeBegin(void)
{ {
return tasmotaModbus->begin(Settings->baudrate * 300, ConvertSerialConfig(Settings->sserial_config)); // Reinitialize modbus port with new baud rate int result = tasmotaModbus->Begin(Settings->baudrate * 300, ConvertSerialConfig(Settings->sserial_config)); // Reinitialize modbus port with new baud rate
if (result)
{
if (2 == result)
{
ClaimSerial();
}
AddLog(LOG_LEVEL_DEBUG, PSTR("MBS: MBR %s ser init at %d baud"), (2 == result ? "HW" : "SW"), Settings->baudrate * 300);
}
return result;
} }
void SetModbusBridgeConfig(uint32_t serial_config) void SetModbusBridgeConfig(uint32_t serial_config)
@ -131,33 +175,83 @@ void SetModbusBridgeConfig(uint32_t serial_config)
if (serial_config != Settings->sserial_config) if (serial_config != Settings->sserial_config)
{ {
Settings->sserial_config = serial_config; Settings->sserial_config = serial_config;
SetModbusBridgeBegin(); ModbusBridgeBegin();
}
}
void SetModbusBridgeBaudrate(uint32_t baudrate)
{
if (baudrate >= 300)
{
Settings->baudrate = baudrate / 300;
ModbusBridgeBegin();
} }
} }
/********************************************************************************************/ /********************************************************************************************/
//
// Handles data received from tasmota modbus wrapper and send this to (TCP or) MQTT client
//
void ModbusBridgeHandle(void) void ModbusBridgeHandle(void)
{ {
bool data_ready = tasmotaModbus->ReceiveReady(); bool data_ready = tasmotaModbus->ReceiveReady();
if (data_ready) if (data_ready)
{ {
uint8_t *buffer; uint8_t *buffer;
buffer = (uint8_t *)malloc(5 + modbusBridge.registerCount); // Addres(1), Function(1), Length(1), Data(1..n), CRC(2) buffer = (uint8_t *)malloc(5 + (modbusBridge.registerCount * 2)); // Addres(1), Function(1), Length(1), Data(1..n), CRC(2)
uint32_t error = tasmotaModbus->ReceiveBuffer(buffer, modbusBridge.registerCount); uint32_t error = tasmotaModbus->ReceiveBuffer(buffer, modbusBridge.registerCount);
ModbusBridgeError errorcode = ModbusBridgeError::noerror;
if (error) if (error)
{ {
AddLog(LOG_LEVEL_DEBUG, PSTR("MBS: MBR Driver error %d"), error); AddLog(LOG_LEVEL_DEBUG, PSTR("MBS: MBR Driver error %d"), error);
free(buffer);
return;
} }
else if (modbusBridge.deviceAddress == 0)
#ifdef USE_MODBUS_BRIDGE_TCP
for (uint32_t i = 0; i < nitems(modbusBridgeTCP.client_tcp); i++)
{
WiFiClient &client = modbusBridgeTCP.client_tcp[i];
if (client)
{
uint8_t MBAP_Header[7];
MBAP_Header[0] = modbusBridgeTCP.tcp_transaction_id >> 8;
MBAP_Header[1] = modbusBridgeTCP.tcp_transaction_id;
MBAP_Header[2] = 0;
MBAP_Header[3] = 0;
MBAP_Header[4] = ((modbusBridge.registerCount * 2) + 3) >> 8;
MBAP_Header[5] = (modbusBridge.registerCount * 2) + 3;
MBAP_Header[6] = buffer[0]; // Send slave address
client.write(MBAP_Header, 7);
client.write(buffer + 1, 1); // Send Functioncode
uint8_t bytecount[1];
bytecount[0] = modbusBridge.registerCount * 2;
client.write(bytecount, 1); // Send length of rtu data
client.write(buffer + 3, (modbusBridge.registerCount * 2)); // Don't send CRC
client.flush();
AddLog(LOG_LEVEL_DEBUG, PSTR("MBS: MBRTCP from Modbus deviceAddress %d, writing %d bytes to client"), buffer[0], (modbusBridge.registerCount * 2) + 9);
}
}
#endif
ModbusBridgeError errorcode = ModbusBridgeError::noerror;
if (modbusBridge.deviceAddress == 0)
{
#ifdef USE_MODBUS_BRIDGE_TCP
// If tcp client connected don't log error and exit this function (do not process)
if (nitems(modbusBridgeTCP.client_tcp))
{
free(buffer);
return;
}
#endif
errorcode = ModbusBridgeError::nodataexpected; errorcode = ModbusBridgeError::nodataexpected;
}
else if (modbusBridge.deviceAddress != (uint8_t)buffer[0]) else if (modbusBridge.deviceAddress != (uint8_t)buffer[0])
errorcode = ModbusBridgeError::wrongdeviceaddress; errorcode = ModbusBridgeError::wrongdeviceaddress;
else if ((uint8_t)modbusBridge.functionCode != (uint8_t)buffer[1]) else if ((uint8_t)modbusBridge.functionCode != (uint8_t)buffer[1])
errorcode = ModbusBridgeError::wrongfunctioncode; errorcode = ModbusBridgeError::wrongfunctioncode;
else if ((uint8_t)modbusBridge.registerCount != (uint8_t)buffer[2]) else if ((uint8_t)modbusBridge.registerCount * 2 != (uint8_t)buffer[2])
errorcode = ModbusBridgeError::wrongregistercount; errorcode = ModbusBridgeError::wrongregistercount;
else else
{ {
@ -174,7 +268,7 @@ void ModbusBridgeHandle(void)
ResponseJsonEnd(); ResponseJsonEnd();
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_MODBUS_RECEIVED)); MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_MODBUS_RECEIVED));
} }
else if ((buffer[1] > 0) && (buffer[0] < 5)) // Read Registers, writing is not supported at this moment else if ((buffer[1] > 0) && (buffer[1] < 5)) // Read Registers, writing is not supported at this moment
{ {
Response_P(PSTR("{\"" D_JSON_MODBUS_RECEIVED "\":{")); Response_P(PSTR("{\"" D_JSON_MODBUS_RECEIVED "\":{"));
ResponseAppend_P(PSTR("\"" D_JSON_MODBUS_DEVICE_ADDRESS "\":%d,"), buffer[0]); ResponseAppend_P(PSTR("\"" D_JSON_MODBUS_DEVICE_ADDRESS "\":%d,"), buffer[0]);
@ -222,15 +316,7 @@ void ModbusBridgeHandle(void)
else else
snprintf(svalue, MBR_MAX_VALUE_LENGTH, "%u", value); snprintf(svalue, MBR_MAX_VALUE_LENGTH, "%u", value);
} }
else if ((modbusBridge.type == ModbusBridgeType::mb_int8) || else if (modbusBridge.type == ModbusBridgeType::mb_bit)
(modbusBridge.type == ModbusBridgeType::mb_uint8))
{
if (modbusBridge.type == ModbusBridgeType::mb_int8)
snprintf(svalue, MBR_MAX_VALUE_LENGTH, "%d", (int8_t)(buffer[3 + count]));
else
snprintf(svalue, MBR_MAX_VALUE_LENGTH, "%u", (uint8_t)(buffer[3 + count]));
}
else if (modbusBridge.type == ModbusBridgeType::mb_bit8)
{ {
uint8_t value = (uint8_t)(buffer[3 + count]); uint8_t value = (uint8_t)(buffer[3 + count]);
snprintf(svalue, MBR_MAX_VALUE_LENGTH, "%d%d%d%d%d%d%d%d", ((value >> 7) & 1), ((value >> 6) & 1), ((value >> 5) & 1), ((value >> 4) & 1), ((value >> 3) & 1), ((value >> 2) & 1), ((value >> 1) & 1), (value & 1)); snprintf(svalue, MBR_MAX_VALUE_LENGTH, "%d%d%d%d%d%d%d%d", ((value >> 7) & 1), ((value >> 6) & 1), ((value >> 5) & 1), ((value >> 4) & 1), ((value >> 3) & 1), ((value >> 2) & 1), ((value >> 1) & 1), (value & 1));
@ -247,6 +333,8 @@ void ModbusBridgeHandle(void)
if (errorcode == ModbusBridgeError::noerror) if (errorcode == ModbusBridgeError::noerror)
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_MODBUS_RECEIVED)); MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_MODBUS_RECEIVED));
} }
else
errorcode = ModbusBridgeError::wrongfunctioncode;
} }
if (errorcode != ModbusBridgeError::noerror) if (errorcode != ModbusBridgeError::noerror)
{ {
@ -258,25 +346,120 @@ void ModbusBridgeHandle(void)
} }
/********************************************************************************************/ /********************************************************************************************/
//
// Inits the tasmota modbus driver, sets serialport and if TCP enabled allocates a TCP buffer
//
void ModbusBridgeInit(void) void ModbusBridgeInit(void)
{ {
if (PinUsed(GPIO_MBR_RX) && PinUsed(GPIO_MBR_TX)) if (PinUsed(GPIO_MBR_RX) && PinUsed(GPIO_MBR_TX))
{ {
tasmotaModbus = new TasmotaModbus(Pin(GPIO_MBR_RX), Pin(GPIO_MBR_TX)); tasmotaModbus = new TasmotaModbus(Pin(GPIO_MBR_RX), Pin(GPIO_MBR_TX));
uint8_t result = tasmotaModbus->Begin(MBR_SPEED); SetModbusBridgeConfig(TS_SERIAL_8E1);
if (result) SetModbusBridgeBaudrate(MBR_SPEED);
#ifdef USE_MODBUS_BRIDGE_TCP
// If TCP bridge is enabled allocate a TCP receive buffer
modbusBridgeTCP.tcp_buf = (uint8_t *)malloc(MODBUS_BRIDGE_TCP_BUF_SIZE);
if (!modbusBridgeTCP.tcp_buf)
{ {
if (2 == result) AddLog(LOG_LEVEL_ERROR, PSTR("MBS: MBRTCP could not allocate buffer"));
{ return;
Serial.begin(MBR_SPEED, SERIAL_8E1);
ClaimSerial();
}
AddLog(LOG_LEVEL_DEBUG, PSTR("MBS: MBR %s ser init at %d baud"), (2 == result ? "HW" : "SW"), MBR_SPEED);
} }
#endif
} }
} }
#ifdef USE_MODBUS_BRIDGE_TCP
/********************************************************************************************/
//
// Handles data for TCP server and TCP client. Sends requests to Modbus Devices
//
void ModbusTCPHandle(void)
{
uint8_t c;
bool busy; // did we transfer some data?
int32_t buf_len;
if (!tasmotaModbus)
return;
// check for a new client connection
if ((modbusBridgeTCP.server_tcp) && (modbusBridgeTCP.server_tcp->hasClient()))
{
WiFiClient new_client = modbusBridgeTCP.server_tcp->available();
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_TCP "MBS: MBRTCP Got connection from %s"), new_client.remoteIP().toString().c_str());
// Check for IP filtering if it's enabled.
if (modbusBridgeTCP.ip_filter)
{
if (modbusBridgeTCP.ip_filter != new_client.remoteIP())
{
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_TCP "MBS: MBRTCP Rejected due to filtering"));
new_client.stop();
}
else
{
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_TCP "MBS: MBRTCP Allowed through filter"));
}
}
// find an empty slot
uint32_t i;
for (i = 0; i < nitems(modbusBridgeTCP.client_tcp); i++)
{
WiFiClient &client = modbusBridgeTCP.client_tcp[i];
if (!client)
{
client = new_client;
break;
}
}
if (i >= nitems(modbusBridgeTCP.client_tcp))
{
i = modbusBridgeTCP.client_next++ % nitems(modbusBridgeTCP.client_tcp);
WiFiClient &client = modbusBridgeTCP.client_tcp[i];
client.stop();
client = new_client;
}
}
do
{
busy = false; // exit loop if no data was transferred
// handle data received from TCP
for (uint32_t i = 0; i < nitems(modbusBridgeTCP.client_tcp); i++)
{
WiFiClient &client = modbusBridgeTCP.client_tcp[i];
buf_len = 0;
while (client && (buf_len < MODBUS_BRIDGE_TCP_BUF_SIZE) && (client.available()))
{
c = client.read();
if (c >= 0)
{
modbusBridgeTCP.tcp_buf[buf_len++] = c;
busy = true;
}
}
if (buf_len == 12)
{
uint8_t mbdeviceaddress = (uint8_t)modbusBridgeTCP.tcp_buf[6];
uint8_t mbfunctioncode = (uint8_t)modbusBridgeTCP.tcp_buf[7];
uint16_t mbstartaddress = (uint16_t)((((uint16_t)modbusBridgeTCP.tcp_buf[8]) << 8) | ((uint16_t)modbusBridgeTCP.tcp_buf[9]));
modbusBridge.registerCount = (uint16_t)((((uint16_t)modbusBridgeTCP.tcp_buf[10]) << 8) | ((uint16_t)modbusBridgeTCP.tcp_buf[11]));
modbusBridgeTCP.tcp_transaction_id = (uint16_t)((((uint16_t)modbusBridgeTCP.tcp_buf[0]) << 8) | ((uint16_t)modbusBridgeTCP.tcp_buf[1]));
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MBS: MBRTCP to Modbus Transactionid:%d, deviceAddress:%d, functionCode:%d, startAddress:%d, Count:%d"),
modbusBridgeTCP.tcp_transaction_id, mbdeviceaddress, mbfunctioncode, mbstartaddress, modbusBridge.registerCount);
tasmotaModbus->Send(mbdeviceaddress, mbfunctioncode, mbstartaddress, modbusBridge.registerCount);
}
}
yield(); // avoid WDT if heavy traffic
} while (busy);
}
#endif
/*********************************************************************************************\ /*********************************************************************************************\
* Commands * Commands
\*********************************************************************************************/ \*********************************************************************************************/
@ -309,55 +492,46 @@ void CmndModbusBridgeSend(void)
} }
modbusBridge.type = ModbusBridgeType::mb_undefined; modbusBridge.type = ModbusBridgeType::mb_undefined;
if (strcmp(stype, "int8") == 0) if (strcmp(stype, "int16") == 0)
{
modbusBridge.type = ModbusBridgeType::mb_int8;
modbusBridge.registerCount = 1 * modbusBridge.count;
}
else if (strcmp(stype, "int16") == 0)
{ {
modbusBridge.type = ModbusBridgeType::mb_int16; modbusBridge.type = ModbusBridgeType::mb_int16;
modbusBridge.registerCount = 2 * modbusBridge.count; modbusBridge.registerCount = modbusBridge.count;
} }
else if (strcmp(stype, "int32") == 0) else if (strcmp(stype, "int32") == 0)
{ {
modbusBridge.type = ModbusBridgeType::mb_int32; modbusBridge.type = ModbusBridgeType::mb_int32;
modbusBridge.registerCount = 4 * modbusBridge.count; modbusBridge.registerCount = 2 * modbusBridge.count;
}
else if (strcmp(stype, "uint8") == 0)
{
modbusBridge.type = ModbusBridgeType::mb_uint8;
modbusBridge.registerCount = 1 * modbusBridge.count;
} }
else if (strcmp(stype, "uint16") == 0) else if (strcmp(stype, "uint16") == 0)
{ {
modbusBridge.type = ModbusBridgeType::mb_uint16; modbusBridge.type = ModbusBridgeType::mb_uint16;
modbusBridge.registerCount = 2 * modbusBridge.count; modbusBridge.registerCount = modbusBridge.count;
} }
else if (strcmp(stype, "uint32") == 0) else if (strcmp(stype, "uint32") == 0)
{ {
modbusBridge.type = ModbusBridgeType::mb_uint32; modbusBridge.type = ModbusBridgeType::mb_uint32;
modbusBridge.registerCount = 4 * modbusBridge.count; modbusBridge.registerCount = 2 * modbusBridge.count;
} }
else if (strcmp(stype, "float") == 0) else if (strcmp(stype, "float") == 0)
{ {
modbusBridge.type = ModbusBridgeType::mb_float; modbusBridge.type = ModbusBridgeType::mb_float;
modbusBridge.registerCount = 4 * modbusBridge.count; modbusBridge.registerCount = 2 * modbusBridge.count;
} }
else if (strcmp(stype, "raw") == 0) else if (strcmp(stype, "raw") == 0)
{ {
modbusBridge.type = ModbusBridgeType::mb_raw; modbusBridge.type = ModbusBridgeType::mb_raw;
modbusBridge.registerCount = modbusBridge.count; modbusBridge.registerCount = modbusBridge.count;
} }
else if (strcmp(stype, "bit8") == 0) else if (strcmp(stype, "bit") == 0)
{ {
modbusBridge.type = ModbusBridgeType::mb_bit8; modbusBridge.type = ModbusBridgeType::mb_bit;
modbusBridge.registerCount = modbusBridge.count; modbusBridge.registerCount = modbusBridge.count;
} }
else else
errorcode = ModbusBridgeError::wrongtype; errorcode = ModbusBridgeError::wrongtype;
if (modbusBridge.registerCount > MBR_MAX_REGISTERS) errorcode = ModbusBridgeError::wrongcount; if (modbusBridge.registerCount > MBR_MAX_REGISTERS)
errorcode = ModbusBridgeError::wrongcount;
if (errorcode != ModbusBridgeError::noerror) if (errorcode != ModbusBridgeError::noerror)
{ {
@ -371,13 +545,8 @@ void CmndModbusBridgeSend(void)
void CmndModbusBridgeSetBaudrate(void) void CmndModbusBridgeSetBaudrate(void)
{ {
if (XdrvMailbox.payload >= 300) SetModbusBridgeBaudrate(XdrvMailbox.payload);
{ ResponseCmndNumber(Settings->baudrate * 300);
XdrvMailbox.payload /= 300; // Make it a valid baudrate
Settings->sbaudrate = XdrvMailbox.payload;
SetModbusBridgeBegin();
}
ResponseCmndNumber(Settings->sbaudrate * 300);
} }
void CmndModbusBridgeSetConfig(void) void CmndModbusBridgeSetConfig(void)
@ -407,6 +576,114 @@ void CmndModbusBridgeSetConfig(void)
ResponseCmndChar(GetSerialConfig(Settings->sserial_config).c_str()); ResponseCmndChar(GetSerialConfig(Settings->sserial_config).c_str());
} }
#ifdef USE_MODBUS_BRIDGE_TCP
//
// Command `TCPStart`
// Params: port,<IPv4 allow>
//
void CmndModbusTCPStart(void)
{
if (!tasmotaModbus)
{
return;
}
int32_t tcp_port = XdrvMailbox.payload;
if (ArgC() == 2)
{
char sub_string[XdrvMailbox.data_len];
modbusBridgeTCP.ip_filter.fromString(ArgV(sub_string, 2));
}
else
{
// Disable whitelist if previously set
modbusBridgeTCP.ip_filter = (uint32_t)0;
}
if (modbusBridgeTCP.server_tcp)
{
AddLog(LOG_LEVEL_INFO, PSTR("MBS: MBRTCP Stopping server"));
modbusBridgeTCP.server_tcp->stop();
delete modbusBridgeTCP.server_tcp;
modbusBridgeTCP.server_tcp = nullptr;
for (uint32_t i = 0; i < nitems(modbusBridgeTCP.client_tcp); i++)
{
WiFiClient &client = modbusBridgeTCP.client_tcp[i];
client.stop();
}
}
if (tcp_port > 0)
{
AddLog(LOG_LEVEL_INFO, PSTR("MBS: MBRTCP Starting server on port %d"), tcp_port);
if (modbusBridgeTCP.ip_filter)
{
AddLog(LOG_LEVEL_INFO, PSTR("MBS: MBRTCP Filtering %s"), modbusBridgeTCP.ip_filter.toString().c_str());
}
modbusBridgeTCP.server_tcp = new WiFiServer(tcp_port);
modbusBridgeTCP.server_tcp->begin(); // start TCP server
modbusBridgeTCP.server_tcp->setNoDelay(true);
}
ResponseCmndDone();
}
//
// Command `Connect`
// Params: port,<IPv4>
//
void CmndModbusTCPConnect(void)
{
int32_t tcp_port = XdrvMailbox.payload;
if (!tasmotaModbus)
{
return;
}
if (ArgC() == 2)
{
char sub_string[XdrvMailbox.data_len];
WiFiClient new_client;
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_TCP "MBS: MBRTCP Connecting to %s on port %d"), ArgV(sub_string, 2), tcp_port);
if (new_client.connect(ArgV(sub_string, 2), tcp_port))
{
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_TCP "MBS: MBRTCP connected!"));
}
else
{
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_TCP "MBS: MBRTCP error connecting!"));
}
// find an empty slot
uint32_t i;
for (i = 0; i < nitems(modbusBridgeTCP.client_tcp); i++)
{
WiFiClient &client = modbusBridgeTCP.client_tcp[i];
if (!client)
{
client = new_client;
break;
}
}
if (i >= nitems(modbusBridgeTCP.client_tcp))
{
i = modbusBridgeTCP.client_next++ % nitems(modbusBridgeTCP.client_tcp);
WiFiClient &client = modbusBridgeTCP.client_tcp[i];
client.stop();
client = new_client;
}
}
else
{
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_TCP "MBS: MBR Usage: port,ip_address"));
}
ResponseCmndDone();
}
#endif
/*********************************************************************************************\ /*********************************************************************************************\
* Interface * Interface
\*********************************************************************************************/ \*********************************************************************************************/
@ -423,12 +700,15 @@ bool Xdrv63(uint8_t function)
{ {
switch (function) switch (function)
{ {
case FUNC_EVERY_250_MSECOND:
ModbusBridgeHandle();
break;
case FUNC_COMMAND: case FUNC_COMMAND:
result = DecodeCommand(kModbusBridgeCommands, ModbusBridgeCommand); result = DecodeCommand(kModbusBridgeCommands, ModbusBridgeCommand);
break; break;
case FUNC_LOOP:
ModbusBridgeHandle();
#ifdef USE_MODBUS_BRIDGE_TCP
ModbusTCPHandle();
#endif
break;
} }
} }
return result; return result;