From bcaac8208fc3e5fc791f1e698d2c9fbe46707633 Mon Sep 17 00:00:00 2001 From: JeroenSt Date: Mon, 25 Jul 2022 09:56:35 +0200 Subject: [PATCH] Adding modbus bridge TCP Removed (u)int8 because modbus registers are always 16 bits and changed bit8 to bit Solved memory leak, changed logging Improved initialisation and configuring serial port Solved bug that addresses above 4 didn't reply Removed logging Added mandatory comment to USE_MODBUS_TCP_BRIDGE Using TasmotaModbus->Begin instead of begin Added bytecount to modbus tcp reply message Added comments Put modustcp variables in ModbusBridgeTCP struct. --- tasmota/my_user_config.h | 1 + .../xdrv_63_modbus_bridge.ino | 412 +++++++++++++++--- 2 files changed, 347 insertions(+), 66 deletions(-) diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 9bf93bf55..eeaa50aba 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -754,6 +754,7 @@ //#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_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_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) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino b/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino index faf102934..af4d39896 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino @@ -17,19 +17,24 @@ along with this program. If not, see . */ -#ifdef USE_MODBUS_BRIDGE +#if defined(USE_MODBUS_BRIDGE) /*********************************************************************************************\ * 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: - * 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_SPEED TM_MODBUS_BAUDRATE -#define MBR_MAX_REGISTERS 64 +#define MBR_MAX_VALUE_LENGTH 30 +#define MBR_SPEED TM_MODBUS_BAUDRATE +#define MBR_MAX_REGISTERS 64 #define D_CMND_MODBUS_SEND "Send" #define D_CMND_MODBUS_SETBAUDRATE "Baudrate" @@ -45,11 +50,41 @@ #define D_JSON_MODBUS_VALUES "Values" #define D_JSON_MODBUS_LENGTH "Length" +#ifndef USE_MODBUS_BRIDGE_TCP const char kModbusBridgeCommands[] PROGMEM = "Modbus|" // Prefix D_CMND_MODBUS_SEND "|" D_CMND_MODBUS_SETBAUDRATE "|" D_CMND_MODBUS_SETSERIALCONFIG; void (*const ModbusBridgeCommand[])(void) PROGMEM = { &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 *tasmotaModbus = nullptr; @@ -80,15 +115,13 @@ enum class ModbusBridgeFunctionCode enum class ModbusBridgeType { mb_undefined, - mb_uint8, mb_uint16, mb_uint32, - mb_int8, mb_int16, mb_int32, mb_float, mb_raw, - mb_bit8 + mb_bit }; enum class ModbusBridgeEndian @@ -116,10 +149,21 @@ struct 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) @@ -131,33 +175,83 @@ void SetModbusBridgeConfig(uint32_t serial_config) if (serial_config != Settings->sserial_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) { bool data_ready = tasmotaModbus->ReceiveReady(); if (data_ready) { 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); - ModbusBridgeError errorcode = ModbusBridgeError::noerror; if (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; + } else if (modbusBridge.deviceAddress != (uint8_t)buffer[0]) errorcode = ModbusBridgeError::wrongdeviceaddress; else if ((uint8_t)modbusBridge.functionCode != (uint8_t)buffer[1]) 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; else { @@ -174,7 +268,7 @@ void ModbusBridgeHandle(void) ResponseJsonEnd(); 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 "\":{")); ResponseAppend_P(PSTR("\"" D_JSON_MODBUS_DEVICE_ADDRESS "\":%d,"), buffer[0]); @@ -222,15 +316,7 @@ void ModbusBridgeHandle(void) else snprintf(svalue, MBR_MAX_VALUE_LENGTH, "%u", value); } - else if ((modbusBridge.type == ModbusBridgeType::mb_int8) || - (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) + else if (modbusBridge.type == ModbusBridgeType::mb_bit) { 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)); @@ -247,6 +333,8 @@ void ModbusBridgeHandle(void) if (errorcode == ModbusBridgeError::noerror) MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_MODBUS_RECEIVED)); } + else + errorcode = ModbusBridgeError::wrongfunctioncode; } 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) { if (PinUsed(GPIO_MBR_RX) && PinUsed(GPIO_MBR_TX)) { tasmotaModbus = new TasmotaModbus(Pin(GPIO_MBR_RX), Pin(GPIO_MBR_TX)); - uint8_t result = tasmotaModbus->Begin(MBR_SPEED); - if (result) + SetModbusBridgeConfig(TS_SERIAL_8E1); + 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) - { - 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); + AddLog(LOG_LEVEL_ERROR, PSTR("MBS: MBRTCP could not allocate buffer")); + return; } +#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 \*********************************************************************************************/ @@ -309,55 +492,46 @@ void CmndModbusBridgeSend(void) } modbusBridge.type = ModbusBridgeType::mb_undefined; - if (strcmp(stype, "int8") == 0) - { - modbusBridge.type = ModbusBridgeType::mb_int8; - modbusBridge.registerCount = 1 * modbusBridge.count; - } - else if (strcmp(stype, "int16") == 0) + if (strcmp(stype, "int16") == 0) { modbusBridge.type = ModbusBridgeType::mb_int16; - modbusBridge.registerCount = 2 * modbusBridge.count; + modbusBridge.registerCount = modbusBridge.count; } else if (strcmp(stype, "int32") == 0) { modbusBridge.type = ModbusBridgeType::mb_int32; - modbusBridge.registerCount = 4 * modbusBridge.count; - } - else if (strcmp(stype, "uint8") == 0) - { - modbusBridge.type = ModbusBridgeType::mb_uint8; - modbusBridge.registerCount = 1 * modbusBridge.count; + modbusBridge.registerCount = 2 * modbusBridge.count; } else if (strcmp(stype, "uint16") == 0) { modbusBridge.type = ModbusBridgeType::mb_uint16; - modbusBridge.registerCount = 2 * modbusBridge.count; + modbusBridge.registerCount = modbusBridge.count; } else if (strcmp(stype, "uint32") == 0) { modbusBridge.type = ModbusBridgeType::mb_uint32; - modbusBridge.registerCount = 4 * modbusBridge.count; + modbusBridge.registerCount = 2 * modbusBridge.count; } else if (strcmp(stype, "float") == 0) { modbusBridge.type = ModbusBridgeType::mb_float; - modbusBridge.registerCount = 4 * modbusBridge.count; + modbusBridge.registerCount = 2 * modbusBridge.count; } else if (strcmp(stype, "raw") == 0) { modbusBridge.type = ModbusBridgeType::mb_raw; 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; } else 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) { @@ -371,13 +545,8 @@ void CmndModbusBridgeSend(void) void CmndModbusBridgeSetBaudrate(void) { - if (XdrvMailbox.payload >= 300) - { - XdrvMailbox.payload /= 300; // Make it a valid baudrate - Settings->sbaudrate = XdrvMailbox.payload; - SetModbusBridgeBegin(); - } - ResponseCmndNumber(Settings->sbaudrate * 300); + SetModbusBridgeBaudrate(XdrvMailbox.payload); + ResponseCmndNumber(Settings->baudrate * 300); } void CmndModbusBridgeSetConfig(void) @@ -407,6 +576,114 @@ void CmndModbusBridgeSetConfig(void) ResponseCmndChar(GetSerialConfig(Settings->sserial_config).c_str()); } +#ifdef USE_MODBUS_BRIDGE_TCP +// +// Command `TCPStart` +// Params: port, +// +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, +// +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 \*********************************************************************************************/ @@ -423,12 +700,15 @@ bool Xdrv63(uint8_t function) { switch (function) { - case FUNC_EVERY_250_MSECOND: - ModbusBridgeHandle(); - break; case FUNC_COMMAND: result = DecodeCommand(kModbusBridgeCommands, ModbusBridgeCommand); break; + case FUNC_LOOP: + ModbusBridgeHandle(); +#ifdef USE_MODBUS_BRIDGE_TCP + ModbusTCPHandle(); +#endif + break; } } return result;