Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

help needed on actual modbusRTU implementation #114

Open
rzylius opened this issue Mar 24, 2021 · 27 comments
Open

help needed on actual modbusRTU implementation #114

rzylius opened this issue Mar 24, 2021 · 27 comments

Comments

@rzylius
Copy link

rzylius commented Mar 24, 2021

Dear Alexander,

I have a solar inverter with quite a specification:

The IMEON RS485 port is configured to use the following

  • Baud rate : 9600
  • Data bits per Byte : 8
  • Parity : None
  • Stop bits : 1
  • Encoding: big endian(MSB LSB: value 0x
  • CRC: little endian (LSB MSB: value 0x3412 is 0x1234)
  • Slave address: 0x01
  • Data message format: Hexadecimal
  • The instant read values in the database are updated each 10 seconds.
  • The set values are updated each 1 minute in database.
  • Response timeout is 10 seconds.

I got connected and all works pretty ok out of the box. But I quite often I receive error (not consistently)

EX_TIMEOUT = 0xE4, // Custom. Operation not finished within reasonable time

I tried to play with #define MODBUSIP_TIMEOUT 1000 setting, without apparent improvement. maybe you may advise the logic how I should try to customize settings for it to work reliably?

Very much appreciate your work,

regards, Rimantas

@emelianov
Copy link
Owner

Hi,
Typically timeout errors is result of wiring problems. Probably flow control is required for transceiver used.

@rzylius
Copy link
Author

rzylius commented Mar 24, 2021

Typically timeout errors is result of wiring problems.

I have controller 3 metres away from the inverter. and 50-70% of transactions do get good response. I thought this means wiring is ok? Or what should I check regarding the wiring?

Probably flow control is required for transceiver used.

Can you elaborate this? Should I use Serial (now I have softwareSerial for that)? Any specific settings you would avise to try?

Technical document of inverter specifies "Response timeout is 10 seconds.". This sounds to me like an exceptional setting, could that be a source of the problem that inverter may be "slow" in quite some cases and controller just timesout?

@emelianov
Copy link
Owner

Basing on you initial post I was suspecting some issue with RS485 transceiver. If most of transactions are ok that was wrong idea.
Still not clear about physical layer: if it's RS845 or Serial TTL ?
Anyway physical data transfer problems is most possible reason of loosing part of packets.
From other hand basing on such long timeout you may try to increase inter frame delay by overwriting setBaudrate(..) in Modbus.cpp to something like

void ModbusRTUTemplate::setBaudrate(uint32_t baud) {
    _t = 4; // play with the value
}

@rzylius
Copy link
Author

rzylius commented Mar 25, 2021

Still not clear about physical layer: if it's RS845 or Serial TTL ?

RS845 in my case, I have esp8266 with max485.

I will try to replace the wire, adn will try to play with setBaudrate

@rzylius
Copy link
Author

rzylius commented Mar 27, 2021

Dear Alexander,

I would very much appreciate your further advice. I tested read and write operations with inverter with my windows10/usbRS485 adapter and qModbusMaster. I do not have with this config much of the access to configuration, but 9800 baud rate / 8N1 / no flow control / 1sec timeout gives me very rare timeouts (like 5%).

on esp8266 I tried both Serial and softwareserial ports.
Your advised setBaudrate _t = 4 works best, but I get ~30-40% timeouts. other integer values more or less degrade performance.

#define MODBUSRTU_TIMEOUT 2000
decreasing this value lower than 1000 increases significantly timeouts. But further increases do not give better results,

Wiring I believe is not a problem, as other adapter on the same wiring works well.

I would very much appreciate your advice how should find out the problem or what settings I should try to test.

Thanks a lot.

@rzylius
Copy link
Author

rzylius commented Mar 28, 2021

I tried with MODBUSrtU_DEBUG option, but not even sure where to start comprehending.

09:39:24.087 -> --------------- CUT HERE FOR EXCEPTION DECODER ---------------
09:39:24.087 ->
09:39:24.087 -> ets Jan 8 2013,rst cause:2, boot mode:(3,6)
09:39:24.087 ->
09:39:24.087 -> load 0x4010f000, len 3584, room 16
09:39:24.087 -> tail 0
09:39:24.087 -> chksum 0xb0
09:39:24.087 -> csum 0xb0
09:39:24.087 -> v2843a5ac
09:39:24.087 -> ~ld
09:39:25.234 -> 10 90 00 00 04 EC CA
09:39:25.234 -> 10 90 00 00 04 EC CA
09:39:27.259 -> --------------- CUT HERE FOR EXCEPTION DECODER ---------------
09:39:27.259 ->
09:39:27.259 -> Exception (28):
09:39:27.259 -> epc1=0x4020e25b epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000000 depc=0x00000000
09:39:27.259 ->
09:39:27.259 -> >>>stack>>>
09:39:27.259 ->
09:39:27.259 -> ctx: cont
09:39:27.259 -> sp: 3ffffd60 end: 3fffffc0 offset: 0190
09:39:27.259 -> 3ffffef0: 0000010a 3ffef608 3ffef5a0 4020e295
09:39:27.259 -> 3fffff00: 00000003 00000c54 00000001 00000001
09:39:27.259 -> 3fffff10: 40201064 3ffefeac 402129f4 00000003
09:39:27.306 -> 3fffff20: 3ffef538 3ffef608 3ffef5a0 40202327
09:39:27.306 -> 3fffff30: 00000000 00000001 4b020c49 00000000
09:39:27.306 -> 3fffff40: 40201064 00000000 402129f4 402129dc
09:39:27.306 -> 3fffff50: 40201064 00000000 402129f4 402129dc
09:39:27.306 -> 3fffff60: 00000000 00000100 00000003 40210100
09:39:27.306 -> 3fffff70: 0000000b fe007465 40212a08 402011c4
09:39:27.306 -> 3fffff80: 3fff0b00 00000001 00000002 3ffefe4c
09:39:27.306 -> 3fffff90: 00000001 3ffef6f0 3ffef6f8 4020241e
09:39:27.306 -> 3fffffa0: 3fffdad0 00000000 3ffefe0c 40210ec0
09:39:27.306 -> 3fffffb0: feefeffe feefeffe 3ffe850c 401014b5
09:39:27.306 -> <<<stack<<<
09:39:27.306 ->
09:39:27.306 -> --------------- CUT HERE FOR EXCEPTION DECODER ---------------
09:39:27.353 ->
09:39:27.353 -> ets Jan 8 2013,rst cause:2, boot mode:(3,6)
09:39:27.353 ->
09:39:27.353 -> load 0x4010f000, len 3584, room 16
09:39:27.353 -> tail 0
09:39:27.353 -> chksum 0xb0
09:39:27.353 -> csum 0xb0
09:39:27.353 -> v2843a5ac
09:39:27.353 -> ~ld
09:39:32.191 -> 10 90 00 00 04 EC CA
09:39:32.191 -> 10 90 00 00 04 EC CA
09:39:34.216 -> --------------- CUT HERE FOR EXCEPTION DECODER ---------------
09:39:34.216 ->
09:39:34.216 -> Exception (28):
09:39:34.216 -> epc1=0x4020e25b epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000000 depc=0x00000000
09:39:34.216 ->
09:39:34.216 -> >>>stack>>>
09:39:34.216 ->
09:39:34.216 -> ctx: cont
09:39:34.216 -> sp: 3ffffd60 end: 3fffffc0 offset: 0190
09:39:34.216 -> 3ffffef0: 0000010a 3ffef608 3ffef5a0 4020e295
09:39:34.264 -> 3fffff00: 00000003 00001ace 00000001 00000001
09:39:34.264 -> 3fffff10: 40201064 3ffefeac 402129f4 00000003
09:39:34.264 -> 3fffff20: 3ffef538 3ffef608 3ffef5a0 40202327
09:39:34.264 -> 3fffff30: 00000000 00000001 45604189 00000000
09:39:34.264 -> 3fffff40: 40201064 00000000 402129f4 402129dc
09:39:34.264 -> 3fffff50: 40201064 00000000 402129f4 402129dc
09:39:34.264 -> 3fffff60: 00000000 00000100 00000003 40210100
09:39:34.264 -> 3fffff70: 0000000b fe007465 40212a08 402011c4
09:39:34.264 -> 3fffff80: 3fff0b00 00000000 00000002 3ffefe4c
09:39:34.264 -> 3fffff90: 00000001 3ffef6f0 3ffef6f8 4020241e
09:39:34.264 -> 3fffffa0: 3fffdad0 00000000 3ffefe0c 40210ec0
09:39:34.310 -> 3fffffb0: feefeffe feefeffe 3ffe850c 401014b5
09:39:34.310 -> <<<stack<<<
09:39:34.310 ->
09:39:34.310 -> --------------- CUT HERE FOR EXCEPTION DECODER ---------------
09:39:34.310 ->
09:39:34.310 -> ets Jan 8 2013,rst cause:2, boot mode:(3,6)
09:39:34.310 ->
09:39:34.310 -> load 0x4010f000, len 3584, room 16
09:39:34.310 -> tail 0
09:39:34.310 -> chksum 0xb0
09:39:34.310 -> csum 0xb0
09:39:34.310 -> v2843a5ac
09:39:34.310 -> ~ld
09:39:39.299 -> 10 90 00 00 04 EC CA
09:39:39.299 -> 10 90 00 00 04 EC CA
09:39:41.323 -> --------------- CUT HERE FOR EXCEPTION DECODER ---------------
09:39:41.323 ->
09:39:41.323 -> Exception (28):
09:39:41.323 -> epc1=0x4020e25b epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000000 depc=0x00000000
09:39:41.323 ->
09:39:41.323 -> >>>stack>>>
09:39:41.323 ->
09:39:41.323 -> ctx: cont
09:39:41.323 -> sp: 3ffffd60 end: 3fffffc0 offset: 0190
09:39:41.323 -> 3ffffef0: 0000010a 3ffef608 3ffef5a0 4020e295
09:39:41.323 -> 3fffff00: 00000003 00001b55 00000001 00000001
09:39:41.369 -> 3fffff10: 40201064 3ffefeac 402129f4 00000003
09:39:41.369 -> 3fffff20: 3ffef538 3ffef608 3ffef5a0 40202327
09:39:41.369 -> 3fffff30: 00000000 00000001 7e353f7c 00000000
09:39:41.369 -> 3fffff40: 40201064 00000000 402129f4 402129dc
09:39:41.369 -> 3fffff50: 40201064 00000000 402129f4 402129dc
09:39:41.369 -> 3fffff60: 00000000 00000100 00000003 40210100
09:39:41.369 -> 3fffff70: 0000000b fe007465 40212a08 402011c4
09:39:41.369 -> 3fffff80: 3fff0b00 00000001 00000002 3ffefe4c
09:39:41.369 -> 3fffff90: 00000001 3ffef6f0 3ffef6f8 4020241e
09:39:41.369 -> 3fffffa0: 3fffdad0 00000000 3ffefe0c 40210ec0
09:39:41.416 -> 3fffffb0: feefeffe feefeffe 3ffe850c 401014b5
09:39:41.416 -> <<<stack<<<
09:39:41.416 ->
09:39:41.416 -> --------------- CUT HERE FOR EXCEPTION DECODER ---------------
09:39:41.416 ->
09:39:41.416 -> ets Jan 8 2013,rst cause:2, boot mode:(3,6)
09:39:41.416 ->
09:39:41.416 -> load 0x4010f000, len 3584, room 16
09:39:41.416 -> tail 0
09:39:41.416 -> chksum 0xb0
09:39:41.416 -> csum 0xb0
09:39:41.416 -> v2843a5ac
09:39:41.416 -> ~ld
09:39:46.365 -> 10 90 00 00 04 EC CA
09:39:46.365 -> 10 90 00 00 04 EC CA
09:39:48.349 -> --------------- CUT HERE FOR EXCEPTION DECODER ---------------
09:39:48.395 ->
09:39:48.395 -> Exception (28):
09:39:48.395 -> epc1=0x4020e25b epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000000 depc=0x00000000
09:39:48.395 ->
09:39:48.395 -> >>>stack>>>
09:39:48.395 ->
09:39:48.395 -> ctx: cont
09:39:48.395 -> sp: 3ffffd60 end: 3fffffc0 offset: 0190
09:39:48.395 -> 3ffffef0: 0000010a 3ffef608 3ffef5a0 4020e295
09:39:48.395 -> 3fffff00: 00000003 00001b1f 00000001 00000001
09:39:48.395 -> 3fffff10: 40201064 3ffefeac 402129f4 00000003
09:39:48.395 -> 3fffff20: 3ffef538 3ffef608 3ffef5a0 40202327
09:39:48.395 -> 3fffff30: 00000000 00000001 4624dd2f 00000000
09:39:48.395 -> 3fffff40: 40201064 00000000 402129f4 402129dc
09:39:48.395 -> 3fffff50: 40201064 00000000 402129f4 402129dc
09:39:48.442 -> 3fffff60: 00000000 00000100 00000003 40210100
09:39:48.442 -> 3fffff70: 0000000b fe007465 40212a08 402011c4
09:39:48.442 -> 3fffff80: 3fff0b00 00000000 00000002 3ffefe4c
09:39:48.442 -> 3fffff90: 00000001 3ffef6f0 3ffef6f8 4020241e
09:39:48.442 -> 3fffffa0: 3fffdad0 00000000 3ffefe0c 40210ec0
09:39:48.442 -> 3fffffb0: feefeffe feefeffe 3ffe850c 401014b5
09:39:48.442 -> <<<stack<<<
09:39:48.442 ->
09:39:48.442 -> --------------- CUT HERE FOR EXCEPTION DECODER ---------------
09:39:48.489 ->
09:39:48.489 -> ets Jan 8 2013,rst cause:2, boot mode:(3,6)
09:39:48.489 ->
09:39:48.489 -> load 0x4010f000, len 3584, room 16
09:39:48.489 -> tail 0
09:39:48.489 -> chksum 0xb0
09:39:48.489 -> csum 0xb0
09:39:48.489 -> v2843a5ac
09:39:48.489 -> ~ld

@emelianov
Copy link
Owner

Can you provide complete code as it hard to say what is going wrong.

As for lost packets. I suspect that you are using RS485 modules with auto RX/TX switching. With such modules a have in my lab I was unable to get baud rate above 9600 form ESP side. From other hand the same module was working at least at 115200. So I suggest to replace RS485 transceiver.

@rzylius
Copy link
Author

rzylius commented Mar 31, 2021

I use max485 with rx/tx control pin. I tested several IC's, the resuls does not seem to differ. I will post the code. very much appreciate your help

@rzylius
Copy link
Author

rzylius commented Mar 31, 2021

Dear @emelianov ,

this is my code:

#define SLAVE_ID 1
const char* ssid = "************";
const char* password = "*******************";
#define HOST_NAME "ESP IMEON bridge"
#define ID_DEVICE 1
#define ID_GW 5
int16_t r26 = 0;
int16_t r50 = 0;
float countSuccess = 0;
float countError = 0;
//Used Pins
const int getPIN = D5; 
const int setPIN = D6;


//////// Libraries
#if defined ESP8266 // Includes of ESP8266
  #include <ESP8266WiFi.h>
    #ifdef USE_MDNS
    #include <DNSServer.h>
    #include <ESP8266mDNS.h>
  #endif
#elif defined ESP32 // Includes of ESP32
  #include <WiFi.h>
  #ifdef USE_MDNS
    #include <DNSServer.h>
    #include "ESPmDNS.h"
  #endif
#endif // ESP


// Time
uint32_t mLastTime = 0;
uint32_t mTimeSeconds = 0;
#include <ModbusRTU.h>
#include <ModbusIP_ESP8266.h>

//SOFTWARE SERIAL
#include "SoftwareSerial.h"
SoftwareSerial swSerial;

//Modbus objects
ModbusIP mb;
ModbusRTU mb_device;
ModbusRTU mb_gw;

void  getLED () {
  digitalWrite(getPIN, HIGH);
  delay(4);
  digitalWrite(getPIN, LOW);
}

void  setLED () {
  digitalWrite(setPIN, HIGH);
  delay(4);
  digitalWrite(setPIN, LOW);
}

uint16_t cbHregGet(TRegister* reg, uint16_t val) {
  getLED();
  return val;
}

uint16_t cbHregSet(TRegister* reg, uint16_t val) {
  setLED();
  return val;
}

bool cbConn(IPAddress ip) {
  Serial.print("connection from ");
  Serial.println(ip);
  return true; // Return 'true' to allow connection or 'false' to drop connection
}

bool cb(Modbus::ResultCode event, uint16_t transactionId, void* data) { // Callback to monitor errors
  switch (event) {
      case 0x00:
        countSuccess++;
        Serial.println("Transaction success");
        break;
      case 0x01:
        countError++;
        Serial.println("Transaction error: EX_ILLEGAL_FUNCTION, Function Code not Supported");
        break;
      case 0xE1:
        countError++;
        Serial.println("Transaction error: EX_GENERAL_FAILURE, Unexpected master error");
        break;
      case 0xE2:
        countError++;
        Serial.println("Transaction error: EX_DATA_MISMACH, Inpud data size mismach");
        break;
      case 0xE3:
        countError++;
        Serial.println("Transaction error: EX_UNEXPECTED_RESPONSE, Returned result doesn't mach transaction");
        break;
      case 0xE4:
        countError++;
        Serial.println("Transaction error: EX_TIMEOUT, Operation not finished within reasonable time");
        break;        
      default:
        countError++;
        Serial.print("Error while pulling from slave: ");
        Serial.println(event, HEX);
        break;
  }
  return true;
}

void readSlave(int16_t FIRST_REG, int16_t REG_COUNT) {
  Serial.print("Read slave, Hreg: 0x");
  Serial.println(FIRST_REG, HEX);
  if (!mb_device.slave()) {    // Check if no transaction in progress
    mb_device.pullHreg(SLAVE_ID, FIRST_REG, FIRST_REG, REG_COUNT, cb); 
    while(mb_device.slave()) { // Check if transaction is active
      mb_device.task();
      delay(10);
   }
 }
}

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);     
  pinMode(setPIN, OUTPUT);     
  pinMode(getPIN, OUTPUT);     
 
  // WiFi connection
    WiFi.begin(ssid, password);
    // Wait for connection
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
    }
  // Register host name in WiFi and mDNS
    String hostNameWifi = HOST_NAME;
    hostNameWifi.concat(".local");
    #ifdef ESP8266 // Only for it
        WiFi.hostname(hostNameWifi);
    #endif

