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

[Question] How to respond to broadcast message on RTU to TCP bridge #198

Closed veista closed 2 years ago

veista commented 2 years ago

Hi,

I ran into a problem: How do I support broadcasting to RTU devices and still respond accordingly to TCP side?

Any suggestions?

emelianov commented 2 years ago

ModbusTCP request with unit_id 0 set should produce RTU broadcast. ModbusRTU devices will not send any response so you may make up response by yourself.

(Just for historical reasons: the question is related to #191)

veista commented 2 years ago

Ok, thanks for the quick response. Is there an easy way of doing this or should I generate my own handler?

emelianov commented 2 years ago

I don't know what ModbusTCP client expects to receive in you installation. In my opinion reasonable behaviour is to return EX_ACKNOWLEDGE error code. Otherwise you can build custom data and send id back with rawResponce.

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

}
veista commented 2 years ago

Yes, this was my easy solution too. I am building just a generic bridge. My personal end application does not use this function, the issue just arose when I accidentally wrote to ID0 and did not handle this case.

I read conflicting information about this issue:

  1. Should let TCP timeout as RTU does: I could not make this happen with your library: how do you terminate a frame without responding in your library?
  2. Should respond something ie. a valid empty response
  3. Should give some error response, I read this does not play well in applications where this functionality is in use

My opinion is that number 2 is correct, since TCP does not support this function, but we want the endpoint to acknowledge that it was done correctly.

I just wanted to ask if you had an opinion on the matter.

On another subject, when are you planning to release the latest bug fix? I was planning to publish my project during this month and it would make it straight forward if there was a release.

Thank you for your help yet again!

emelianov commented 2 years ago
  1. Should let TCP timeout as RTU does: I could not make this happen with your library: how do you terminate a frame without responding in your library?
  2. Should respond something ie. a valid empty response
  3. Should give some error response, I read this does not play well in applications where this functionality is in use
  1. If you send no rawResponce or rawError in bridge application it will be timeout at TCP client side. It's worse behaviour in my opinion.
  2. In this case you need reply not with empty response but with correct one. That is you need to reply with response matches the request. (For example the library checks that size of the response and operation code are matches original request expected result). Also returning success result indicates to the client that request processed by target devices but indeed we have no information if request is really processed by any RTU device. From other hand if UnitID is set as 0 by mistake client get no error indication and this will lead to absolutely unpredictable situation.
  3. As client should understand how broadcast is function it must have special processing for such queries. That case error code return is absolutely valid behaviour in my opinion.

On another subject, when are you planning to release the latest bug fix? I was planning to publish my project during this month and it would make it straight forward if there was a release.

No problem. I think the library release may be scheduled, say, on 10-15 of April.

veista commented 2 years ago

Thank you. You are right. I will implement it with the error response.

emelianov commented 2 years ago

No problem. I think the library release may be scheduled, say, on 10-15 of April.

@veista 4.1.0 released