I'm testing the following code as a bridge between https://sourceforge.net/projects/qmodmaster/ and https://pypi.org/project/pyModSlave/.
As hardware, I'm using a NodeMCU 1.0 and a USB/TTL converter.
The Modbus library is https://github.com/emelianov/modbus-esp8266.
#include <ESP8266WiFi.h>
#include <ModbusTCP.h>
#include <ModbusRTU.h>
#include <SoftwareSerial.h>
#include <StreamBuf.h>
#define BSIZE 1024
const unsigned long baudrate = 19200;
const unsigned long chtimeus = (8 + 3) * 1000000 / baudrate;
ModbusRTU rtu;
ModbusTCP tcp;
IPAddress srcIp;
SoftwareSerial debug_serial(D1, D2);
uint8_t serialw_rtur[BSIZE];
uint8_t serialr_rtuw[BSIZE];
StreamBuf serialw_rtur_sb(serialw_rtur, BSIZE);
StreamBuf serialr_rtuw_sb(serialr_rtuw, BSIZE);
DuplexBuf modbus_db(&serialw_rtur_sb, &serialr_rtuw_sb);
DuplexBuf serial_db(&serialr_rtuw_sb, &serialw_rtur_sb);
uint8_t com_buf[BSIZE];
uint8_t log_buf[BSIZE];
StreamBuf com_sbuf(com_buf, BSIZE);
StreamBuf log_sbuf(log_buf, BSIZE);
uint16_t transRunning = 0;
uint8_t slaveRunning = 0;
bool cbRtuTrans(Modbus::ResultCode event, uint16_t transactionId, void* data) {
if (event != Modbus::EX_SUCCESS) {
debug_serial.printf("modbusRTU transaction result: %02X\n\r", event);
transRunning = 0;
}
return true;
}
Modbus::ResultCode cbTcpRaw(uint8_t* data, uint8_t len, void* custom) {
auto src = (Modbus::frame_arg_t*) custom;
debug_serial.print("received a TCP request from ");
debug_serial.print(IPAddress(src->ipaddr));
debug_serial.printf("-> Fn: %02X, len: %d\n\r", data[0], len);
if (transRunning) {
tcp.setTransactionId(src->transactionId);
tcp.errorResponce(IPAddress(src->ipaddr), (Modbus::FunctionCode)data[0], Modbus::EX_SLAVE_DEVICE_BUSY);
return Modbus::EX_SLAVE_DEVICE_BUSY;
}
rtu.rawRequest(src->unitId, data, len, cbRtuTrans);
if (!src->unitId) {
tcp.setTransactionId(src->transactionId);
tcp.errorResponce(IPAddress(src->ipaddr), (Modbus::FunctionCode)data[0], Modbus::EX_ACKNOWLEDGE);
transRunning = 0;
slaveRunning = 0;
return Modbus::EX_ACKNOWLEDGE;
}
srcIp = IPAddress(src->ipaddr);
slaveRunning = src->unitId;
transRunning = src->transactionId;
return Modbus::EX_SUCCESS;
}
Modbus::ResultCode cbRtuRaw(uint8_t* data, uint8_t len, void* custom) {
auto src = (Modbus::frame_arg_t*)custom;
debug_serial.printf("received RTU response from %d-> Fn: %02X, len: %d\n\r", src->slaveId, data[0], len);
tcp.setTransactionId(transRunning);
uint16_t succeed = tcp.rawResponce(srcIp, data, len, slaveRunning);
if (!succeed) {
debug_serial.print("failed to");
}
debug_serial.print("respond to TCP: ");
debug_serial.println(srcIp);
transRunning = 0;
slaveRunning = 0;
return Modbus::EX_PASSTHROUGH;
}
void print_log(const char *msg) {
debug_serial.print(msg);
debug_serial.print(": ");
while(log_sbuf.available()) {
debug_serial.print(log_sbuf.read(), HEX);
debug_serial.print(' ');
}
debug_serial.println();
}
void process_from_to(Stream from_s, Stream to_s, unsigned delay_us = 0) {
while(from_s->available()) {
char c = from_s->read();
com_sbuf.write(c);
log_sbuf.write(c);
if(delay_us != 0)
delayMicroseconds(delay_us);
}
while(com_sbuf.available())
to_s->write(com_sbuf.read());
}
void setup() {
Serial.begin(baudrate, SERIAL_8E1);
debug_serial.begin(baudrate, SWSERIAL_8E1);
WiFi.begin("<SSID>", "<PASSWORD>");
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
debug_serial.print(".");
}
debug_serial.println("");
debug_serial.println("IP address: ");
debug_serial.println(WiFi.localIP());
tcp.server();
tcp.onRaw(cbTcpRaw);
rtu.begin((Stream*)&modbus_db);
rtu.master();
rtu.onRaw(cbRtuRaw);
}
void loop() {
rtu.task();
tcp.task();
yield();
if(Serial.available()) {
process_from_to(&Serial, &serial_db, chtimeus << 1);
print_log("received response from RTU");
}
if(serial_db.available()) {
process_from_to(&serial_db, &Serial);
print_log("sent request to RTU");
}
}
qModMaster has the following configs:
- Modbus Mode: TCP
- Slave Addr: 1
- Scan Rate: 5 s
- Function Code: 0x03
- Start Address: 0x00
- Register Number: 10
- Slave IP: my ESP's IP in the network
- Slave Port: 502
pyModSlave has the following configs:
- Modbus Mode: RTU
- Slave Address: 1
- SimCycle: 5 s
- Port: my ESP's Serial's USB port
- Baudrate: 19200
- Data Bits: 8
- Stop Bits: 1
- Parity: None
I've already tried using hard and soft Serial, baudrates of 9600 and 19200 and powering the board with a regulated supply @5V - 1A.
Both programs work normally when I communicate them directly with the ESP, either reading from the RTU slave or receiving reads from the TCP client.
When I use ESP as a bridge, the RTU slave receives the request normally and responds correctly in <5ms, but the timeout occurs.
An external monitor (a Uno with the Rx of a SoftwareSerial connected to the NodeMCU hardware Rx pin) shows that the correct data reaches the ESP:
rtu monitor:
[RTU]>Rx > 18:21:37:626 - 01 03 00 00 00 0A C5 CD
[RTU]>Tx > 18:21:37:626 - 01 03 14 9D AC D8 0D 61 1A 29 7B 0E 7D 19 15 62 01 C2 F1 9E CF 41 C6 3C D4
[RTU]>Rx > 18:21:47:674 - 01 03 00 00 00 0A C5 CD
[RTU]>Tx > 18:21:47:675 - 01 03 14 A7 EE 7E F2 5D 43 66 D7 4B 73 3A 0A D2 B6 23 BB F0 E4 AB D0 CD 9E
[RTU]>Rx > 18:21:57:590 - 01 03 00 00 00 0A C5 CD
[RTU]>Tx > 18:21:57:590 - 01 03 14 D0 31 DC 14 E7 A2 84 49 C6 3A 1D 13 BD 7E DD DF E1 C7 02 89 F0 80
[RTU]>Rx > 18:22:07:638 - 01 03 00 00 00 0A C5 CD
[RTU]>Tx > 18:22:07:639 - 01 03 14 71 26 FE 5B 12 23 A7 F4 39 0A 55 12 F9 E4 63 27 90 0C 18 11 00 51
[RTU]>Rx > 18:22:17:682 - 01 03 00 00 00 0A C5 CD
[RTU]>Tx > 18:22:17:683 - 01 03 14 1C 9D A1 68 11 71 A4 96 1D BF D4 C3 86 B1 B2 42 68 C4 72 BD C6 A3
[RTU]>Rx > 18:22:27:722 - 01 03 00 00 00 0A C5 CD
[RTU]>Tx > 18:22:27:724 - 01 03 14 93 6B 28 A4 F2 DD 3D E1 C3 29 61 82 EF CC E3 37 99 AA 32 04 3A 56
external monitor:
response: 1 3 14 9D AC D8 D 61 1A 29 7B E 7D 19 15 62 1 C2 F1 9E CF 41 C6 3C D4
response: 1 3 14 A7 EE 7E F2 5D 43 66 D7 4B 73 3A A D2 B6 23 BB F0 E4 AB D0 CD 9E
response: 1 3 14 D0 31 DC 14 E7 A2 84 49 C6 3A 1D 13 BD 7E DD DF E1 C7 2 89 F0 80
response: 1 3 14 71 26 FE 5B 12 23 A7 F4 39 A 55 12 F9 E4 63 27 90 C 18 11 0 51
response: 1 3 14 1C 9D A1 68 11 71 A4 96 1D BF D4 C3 86 B1 B2 42 68 C4 72 BD C6 A3
response: 1 3 14 93 6B 28 A4 F2 DD 3D E1 C3 29 61 82 EF CC E3 37 99 AA 32 4 3A 56
bridge monitor:
received a TCP request from 192.168.1.12-> Fn: 03, len: 5
sent request to RTU: 1 3 0 0 0 A C5 CD
received response from RTU: 1 20 51 C6 C3 D 58 A3 69 39 7D 46 91 5 C2 7C F6 41 1C A2
received a TCP request from 192.168.1.12-> Fn: 03, len: 5
modbusRTU transaction result: E4
received a TCP request from 192.168.1.12-> Fn: 03, len: 5
sent request to RTU: 1 3 0 0 0 A C5 CD
received response from RTU: 1 20 D1 73 7E BE 35 99 D7 A9 E9 A 3A DB DA F0 DE D D7 9E
received a TCP request from 192.168.1.12-> Fn: 03, len: 5
modbusRTU transaction result: E4
received a TCP request from 192.168.1.12-> Fn: 03, len: 5
sent request to RTU: 1 3 0 0 0 A C5 CD
received response from RTU: 1 20 11 17 E2 14 9E 23 49 9C EA 13 6F 75 DF 78 9C 89 80
received a TCP request from 192.168.1.12-> Fn: 03, len: 5
modbusRTU transaction result: E4
received a TCP request from 192.168.1.12-> Fn: 03, len: 5
sent request to RTU: 1 3 0 0 0 A C5 CD
received response from RTU: 1 20 51 99 FE 2B 34 3A F4 4E A1 95 94 E4 AC 12 C6 18 44 51
received a TCP request from 192.168.1.12-> Fn: 03, len: 5
modbusRTU transaction result: E4
received a TCP request from 192.168.1.12-> Fn: 03, len: 5
sent request to RTU: 1 3 0 0 0 A C5 CD
received response from RTU: 1 20 11 D4 A1 2B 14 91 96 47 A3 C3 58 93 42 8B 72 6F DC FF
received a TCP request from 192.168.1.12-> Fn: 03, len: 5
modbusRTU transaction result: E4
received a TCP request from 192.168.1.12-> Fn: 03, len: 5
sent request to RTU: 1 3 0 0 0 A C5 CD
received response from RTU: 1 20 D1 B6 A1 A4 BE D7 A C3 4A 9 EF 6E 37 66 35 22 3A F5
received a TCP request from 192.168.1.12-> Fn: 03, len: 5
modbusRTU transaction result: E4
tcp monitor
[TCP]>Tx > 18:21:37:517 - 00 16 00 00 00 06 01 03 00 00 00 0A
Sys > 18:21:38:031 - Error : Timeout
[TCP]>Tx > 18:21:42:523 - 00 17 00 00 00 06 01 03 00 00 00 0A
[TCP]>Rx > 18:21:42:649 - 00 17 00 00 00 03 FF 83 06
Sys > 18:21:42:649 - Error : Slave device or server is busy
[TCP]>Tx > 18:21:47:524 - 00 18 00 00 00 06 01 03 00 00 00 0A
Sys > 18:21:48:039 - Error : Timeout
[TCP]>Tx > 18:21:52:525 - 00 19 00 00 00 06 01 03 00 00 00 0A
[TCP]>Rx > 18:21:52:684 - 00 19 00 00 00 03 FF 83 06
Sys > 18:21:52:684 - Error : Slave device or server is busy
[TCP]>Tx > 18:21:57:521 - 00 1A 00 00 00 06 01 03 00 00 00 0A
Sys > 18:21:58:027 - Error : Timeout
[TCP]>Tx > 18:22:02:536 - 00 1B 00 00 00 06 01 03 00 00 00 0A
[TCP]>Rx > 18:22:02:595 - 00 1B 00 00 00 03 FF 83 06
Sys > 18:22:02:595 - Error : Slave device or server is busy
[TCP]>Tx > 18:22:07:543 - 00 1C 00 00 00 06 01 03 00 00 00 0A
Sys > 18:22:08:044 - Error : Timeout
[TCP]>Tx > 18:22:12:539 - 00 1D 00 00 00 06 01 03 00 00 00 0A
[TCP]>Rx > 18:22:12:633 - 00 1D 00 00 00 03 FF 83 06
Sys > 18:22:12:633 - Error : Slave device or server is busy
[TCP]>Tx > 18:22:17:542 - 00 1E 00 00 00 06 01 03 00 00 00 0A
Sys > 18:22:18:047 - Error : Timeout
[TCP]>Tx > 18:22:22:531 - 00 1F 00 00 00 06 01 03 00 00 00 0A
[TCP]>Rx > 18:22:22:577 - 00 1F 00 00 00 03 FF 83 06
Sys > 18:22:22:577 - Error : Slave device or server is busy
[TCP]>Tx > 18:22:27:536 - 00 20 00 00 00 06 01 03 00 00 00 0A
Sys > 18:22:28:041 - Error : Timeout
[TCP]>Tx > 18:22:32:534 - 00 21 00 00 00 06 01 03 00 00 00 0A
[TCP]>Rx > 18:22:32:595 - 00 21 00 00 00 03 FF 83 06
Sys > 18:22:32:595 - Error : Slave device or server is busy
Please, any hint on what to test or what else to investigate? Thanks.