Serial.begin(115200); 

//setup modbus RTU
  swSerial.begin(9600, SWSERIAL_8N1, D1, D2, false);
  mb_device.begin(&swSerial, D0); 
  
  mb_device.master();
//setup modbus TCP
  mb.server();
  mb.onConnect(cbConn); // Add callback on connection event

// READ ONLY IMEON REGISTERS
  mb.addHreg(0x0100, 0, 30);
  mb.addHreg(0x0200, 0, 21);
  mb.addHreg(0x0300, 0, 4);
  mb.addHreg(0x0400, 0, 16);
  mb.addHreg(0x0500, 0, 6);
  mb.addHreg(0x0600, 0, 3);

  mb.onGetHreg(0x0100, cbHregGet, 1500);
  mb.onSetHreg(0x0100, cbHregSet, 1500);
}
 
void loop() {
    // Each second
    if ((millis() - mLastTime) >= 1000) {
        Serial.println("");
        Serial.print("Transaction status: ");
        Serial.println(countError / (countError + countSuccess));
        mLastTime = millis();
        mTimeSeconds++;
        digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
        switch (mTimeSeconds % 10) {
          case 1:
            readSlave (0x0100, 11);
            break;
          case 2:
            readSlave (0x0300, 4);
            break;
          case 3:
            readSlave (0x0400, 4);
            break;
          case 4:
            readSlave (0x040A, 6);
            break;
          case 5:
            readSlave (0x0503, 6);
            break;
          case 6:
            readSlave (0x0600, 3);
            break;
          case 7:
            readSlave (0x0117, 7);
            break;
          case 8:
            readSlave (0x1000, 1);
            break;
          case 9:
            break;
          case 0:
            break;
        }
     }
    mb.task();
    mb_device.task();
    yield();
}

