mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-23 10:46:31 +00:00
Merge pull request #16075 from jeroenst/ModbusTCP
Adding modbus bridge TCP
This commit is contained in:
commit
a0d6670a5a
@ -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)
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user