Receive, gather and order data from an rs485 sensor

This is my code to connect and read data from a rs485 sensor. The sensor measure electroconductivity, temperature and salinity, is conected using an MAX485 to ttl converter.


image

attached connection protocol information,

The code establishes the connection and reads and stores the values in an array called 'values[20]'. then take the 3 variables according to their position and store in uint16_t variables like ec.
I think data comes in Big endian format with lenght of 4 bytes. i.e 41C3 wich is the input of ('tempe') temperature, it must be interpreted in DEC as 24375. but the arduino is interpreting the data as 16835 in DEC value.
I have read several posts and tried to convert the data to little endian using bitswaping and the union function but I have not been successful. I require the arduino to understand that the data is big endian float and display it as such. The current serial monitor is this:

Any help is appreciated

[code]
#include<ModbusMaster.h>
#include <SoftwareSerial.h>

#define RX_PIN 12
#define TX_PIN 13
#define MAX485_REDE_PIN 8

uint32_t values[20]; //store all answer from the sensor
uint16_t  ec; //electroconductivity from the sensor Reg addres 0x0000 and lenght: 4 bytes
uint16_t tempe;//temperature from sensor, Register addres: 0x0004
uint16_t salt;//salinity from sensor, Register addres: 0x0008
int16_t value;
SoftwareSerial modbusSerial(RX_PIN, TX_PIN);
ModbusMaster node; //object node for class ModbusMaster

void preTransmission() //Function for setting stste of Pins DE & RE of RS-485
{
  digitalWrite(MAX485_REDE_PIN, 1);
}
void postTransmission()
{
  digitalWrite(MAX485_REDE_PIN, 0);
}

void setup()
{
  pinMode(MAX485_REDE_PIN, OUTPUT);
  digitalWrite(MAX485_REDE_PIN, 0);
  Serial.begin(9600); //Baud Rate as 9600
  modbusSerial.begin(9600); //Baud Rate as 9600
  node.begin(4, modbusSerial); //Slave ID as 4 ec sensor
  node.preTransmission(preTransmission); //Callback for configuring RS-485 Transreceiver correctly
  node.postTransmission(postTransmission);
}

void loop()
{
  uint8_t result = node.readHoldingRegisters(0x0000, 20);//Read the 20 reggisters complete frame

  if (result == node.ku8MBSuccess)
  {
    for (byte i = 0; i < 20; i++) {
      values[i] = node.getResponseBuffer(i); //store all frame data in array to compare after
      Serial.print(values[i], HEX);
      Serial.print(",");
    }
    ec = values[0]; //in the first 4 bytes is data of electroconductivity
    tempe = values[4];//this correspond to data from temperature stored in values
    salt = values[6];//finally this is the lecture of salinity
  }
  Serial.print("/");
  Serial.print(ec, HEX); //This is printing 3CF7 what in float big endian must be 0.030
  Serial.print(",");
  Serial.print(tempe, HEX);//This is printing 41C3 what in float big endian must be 24.37
  Serial.print(",");
  Serial.println(salt, HEX);////This is printing 4171 what in float big endian must be 15.062
   delay(3000);
  node.clearResponseBuffer();
  //If I try to convert the values to little endian swaping the bytes with the next function, the output is:
  //F73C, C341 and 7141 what arduino understand in DEC as 49985,16835 and 16753.
  uint16_t Out1 = (ec << 8) | (ec >> 8);
  uint16_t float_value = (float)Out1;
  Serial.println("ec swaped: " + String(Out1,HEX));////This is printing 4171 what in float big endian must be 15.062
}

[/code]

Hi @nicolasmaker11 ,

Welcome to the forum..

um.. that's 2 bytes not 4..
don't think you have an endian issue, just missing all the bytes..
each modbus register is a word 2 bytes, you have to combine 2 registers into a 4 byte float..
let's take the datasheet example temp is 419C6F60..
first word 419C second word 6F60..


void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("ready..");
  word firstWord = 0x419C;
  word secondWord = 0x6F60;