@emelianov
Copy link
Owner

Hello,
Can see nothing critically wrong with the code.
Suggestions are:

  1. to use HardwareSerial for Modbus.
  2. It's just small improvements
void readSlave(int16_t FIRST_REG, int16_t REG_COUNT) {
  Serial.print("Read slave, Hreg: 0x");
  Serial.println(FIRST_REG, HEX);
  if (!mb_device.slave()) {    // Check if no transaction in progress
    mb_device.pullHreg(SLAVE_ID, FIRST_REG, FIRST_REG, REG_COUNT, cb); 
    while(mb_device.slave()) { // Check if transaction is active
      mb_device.task();
      mb.task(); // Should process incoming requests if long time response of timeout on mb_device happens
      delay(10);
   }
 }
}
  1. Just random thought: mb.onGetHreg(0x0100, cbHregGet, 1500); It's a bit wild but working way to add callbacks to all already defined registers ;).

@DOSpt
Copy link

DOSpt commented Apr 18, 2021

Hi @rzylius

Do you have a terminator resistor (typically 120ohm)?
If not you might be having problems with the physical signal that would explain why at a different baud rate the results are different. Have you measured the signal with an oscilloscope just to have a better understanding of what is happening? I wouldn't look at the code before making sure that the signal integrity is OK. Anyway, make sure you have an 120ohm resistor in parallel with the differential signals on the master side.

