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

ESP32: Reading float type from Modbus #155

Closed
GabrielLAPU opened this issue Oct 1, 2021 · 18 comments
Closed

ESP32: Reading float type from Modbus #155

GabrielLAPU opened this issue Oct 1, 2021 · 18 comments

Comments

@GabrielLAPU
Copy link

Good morning everyone,

I am using the library the Emelianov Modbus library in an ESP32 thing module with the Arduino IDE to read the values ​​of a sensor by Modbus.
The sensor sends 5 records with the following configuration:

Register address: 1 data type 32b floating point (big endian)
Register address: 2 data type 32b floating point (big endian)
Register address: 3 data type 16b unsigned
Register address: 4 data type 16b unsigned
Register address: 5 data type 16b unsigned

Reading unsigned 16b type registers, I haven't any problem but, I have problems with the 32b floating point values, which I cannot see reasonable values.
I am testing the following code that I saw in another forum:

uint32_t value32;
mb.readHreg (remote, 1, (uint16_t *) & value32, 2,0,1); // read two sequental registers to 32-bit variable
value33 = (value32 >> 16) | (value32 << 16);
Serial.print ("Value32:"); Serial.println (value32, BIN);
Serial.print ("Value33:"); Serial.println (value33, BIN);

But I do not get a reasonable temperature value than expected (around to 24.1ºC)
These are the values ​​rescued from the serial port:
Value32: 11011001000001100100000110111111
Value33: 1000001101111111101100100000110

Could someone tell me what may be going on?

Thanks and best regards.

@emelianov
Copy link
Owner

Hello,

Probably that's because of sensor documentation uses 1-based offset for registers numbering as library uses 0-based one.
Try to read starting from hreg(0) :mb.readHreg (remote, 0, (uint16_t *) & value32, 2, nullptr, 1);

@GabrielLAPU
Copy link
Author

Hello.
Thanks for your reply.
I have tried your suggestion but the value read in this case is 0. Indeed I must read from register 1, since the rest of the values of position 3, 4 and 5 match me correctly.
Checking the values that it provides me by executing the print commands:
mb.readHreg(remote, 1, (uint16_t*)&value32, 2,0,1); // read two sequental registers to 32-bit variable
value33=(value32>>16) | (value32<<16);
Serial.print("Value32_BIN:");Serial.println(value32,BIN);
Serial.print("Value33_BIN:");Serial.println(value33,BIN);
Serial.print("Value32_BIN:");Serial.println(value32);
Serial.print("Value33_BIN:");Serial.println(value33);

Value32_BIN:11100000100011000100000110011100
Value33_BIN:1000001100111001110000010001100
Value32_BIN:3767288220
Value33_BIN:1100800140
Value32_BIN:11100000100011000100000110011100
Value33_BIN:1000001100111001110000010001100
Value32_BIN:3767288220
Value33_BIN:1100800140
Value32_BIN:11010100110100010100000110011101
Value33_BIN:1000001100111011101010011010001
Value32_BIN:3570483613
Value33_BIN:1100862673

Values have 31 bits instead of 32 when I apply swap words order

Do you know why this can happen?

@emelianov
Copy link
Owner

You need also swap bytes in words:

union sw {
  float f;
  uint32_t u32;
  struct {
    uint16_t u161;
    uint16_t u162;
  };
};

  v32.u32 = 0b11010100110100010100000110011101; // Original
  v32.u32=(v32.u32>>16) | (v32.u32<<16);
  v32.u161 = (v32.u161>>8) | (v32.u161<<8);
  v32.u162 = (v32.u162>>8) | (v32.u162<<8);
  Serial.println(v32.f);// swap (8<=>8)<=>(8<=8>) ===> 19.73

@GabrielLAPU
Copy link
Author

@emelianov I have checked your proposed code and it is not working properly, in fact, I have tried the following code to see if the swap is doing it correctly for me, the tested code is this:

`union sw{
float f;
uint32_t u32;
struct{
uint16_t u161;
uint16_t u162;
};
};
sw value32 ;
void loop()
{
value32.u32 = 0b11010100110100010100000110011101; // Original
value32.u32=(value32.u32>>16) | (value32.u32<<16);
value32.u161 = (value32.u161>>8) | (value32.u161<<8);
value32.u162 = (value32.u162>>8) | (value32.u162<<8);

Serial.println(value32.u32);// swap (8<=>8)<=>(8<=8>) ===> 19.73
Serial.println(value32.f);

`
But the serial print is showing the follow results:

