diff --git a/examples/Bridge/README.md b/examples/Bridge/README.md index e03a7c9..9ed49c5 100644 --- a/examples/Bridge/README.md +++ b/examples/Bridge/README.md @@ -1,17 +1,21 @@ # Bridge functions -## Basic +## [Basic](basic/basic.ino) Basic 'Bridge'. Indeed this sample pulling data from Modbus Server and stores it to local registers. Local registers can be accessed via Modbus Client instance that running aside. -## True +## [ModbusRTU to ModbusTCP bridge](true/true.ino) Fullfunctional ModbusRTU to ModbusTCP bridge. -## MultipeServerID +## [Multiple Server ID](MultipeServerID/MultipeServerID.ino) Respond for multiple ModbusRTU IDs from single device +## [ModbusTCP to Modbus RTU Simulator](TCP-to-RTU-Simulator/TCP-to-RTU-Simulator.ino) + +Fullfunctional ModbusTCP to ModbusRTU bridge with on-device ModbusRTU simulator + ```c uint16_t rawRequest(id_ip, uint8_t* data, uint16_t len, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT); uint16_t rawResponce(id_ip, uint8_t* data, uint16_t len, uint8_t unit = MODBUSIP_UNIT); @@ -22,7 +26,12 @@ uint16_t errorResponce(id_ip, Modbus::FunctionCode fn, Modbus::ResultCode excode - `len` Byte count to send - `unit` UnitId (ModbusTCP/TLS only) - `fn` function code in responce -- `excode` Exceprion code in responce +- `excode` Exception code in responce + +```c +uint16_t setTransactionId(uint16_t id); +``` +- `id` Value to replace transaction id sequence (ModbusTCP/TLS only) ```c union frame_arg_t { diff --git a/examples/Bridge/TCP-to-RTU-Simulator/TCP-to-RTU-Simulator.ino b/examples/Bridge/TCP-to-RTU-Simulator/TCP-to-RTU-Simulator.ino new file mode 100644 index 0000000..5b94947 --- /dev/null +++ b/examples/Bridge/TCP-to-RTU-Simulator/TCP-to-RTU-Simulator.ino @@ -0,0 +1,134 @@ +/* + ModbusRTU ESP8266/ESP32 + ModbusTCP to ModbusRTU bridge with on-device ModbusRTU simulator +*/ +#ifdef ESP8266 + #include +#else //ESP32 + #include +#endif +#include +#include +//#include +//SoftwareSerial S(13, 15); +#include +#define BSIZE 1024 +uint8_t buf1[BSIZE]; +uint8_t buf2[BSIZE]; +StreamBuf S1(buf1, BSIZE); +StreamBuf S2(buf2, BSIZE); +DuplexBuf P1(&S1, &S2); +DuplexBuf P2(&S2, &S1); +ModbusRTU sym; + +int DE_RE = 2; + +ModbusRTU rtu; +ModbusTCP tcp; + +IPAddress srcIp; + + +uint16_t transRunning = 0; // Currently executed ModbusTCP transaction +uint8_t slaveRunning = 0; // Current request slave + +bool cbTcpTrans(Modbus::ResultCode event, uint16_t transactionId, void* data) { // Modbus Transaction callback + if (event != Modbus::EX_SUCCESS) // If transaction got an error + Serial.printf("Modbus result: %02X, Mem: %d\n", event, ESP.getFreeHeap()); // Display Modbus error code (222527) + if (event == Modbus::EX_TIMEOUT) { // If Transaction timeout took place + tcp.disconnect(tcp.eventSource()); // Close connection + } + return true; +} + +bool cbRtuTrans(Modbus::ResultCode event, uint16_t transactionId, void* data) { + if (event != Modbus::EX_SUCCESS) // If transaction got an error + Serial.printf("Modbus result: %02X, Mem: %d\n", event, ESP.getFreeHeap()); // Display Modbus error code (222527) + return true; +} + + +// Callback receives raw data +Modbus::ResultCode cbTcpRaw(uint8_t* data, uint8_t len, void* custom) { + auto src = (Modbus::frame_arg_t*) custom; + + Serial.print("TCP IP: "); + Serial.print(IPAddress(src->ipaddr)); + Serial.printf(" Fn: %02X, len: %d \n\r", data[0], len); + + if (transRunning) { // Note that we can't process new requests from TCP-side while waiting for responce from RTU-side. + tcp.setTransactionId(transRunning); // Set transaction id as per incoming request + tcp.errorResponce(src->ipaddr, (Modbus::FunctionCode)data[0], Modbus::EX_SLAVE_DEVICE_BUSY); + return Modbus::EX_SLAVE_DEVICE_BUSY; + } + + rtu.rawRequest(slaveRunning, data, len, cbRtuTrans); + + if (src->unitId) { + tcp.setTransactionId(transRunning); // Set transaction id as per incoming request + + //uint16_t succeed = tcp.rawResponce(src->ipaddr, data, len, slaveRunning); + + tcp.errorResponce(src->ipaddr, (Modbus::FunctionCode)data[0], Modbus::EX_ACKNOWLEDGE); + return Modbus::EX_ACKNOWLEDGE; + } + + srcIp = src->ipaddr; + + slaveRunning = src->slaveId; + + transRunning = src->transactionId; + + return Modbus::EX_SUCCESS; + +} + + +// Callback receives raw data from ModbusTCP and sends it on behalf of slave (slaveRunning) to master +Modbus::ResultCode cbRtuRaw(uint8_t* data, uint8_t len, void* custom) { + auto src = (Modbus::frame_arg_t*) custom; + tcp.setTransactionId(transRunning); // Set transaction id as per incoming request + uint16_t succeed = tcp.rawResponce(srcIp, data, len, slaveRunning); + if (!succeed){ + Serial.print("fail"); + } + Serial.printf("RTU Slave: %d, Fn: %02X, len: %d, ", src->slaveId, data[0], len); + Serial.print("Response TCP IP: "); + Serial.println(srcIp); + + transRunning = 0; + slaveRunning = 0; + return Modbus::EX_PASSTHROUGH; +} + + +void setup() { + Serial.begin(115000); + WiFi.begin("E2", "*****"); + while (WiFi.status() != WL_CONNECTED) { + delay(1000); + Serial.print("."); + } + Serial.println(""); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + + tcp.server(); // Initialize ModbusTCP to pracess as server + tcp.onRaw(cbTcpRaw); // Assign raw data processing callback + + //S.begin(19200, SWSERIAL_8E1); + //rtu.begin(&S, DE_RE); // Specify RE_DE control pin + sym.begin((Stream*)&P2); + sym.slave(1); + sym.addHreg(1, 100); + rtu.begin((Stream*)&P1); // Specify RE_DE control pin + rtu.master(); // Initialize ModbusRTU as master + rtu.onRaw(cbRtuRaw); // Assign raw data processing callback +} + +void loop() { + sym.task(); + rtu.task(); + tcp.task(); + yield(); +} \ No newline at end of file diff --git a/examples/README.md b/examples/README.md index c398860..b779609 100644 --- a/examples/README.md +++ b/examples/README.md @@ -24,7 +24,7 @@ Examples of using callback functions. Modbus file operations examples. -## [Modbus RTU to Modbus TCP bridge](bridge) +## [ModbusRTU to ModbusTCP bridge and related functions](bridge) Very basic example of accessing ModbusRTU slave device connected to ESP8266/ESP32 via ModbusTCP server.