Other thing that might be causing this is if there is a delay on the pins that control the direction of the transceiver. If there is to much capacitance on this pin there is the risk that it can be slow enough that when one of the sides starts transmitting it is still in the wrong mode. Again, the best thing to do is to measure the signals to be sure.

@rzylius
Copy link
Author

rzylius commented Apr 19, 2021

Hi @DOSpt ,

thanks for the ideas. Indeed, I had max485 module connected directly without any resistors etc.

Now I used https://www.anodas.lt/en/max485-ttl-to-rs485-converter-module module, which improved the error rate. I hoped that would kill all the problems you advised on - resistor, capacitance, etc. Still, I get about ~20-25% errors consistently.

I will try to do measurements with oscilloscope. (well, and I guess I will come back with my measurements :)

@emelianov
Copy link
Owner

why at a different baud rate the results are different.

Prtobably because of SoftwareSerial. Serial data output formed by software is not so stable-in-time as hardware one.

Other thing that might be causing this is if there is a delay on the pins that control the direction of the transceiver. If there is to much capacitance on this pin there is the risk that it can be slow enough that when one of the sides starts transmitting it is still in the wrong mode. Again, the best thing to do is to measure the signals to be sure.

It should not be a problem as according to Modbus specification remode must send responce with delay that long enough to eliminate electrical processes affects.

