emelianov / modbus-esp8266

Most complete Modbus library for Arduino. A library that allows your Arduino board to communicate via Modbus protocol, acting as a master, slave or both. Supports network transport (Modbus TCP) and Serial line/RS-485 (Modbus RTU). Supports Modbus TCP Security for ESP8266/ESP32.
Other
534 stars 188 forks source link

Cannot Respond to TCP Master with rawResponce #191

Closed veista closed 2 years ago

veista commented 2 years ago

Hi,

Thank you for your amazing library. I ran into this issue, when I am trying to implement a ModBus TCP to RTU Bridge with ESP8266.

Here is my code:

#ifdef ESP8266
 #include <ESP8266WiFi.h>
#else //ESP32
 #include <WiFi.h>
#endif
#include <ModbusTCP.h>
#include <ModbusRTU.h>
#include <SoftwareSerial.h>

SoftwareSerial S(13, 15);

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);

  srcIp = src->ipaddr;

  slaveRunning = src->slaveId;

  transRunning = src->transactionId;

  rtu.rawRequest(slaveRunning, data, len, cbRtuTrans);

  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;
  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.print(srcIp);

  transRunning = 0;
  slaveRunning = 0;
  return Modbus::EX_PASSTHROUGH;
}

void setup() {
  Serial.begin(115000);
  WiFi.begin("SSID", "PASSWORD");
  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
  rtu.master(); // Initialize ModbusRTU as master
  rtu.onRaw(cbRtuRaw); // Assign raw data processing callback
}

void loop() { 
  rtu.task();
  tcp.task();
  yield();
}

Receiving from TCP works, sending to RTU works, reading from RTU works, Responding to TCP fails.

I have tried everything that comes to mind. Any help would be appreciated.

emelianov commented 2 years ago

Let me some time to check...

veista commented 2 years ago

Great! If I can help in any way, please let me know.

emelianov commented 2 years ago

Thanks for pointing the issue. Fixed library code just pushed to master branch. Simulator example based on you code: (The code is implementing RTU master and slave on the same ESP device but you can easily revert it to SoftwareSerial)

#ifdef ESP8266
 #include <ESP8266WiFi.h>
#else //ESP32
 #include <WiFi.h>
#endif
#include <ModbusTCP.h>
#include <ModbusRTU.h>
//#include <SoftwareSerial.h>
//SoftwareSerial S(13, 15);
#include <StreamBuf.h>
#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.errorResponce(src->ipaddr, (Modbus::FunctionCode)data[0], Modbus::EX_SLAVE_DEVICE_BUSY);
    return Modbus::EX_SLAVE_DEVICE_BUSY;
  }

  srcIp = src->ipaddr;

  slaveRunning = src->slaveId;

  transRunning = src->transactionId;

  rtu.rawRequest(slaveRunning, data, len, cbRtuTrans);

  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();
}
veista commented 2 years ago

Worked like a charm. Thank you so much!

FreFre commented 1 year ago

Thank you for this very elaborated library. I'm a bit of a beginner and I'm having issues resolving the following line #include <StreamBuf.h> I'm using a Wemos D1 mini v4. Where do you get this library?

veista commented 1 year ago

Take a look here if it helps https://github.com/veista/modbus_bridge

emelianov commented 1 year ago

#include <StreamBuf.h> Where do you get this library?

https://github.com/emelianov/StreamBuf