2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00
2638336468
-0.00

I hope see in the float values 19.73 ¿Is correct?
Thanks in advance.

@emelianov
Copy link
Owner

Ops... Probably i've lost in swaps. Choose appropriate one:

union sw {
  float f;
  uint32_t u32;
  struct {
    uint16_t u161;
    uint16_t u162;
  };
};

  sw v32;
  v32.u32 = 0b11010100110100010100000110011101;
  Serial.println(v32.f); //orig
  v32.u32=(v32.u32>>16) | (v32.u32<<16);
  Serial.println(v32.f); // swap 16<=>16
  v32.u161 = (v32.u161>>8) | (v32.u161<<8);
  v32.u162 = (v32.u162>>8) | (v32.u162<<8);
  Serial.println(v32.f);// swap (8<=>8)<=>(8<=8>)
  v32.u32 = 0b11010100110100010100000110011101;
  v32.u161 = (v32.u161>>8) | (v32.u161<<8);
  v32.u162 = (v32.u162>>8) | (v32.u162<<8);
  Serial.println(v32.f);// swap (8<=>8)(8<=8>)
}

@GabrielLAPU
Copy link
Author

GabrielLAPU commented Oct 4, 2021

I have seen the following, it seems that the problem in those variables had it in the consecutive reading of registers. I mean, in the read loop I was reading several registers without any time control, in these float registers, I think some data was lost in the communication.
Finally I implemented a swap like the first ones proposed and including some delays, The application is working correctly.
This is the final code:

uint16_t V1;
uint16_t V2;
uint16_t V3;
float V4_float;
float V5_float;
union sw{
  float f;
  uint32_t u32;
  struct{
    uint16_t u161;
    uint16_t u162;
  };
};
sw V4 ;
sw V5;
void loop()
{
if (mb.isConnected(remote)) {   
mb.readHreg(remote, 1, (uint16_t*)&V5.u32, 2,nullptr,1); // read two sequental registers to 32-bit variable
V5.u32=(V5.u32>>16) | (V5.u32<<16); // swap 16<=>16
Serial.print("Value V5: ");Serial.println(V5.f); // swap 16<=>16
delay(200);
mb.readHreg(remote, 2, (uint16_t*)&V4.u32, 2,nullptr,1); // read two sequental registers to 32-bit variable
V4.u32=(V4.u32>>16) | (V4.u32<<16); Serial.println(V4.f); // swap 16<=>16
Serial.print("Value V4: ");    
delay(200);
mb.readHreg(remote,3,&V3,nullptr,1);
Serial.print("Valor V3: ");        Serial.println(V3); 
delay(200);
mb.readHreg(remote,4,&V2,nullptr,1);
Serial.print("Valor V2: ");      Serial.println(V2); 
delay(200);
mb.readHreg(remote,5,&V1,1,nullptr,1);
Serial.print("Valor V1: ");  Serial.println(V1); 
delay(200);
 } else {
      mb.connect(remote,502);           // Try to connect if no connection
      Serial.println("Conexión ModBus no establecida, Conectando....");
  }
      mb.task();                      // Common local Modbus task
      delay(1000);
}

Is my reasoning possible? If so, is it solved in the best way?
Thanks so much for your help.

@emelianov
Copy link
Owner

It should be more complex code. As a minimum for each read

uint16_t tr = mb.readHreg(remote,4,&V2,nullptr,1);
while (mb.isTransaction(tr))
  mb.task();
// At this point request is finished and value in V2 is set. If got response from server for sure.

@GabrielLAPU
Copy link
Author

@emelianov thank you for your support.

@GabrielLAPU
Copy link
Author

GabrielLAPU commented Dec 1, 2021

Hi @emelianov,

I had a problem using the function. I would like to do a multi-read of a sensor, this means that two or more ESP32s would read data from the same sensor. With what I have programmed, that reading is confusing reading non-logical data or even ovf. You have an example where you can see the code as it is implemented.

Now in each ESP32 I have the following programmed:

void setup ()
{
 Serial.begin (9600);
if (WiFi.status ()! = WL_CONNECTED) {
Serial.println ("No WiFi");
} else {
mb.client ();
mb.connect (remote, 502);
if (mb.isConnected (remote)) {// Check is the sensor is present
Serial.println ("Sensor OK");
} else {
Serial.println ("Modbus Error");
}
mb.disconnect (remote);
}
void loop ()
{
if (WiFi.status () == WL_CONNECTED) {
      mb.connect (remote, 502);
      // delay (200);
      if (mb.isConnected (remote)) {
          fcn_ModBusRead ();// read values from sensor
          mb.disconnect (remote);
       } else {
            ERROR = 202;
          }
      
  }
    mb.task ();
} 

Thanks in advance.
Regards

@GabrielLAPU GabrielLAPU reopened this Dec 1, 2021
@emelianov
Copy link
Owner

  • ModbusTCP allows multiple clients to access the same server. The only limitation on simultaneous connections (4 by default, can be increased up to 10 for ESP32).
  • As for your code snippet it's not good as mb.connect() will cause disconnect/connect each loop. You'd better to use way used in the library examples.
  • As for non-logical data if you mean datatypes outside uint16_t or bool types general ideas described here

@GabrielLAPU
Copy link
Author

GabrielLAPU commented Dec 16, 2021

Hi Emilianov, thank you for your answer and sorry for mi delayed response. Until today I can not read your answer.

I have changed my code as I have could see in the examples. I go to test if with this code, 4 differentes ESP32 can read to the same sensor.

Another problem which I have found with this code, is with the expresion "mb.isConnected(remote)", I hoped when I swicht off the sensor, this expression send me a "0" if this failure occur 2 times, I must send the ERROR by I2C to another system, but this expression sent to me always "1". Do I checking correctly that the connection is running? If not, How I can check it correctly?

Thanks in advance.
`
void fcn_ModBusRead (){
int32_t Dato1 = mb.readHreg(remote, 1, (uint16_t*)&Temp.u32, 2,nullptr,1);
while(mb.isTransaction(Dato1)){
mb.task();
yield();
}
Temp.u32=(Temp.u32>>16) | (Temp.u32<<16);
int32_t Dato2 = mb.readHreg(remote, 2, (uint16_t*)&Hr.u32, 2,nullptr,1);
while(mb.isTransaction(Dato2)){
mb.task();
yield();
}
}
void setup()
{
Serial.begin(9600);
Wire.begin(I2C_SDA, I2C_SCL);
wifiManager.autoConnect();
if(WiFi.status()!=WL_CONNECTED){
ERROR=101;
} else{
mb.client();
mb.connect(remote,502);
}
}
void loop()
{
if (WiFi.status()== WL_CONNECTED){
if(mb.isConnected(remote)){
fcn_ModBusRead();
ContFallosMD = 1;
ERROR = 0;
}else {
if (ContFallosMD<=ContFallosMDLimite){
ContFallosMD++;
} else{
ERROR = 202;
Serial.println("Send ERROR by I2C");
}
}
} else{
ERROR = 102;
Serial.println("Send ERROR by I2C");
}

`

@emelianov
Copy link
Owner

In case if Modbus server becomes offline and stops responding connection becomes reported as alive on client side for a TCP timeout time. Moreover if no request to be sent over this connection timeout may starts counting only on first request attempt. That's how TCP is designed.

@GabrielLAPU
Copy link
Author

Hi Emelianov, I am reviwing the code according to the main issues found on it.

Attached you can found the changed code. Is correct in this code the use of the mb.connect()? if not, could you clarify to me the example model in the library?

I am trying use a callback for determine if the sensor is connected to the network or not. For it, I using a callback function and use the TIMEOUT event to determine that a problem exits with the sensor.
But if the sensor is connected, the value is not received correctly and I obtain a event number E1 Unexpected master error if I disconnect the sensor, I obtain correctly the E4 TIMEOUT ERROR. But if the sensor is connected I always obtain this error. Am I using correctly the callback?

The code:

bool cb(Modbus::ResultCode event, int16_t Dato1, void* data){
  if (event != Modbus::EX_SUCCESS)                  // If transaction got an error
        Serial.printf("Modbus result: %02X\n", event);  // Display Modbus error code
  if (event == Modbus::EX_TIMEOUT) {    // If Transaction timeout took place
      Serial.println("Modbus result: TIMEOUT");
     mb.disconnect(remote);              // Close connection to slave and
     mb.dropTransactions();              // Cancel all waiting transactions
  }
  return true;
}
void fcn_ModBusRead (){
         int32_t Dato1 = mb.readHreg(remote, 1, (uint16_t*)&Temp.u32, 2,cb);
         while(mb.isTransaction(Dato1)){
                   mb.task();
                   yield();
         }
         Temp.u32=(Temp.u32>>16) | (Temp.u32<<16);
          int32_t Dato2 = mb.readHreg(remote, 2, (uint16_t*)&Hr.u32, 2,nullptr,1);
          while(mb.isTransaction(Dato2)){
                  mb.task();
                   yield();
           }
}
void setup() {
         Serial.begin(9600);
         Wire.begin(I2C_SDA, I2C_SCL);
         wifiManager.autoConnect();
    
        if(WiFi.status()!=WL_CONNECTED){
            ERROR=101;
       } else{
            mb.client();
            mb.connect(remote,502);
     }
}
void loop() {
         if (WiFi.status()== WL_CONNECTED){
                if(mb.isConnected(remote)){
                fcn_ModBusRead();
               ContFallosMD = 1;
               ERROR = 0;
          }else {
               if (ContFallosMD<=ContFallosMDLimite){
                  ContFallosMD++;
               } else{
                  ERROR = 202;
                  Serial.println("Send ERROR by I2C");
                }
          }
        } else{
            ERROR = 102;
                 Serial.println("Send ERROR by I2C");
          }
}

Thanks in advance

@emelianov emelianov reopened this Feb 15, 2022
@emelianov
Copy link
Owner

Take a look comments below. Probably they will help with E4 errors.
As for E1 errors, they can appears only in case if response from Modbus server is wrong.

void setup() {
         Serial.begin(9600);
         Wire.begin(I2C_SDA, I2C_SCL);
         wifiManager.autoConnect();
         mb.client(); // Need to init Modbus as a client anyway
    
        if(WiFi.status()!=WL_CONNECTED){
            ERROR=101;
       } else{
            mb.connect(remote,502);
     }
}
void loop() {
         if (WiFi.status()== WL_CONNECTED){
                if(mb.isConnected(remote)){
                  fcn_ModBusRead();
                 ContFallosMD = 1;
                 ERROR = 0;
               } else {
                 mb.connect(remote, 502);  // Need to try to restore connection if not connected 
                 if (ContFallosMD<=ContFallosMDLimite){
                   ContFallosMD++;
                 } else {
                   ERROR = 202;
                   Serial.println("Send ERROR by I2C");
                 }
              }
        } else{
            ERROR = 102;
                 Serial.println("Send ERROR by I2C");
        }
       delay(50); // Need to reduce pooling rate as 'remote' device may fail on high rate
}

@GabrielLAPU
Copy link
Author

I am testing your changes and don't work properly. If the sensor is connected and then run the application the code works properly I can read the values and send ir by I2C, the problem is when the connexion is running and I switch off the sensor, I would hope that the expresion mb.isConnected(remote) would be false and would start to count and increasing the variable ContFallosMD, but the real situation is that expresion mb.isConnected(remote) is true although the sensor is desconnected.

@emelianov
Copy link
Owner

With the code above is expected that .isConnected() become false after any active Modbus transaction got timeout error. If no transaction in progress connection may be reported as connected indefinitely.

@GabrielLAPU
Copy link
Author

I don't understand the situation, If each cycle time I check the connection and if the .isConnected is false, a new transaction is required by mean the fcn_ModbusRead() and if the sensor is desconnected, the this new transaction get timeout error.
Where am I wrong in my reasoning? How I could detect that the sensor is off?

@emelianov
Copy link
Owner

Have no time to test with hardware but this minimalistic code commented with haw it should work.

bool cb(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\n", event);  // Display Modbus error code
  if (event == Modbus::EX_TIMEOUT) {    // If Transaction timeout took place
    mb.disconnect(remote);              // Close connection to slave and
    mb.dropTransactions();              // Cancel all waiting transactions
  }
  return true;
}

uint16_t res = 0;
void loop() {
    if (!mb.isConnected(remote)) {   // Check if connection to Modbus Slave is established
        mb.connect(remote);               // Try to connect if no connection. If no success 5 seconds timeout there
        Serial.print(".");
    }
    if (mb.isConnected(remote) { // Check if Modbus library thinks that remote is alive
      uint16_t tr = mb.readHreg(remote, REG, &res, 1, cb); // Initiate read request
      while (mb.isTransaction(tr) {  // While transaction is active wait for response data. If remote is dead 1 second timeout there
         mb.task(); // this fiction processes response frame, calls callback  with EX_SUCCESS and clears transaction
                           // or in case of timeout calls callback with EX_TIMEOUT
                           // in case of timeout function above forces remote disconnect and destroys other transactions if any.
         yield();
      }
      delay(50);
   } else {
     // Not connected state code is there.
   }
}

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

2 participants