@DOSpt
Copy link

DOSpt commented Apr 19, 2021

Hi @rzylius

@emelianov might be right on the software serial causing problems. Even if you are already using serial0 and serial2 there is an extra serial hardware module on the ESP32 that is a bit hidden that can be used (in the default pins it is unusable as by default it shares pins with the SPI Flash on the module). You can still use the serial1 (UART1) module for this. All pins in the ESP32 are remapable so this is not a problem. In order to use it just to the following:

Serial1.begin(9600, SERIAL_8N1, D1, D2);
mb_device.begin(&Serial1, D0); 

@rzylius
Copy link
Author

rzylius commented Apr 25, 2021

@DOSpt , thanks for ideas.

I tried to use Arduino MEGA (to have all the hw serials I need), but have no visible influence. I do receive error rate of ~30%.
Will try to look with osciloscope.

@emelianov
Copy link
Owner

@rzylius you'd better use some kind of logical analizer as it'll show dataflow that is easier to analize.

@rzylius
Copy link
Author

rzylius commented May 4, 2021

Dear @emelianov ,

sorry for the outside question. To prepare myself for recording transactions with logic analyzer, I set up my test case. I wanted to be sure that I can understand what I see (I did not find much of tuitorials on Modbus - logic analysis on youtube) :

  • I use Arduino MEGA which I tested for connection with Inverter, with the same settings. 9600 8N1
  • I connected modbus link to USB-rs485 device to my widows machine and run on windows slave emulator
  • Connect salaeae 8 port logic analyzer