//combine the words..
  unsigned long combined = (unsigned long)firstWord << 16 | secondWord;
  Serial.println(combined,HEX);
  float temp;
//copy into a float var..
  memcpy((byte*)&temp,(byte*)&combined,sizeof(float));
  Serial.println(temp);
}

void loop() {
  // put your main code here, to run repeatedly:

}

prints out 19.55
rinse and repeat for the rest..

good luck.. ~q

Thank you very much, it works perfectly, you have no idea how many hours I've been stuck with this, in other posts I had read that I had to do bitswaping and join things with union, all that confused me.

I understand that memcpy() copies a block of data from one location in memory to another, so here I would be reorganizing the information in the output float, but besides that there are other operations with AND, could you explain to me what is happening here please.

that copies 4 bytes from combined into temp..
a float is 4 bytes and an unsigned long is also 4 bytes..
getResponseBuffer give you 1 word (2 bytes)..
you need to get the 2 words, 2 calls to function, then combine them..
i used an unsigned long to combine the 2 words, first word get shifted to the left 16 bits and it's type casted to an unsigned long so we don't loose it in the shift, then the second word is OR'd in..
then a quick copy of bytes and all good..

threw me for a bit as you stated an endian issue, which means the order of bytes in the words needs to be swapped, but couldn't get to the 19.55 until I just combined and copied, easy..

~q

sorry, just realized what you actually asked me..
the & returns us a pointer, then we typecast that pointer to byte pointer..
then we copy from one byte pointer to another..

~q

@qubits-us thanks again for the help and explanation. I share the code in case someone else finds it useful:

[code]
#include<ModbusMaster.h>
#include <SoftwareSerial.h>

#define RX_PIN 12
#define TX_PIN 13
#define MAX485_REDE_PIN 8

uint32_t values[20]; //store all answer from the sensor
float  ec; //electroconductivity from the sensor Reg addres 0x0000 and lenght: 4 bytes
float salt;//salinity from sensor, Register addres: 0x0008
float temp;//Temperature from ssor

SoftwareSerial modbusSerial(RX_PIN, TX_PIN);
ModbusMaster node; //object node for class ModbusMaster

void preTransmission() //Function for setting stste of Pins DE & RE of RS-485
{
  digitalWrite(MAX485_REDE_PIN, 1);
}
void postTransmission()
{
  digitalWrite(MAX485_REDE_PIN, 0);
}

void setup()
{
  pinMode(MAX485_REDE_PIN, OUTPUT);
  digitalWrite(MAX485_REDE_PIN, 0);
  Serial.begin(9600); //Baud Rate as 9600
  modbusSerial.begin(9600); //Baud Rate as 9600
  node.begin(4, modbusSerial); //Slave ID as 4 ec sensor
  node.preTransmission(preTransmission); //Callback for configuring RS-485 Transreceiver correctly
  node.postTransmission(postTransmission);
}

void loop()
{
  uint8_t result = node.readHoldingRegisters(0x0000, 20);//Read the 20 reggisters complete frame

  if (result == node.ku8MBSuccess)
  {
    for (byte i = 0; i < 20; i++) {
      values[i] = node.getResponseBuffer(i); //store all frame data in array to compare after
//      Serial.print(values[i], HEX);
//      Serial.print(",");
    }
    ec = JoinWordsInFloat(values[0], values[1]);
    temp = JoinWordsInFloat(values[4], values[5]);
    salt = JoinWordsInFloat(values[6], values[7]);
    Serial.println("EC: " + String(ec)+ "mS/cm");
    Serial.println("Temperature: " + String(temp)+ "°C");
    Serial.println("Salinity: " + String(salt)+ "ppm");
  }
  delay(3000);
  node.clearResponseBuffer();
}
float JoinWordsInFloat(uint16_t first, uint16_t secon) {
  word firstWord = first;
  word secondWord = secon;
  float val;
  unsigned long combined = (unsigned long)firstWord << 16 | secondWord;
  memcpy((byte*)&val, (byte*)&combined, sizeof(float));
  return val;
}


[/code]