Via MEGA serial port I see that mega successfully querries my windows slave device and receives right answers.

But here are my screens from logic analyzer:

Channel 3 is Modbus A connection, configures as modbus master
Channel 1 is Modbus B inverse, configures as modbus slave

image

There are two communication spikes, I gather this is request and response.

So the request part:
image

Channel 3 (A) gives correct interpretation, it is reading holding registers starting with 0x1000.

Now what supposed to be response:
image

What is wrong here? As I worte earlier, actually MEGA receives correct response from the slave, so I expected to see slave response all right, so analyzer software is just misinterpreting what it reads?

@emelianov
Copy link
Owner

I'm not sure that correctly understanding analyser wiring.
If Channel1 D0 is RE_DE pin it doesn't looks correct.

@rzylius
Copy link
Author

rzylius commented May 11, 2021

@emelianov , no, sorry I am getting to know what to measure. Will post updated screenshots, which makes more sense :)

@derjohn
Copy link

derjohn commented Aug 9, 2022

@rzylius Have you been able to read the metrics of the imeon? I own a 9.12 and want to read the modbus registers .... but I've not been able to find any docs about the registers meaning. Do you have?

@rzylius
Copy link
Author

rzylius commented Aug 9, 2022

Hi, yes I do. It has "restricted distribution" written all over, so I guess I can not place it plainly on the web.

IMEON provided it to me on request. By the way, reading is without any security, but if you want to write values, you will need unique key, which you can get only from IMEON.

Drop me an email at [email protected] I can send the doc personally. And I we have one more friend with whom we discuss IMEON things, that will make three of us. Might make sense to make some kind of a group.

@matze-sr
Copy link

Hi @rzylius,
I now own the imeon 9.12 as well. Can you provide me the docs too? Did you opened a case at Imeon to get them? Can you tell me the wiring for the RJ45 modbus jacket on the Imeon? Is it Pin 1 -> A | Pin 2 -> B | Pin 8 -> GND like it is for the Smartmeter? Thanks in advance!

@rzylius
Copy link
Author

rzylius commented May 18, 2023 via email

@rzylius
Copy link
Author

rzylius commented Nov 6, 2024

Hi,

after some time I came back to the modbusRTU implementation. A reminder the HW I want to connect to:

The IMEON RS485 port is configured to use the following

Baud rate : 9600
Data bits per Byte : 8
Parity : None
Stop bits : 1
Encoding: big endian(MSB LSB: value 0x
CRC: little endian (LSB MSB: value 0x3412 is 0x1234)
Slave address: 0x01
Data message format: Hexadecimal
The instant read values in the database are updated each 10 seconds.
The set values are updated each 1 minute in database.
Response timeout is 10 seconds.

I got connected and all works pretty ok out of the box, but I consistently get 20-30% of timeouts. I tried to work with timeouts and as you advised, but no significant improvement.

so I settled on the notions, that this is some IMEON fault.

But now I built test code with different library, and it works with 1 error out of 9000 requests. As I myself have no deep technical knowledge, maybe this would provide hint into the peculiar problem I encounter:

this is a code which works without problems:

`
#include <ModbusMaster.h>

ModbusMaster node;

const uint8_t slaveId = 1; // ModbusRTU Slave ID
const uint16_t startAddress = 768; // Start address for the registers
const uint8_t numRegisters = 8; // Number of registers to read

unsigned long lastRequestTime = 0;
unsigned long errors = 0;
unsigned long corrects = 0;
unsigned long maxResponseTime = 0; // To track the max response time of correct responses
const unsigned long requestInterval = 3000; // Query every 3 seconds

const unsigned long customTimeout = 2000; // Custom timeout in milliseconds
unsigned long requestStartTime = 0; // To store the start time of the request

void setup() {
Serial.begin(115200);

// Initialize ModbusRTU communication on Serial2 at 9600 baud, 8N1
Serial2.begin(9600, SERIAL_8N1, 16, 17); // RX = GPIO16, TX = GPIO17 for ESP32
node.begin(slaveId, Serial2);

Serial.println("[Setup] ModbusRTU Master initialized. Querying slave for registers...");
}

void loop() {
// Check if it's time to send a new request
if (millis() - lastRequestTime >= requestInterval) {
lastRequestTime = millis();

// Record the start time before sending the request
requestStartTime = millis();

Serial.println("[ModbusRTU] Sending read request...");

// Send a read request for holding registers starting at address 768 for 8 registers
uint8_t result = node.readHoldingRegisters(startAddress, numRegisters);

// Calculate the response time
unsigned long responseTime = millis() - requestStartTime;

if (result == node.ku8MBSuccess) {
  // Successful response
  Serial.printf("[ModbusRTU] Successfully retrieved register values (Response time: %lu ms):\n", responseTime);
  corrects += 1;

  // Update max response time if this response took longer than the previous max
  if (responseTime > maxResponseTime) {
    maxResponseTime = responseTime;
  }

  // Loop through and print each register value
  for (uint8_t i = 0; i < numRegisters; i++) {
    Serial.printf("Register %d: %d\n", startAddress + i, node.getResponseBuffer(i));
  }

} else {
  // Handle errors or timeouts
  Serial.printf("[ModbusRTU] Error code: %02X (Response time: %lu ms)\n", result, responseTime);
  errors += 1;

  // Check if the response time exceeded our custom timeout
  if (responseTime > customTimeout) {
    Serial.println("[ModbusRTU] Warning: Delayed response received after custom timeout");
  }
}

// Calculate the error rate percentage safely
float errorRate = corrects > 0 ? (errors / (float)(corrects + errors)) * 100.0 : 0.0;

Serial.printf("Corrects: %lu, Errors: %lu, Error Rate: %.2f%%, Max Response Time: %lu ms\n", corrects, errors, errorRate, maxResponseTime);

}
}`

@rzylius
Copy link
Author

rzylius commented Nov 24, 2024

one more thing, in ModbusMaster.h I changed timeout parameter to 10000 to reflect IMEON timeout of 10sec

static const uint16_t ku16MBResponseTimeout = 2000; ///< Modbus timeout [milliseconds]

@derjohn
Copy link

derjohn commented Nov 24, 2024

# arp -n|grep 192.168.9
192.168.9.12     0x1         0x2         6c:ec:eb:a2:cc:f3     *        phy0-sta0
192.168.9.2      0x1         0x2         d8:b0:4c:d1:86:4a     *        phy0-sta0  <== Modbus

Looking up d8:b0:4c:d1:86:4a you will find Jinan USR IOT Technology Co., Ltd. It's probably this product inside the Imeon 9.12: https://en.usr.cn/Industrial-Serial-RS485-to-Ethernet-modbus-rtu-Converter.html

On the Webpage there is a table with an overview about portspeed etc. (Dunno, if that helps, though)

@rzylius
Copy link
Author

rzylius commented Nov 25, 2024

Hi, I did some more testing.

`#include <ModbusRTU.h>

// Instantiate ModbusRTU object
ModbusRTU mb;

// Define Modbus parameters
const uint8_t SLAVE_ID = 1; // ID of the Modbus slave device
const uint16_t START_ADDRESS = 768; // Starting address of the registers to read
const uint8_t NUM_REGISTERS = 8; // Number of registers to read

// Buffer to store register values
uint16_t regBuffer[NUM_REGISTERS];

// Timing variables
unsigned long requestStartTime = 0;
unsigned long responseTime = 0;
unsigned long timeoutDuration = 0;

unsigned long maxResponseTime = 0;
unsigned long maxTimeoutDuration = 0;

// Timing for queries
const unsigned long QUERY_INTERVAL = 1000; //
unsigned long lastQueryTime = 0;

// Counters for successful and error reads
unsigned long successCount = 0;
unsigned long errorCount = 0;

// Flag to indicate if a query is pending
bool queryPending = false;

// Callback function when a response is received
bool cb(Modbus::ResultCode event, uint16_t transactionId, void* data) {
unsigned long currentTime = millis();
Serial.print("CB: ");

if (event == Modbus::EX_SUCCESS) {
successCount++; // Increment success counter
responseTime = currentTime - requestStartTime;
Serial.print("Successful Response Time: ");
Serial.print(responseTime);
Serial.print(" ms");

// Update maximum response time
if (responseTime > maxResponseTime) {
  maxResponseTime = responseTime;
  Serial.print(" New Max Response Time: ");
  Serial.print(maxResponseTime);
  Serial.print(" ms");
}
Serial.println("");

// Optionally, process the received data
Serial.print("Received values from slave ");
Serial.print(SLAVE_ID);
Serial.print(" registers ");
Serial.print(START_ADDRESS);
Serial.print(" to ");
Serial.print(START_ADDRESS + NUM_REGISTERS - 1);
for (int i = 0; i < NUM_REGISTERS; i++) {
  Serial.print(": ");
  Serial.print(regBuffer[i]);
}
Serial.println("");

}
else {
errorCount++; // Increment error counter

if (event == Modbus::EX_TIMEOUT) {
  timeoutDuration = currentTime - requestStartTime;
  Serial.print("Request TIMEOUT after: ");
  Serial.print(timeoutDuration);
  Serial.println(" ms");
  
  // Update maximum timeout duration
  if (timeoutDuration > maxTimeoutDuration) {
    maxTimeoutDuration = timeoutDuration;
    Serial.print(" New Max Timeout Duration: ");
    Serial.print(maxTimeoutDuration);
    Serial.println(" ms");
  }
}
else {
  Serial.print("Modbus Exception: ");
  Serial.println(event);
}

}

// Update lastQueryTime and reset queryPending flag
lastQueryTime = currentTime;
queryPending = false;

return true;
}

void setup() {
// Initialize Serial for debugging (Monitor at 115200 baud)
Serial.begin(115200);

Serial2.begin(9600, SERIAL_8N1, 16, 17, 18, -1);

// Initialize ModbusRTU object as Master
mb.begin(&Serial2);
mb.master();

// Wait a moment before starting communication
delay(1000);
}

void loop() {
unsigned long currentTime = millis();

// Check if it's time to send a new query and no query is pending
if (!queryPending && (currentTime - lastQueryTime >= QUERY_INTERVAL)) {

// Record the time when the request is sent
requestStartTime = currentTime;

// Send request to read holding registers
mb.readHreg(SLAVE_ID, START_ADDRESS, regBuffer, NUM_REGISTERS, cb);

// Set the query as pending
queryPending = true;

}

mb.task();

// Optionally, print maximum times and error statistics periodically
// For example, every minute
static unsigned long lastMaxPrintTime = 0;
if (currentTime - lastMaxPrintTime >= 60000) { // 60 seconds
lastMaxPrintTime = currentTime;
Serial.println("===== Maximum Times and Error Statistics =====");
Serial.print("Max Response Time: ");
Serial.print(maxResponseTime);
Serial.println(" ms");
Serial.print("Max Timeout Duration: ");
Serial.print(maxTimeoutDuration);
Serial.println(" ms");

// Print counters
Serial.print("Successful Reads: ");
Serial.println(successCount);
Serial.print("Error Reads: ");
Serial.println(errorCount);

// Calculate and print error rate
unsigned long totalReads = successCount + errorCount;
float errorRate = (totalReads > 0) ? ((float)errorCount / totalReads) * 100.0 : 0.0;
Serial.print("Error Rate: ");
Serial.print(errorRate, 2); // Two decimal places
Serial.println(" %");

Serial.println("==============================================");

// Optional: Reset counters if you want to monitor per-minute statistics
// successCount = 0;
// errorCount = 0;
// maxResponseTime = 0;
// maxTimeoutDuration = 0;

}
}
`
I increased in ModbusSettings.h RTU timeout to 10000. It seems, that one problem was that real timeout still was 2 sec previously.

Now success rate of reads is 78%, which is not bad.

But what I observe (I use autoflow rs485-ttl module), that when timeout occurs, actually RX led blinks. that is IMEON responds, but module fails to read it? I have in callback a condition for exception - which never works, module just waits for timeout.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants