Sensor setup with RS485 Modbus RTU protocol

Hello. I purchased a sensor that works with the RS485 ModbusRTU protocol. I want to start it with Arduino, but there is no guide with the sensor. Previously, I set up sensors that work with this type of protocol, but they had a guide and it was written in the guide which registry should be called.

Sensor data sheet:
digital sensors_en.pdf (685.8 KB)

Thank you for your guidance

My first piece of guidance would be to suggest you contact the seller of the sensor and ask them for the relevant documentation.

Failing that, my second piece of guidance would be to tell us which sensor you have from the sensors in the PDF you provided.

And finally, I would ask what you discovered when you searched for documentation on the sensor you have bought.

I received the sensor manual from the company. My sensor is HY-CS1753D.

HY- CS1753D Operation Manual Of PH Sensor Series.pdf (597.3 KB)

How to enter Measurement Value addresses in esp program?

That manual appears to have all the details you need. Post the code you currently have.

This is the code that I used to set up a Chinese sensor exactly with the RS485 Modbus protocol.

NPK_Rev_1.7.5.ino (23.1 KB)

Probably the only change I need to make is in the addresses section:

const byte ni[] = {0x02, 0x03, 0x00, 0x1F, 0x00, 0x01, 0xB5, 0xFF}; // Multi Sensor: Nitrogen
const byte phos[] = {0x02, 0x03, 0x00, 0x1E, 0x00, 0x01, 0xE4, 0x3F};
const byte pot[] = {0x02, 0x03, 0x00, 0x20, 0x00, 0x01, 0x85, 0xF3};
//const byte pot[] = {0x01, 0x03, 0x00, 0x20, 0x00, 0x01, 0x85, 0xC0};
const byte moist[] = {0x02, 0x03, 0x00, 0x12, 0x00, 0x01, 0x24, 0x3C};
const byte temp[] = {0x02, 0x03, 0x00, 0x13, 0x00, 0x01, 0x75, 0xFC};
const byte ec[] = {0x02, 0x03, 0x00, 0x15, 0x00, 0x01, 0x95, 0xFD};

If you are using the NPK sensor code from these forums that use canned Modbus messages, then yes, you just need to change the bytes in the arrays you've posted above.

  • The 1st byte should be 0x01 - the device address
  • The 2nd byte should be 0x03 - for reading
  • The 3rd and 4th bytes are the address from the table in your manual but in hexadecimal - high byte then low byte.
  • The 5th and 6th bytes are a word count - usually set to 0x00 & 0x01.
  • The 7th and 8th bytes are the Modbus checksum for the message. You need to use an on-line CRC-16 calculator to generate the required value.

I use crccalc.com and it works fine.

You could start with trying to read something known such as temperature or the year of manufacture.

I didn't understand bytes 4, 5, 7, and 8. Can you explain a bit more? Thank you, my friend

Ok, lets try a worked example. Suppose you want to read the year of manufacture. From the user guide you posted, that data is at address 111 decimal.

The canned message you want is:

0x01    : The device address
0x03    : Function code 3 - the guide says 3 is for reading
0x00    : \ These 2 bytes are 0x006F => address 111
0x6F    : /
0x00    : \ These 2 bytes are the number of words to read 0x0001 => 1 word (i.e. 2 bytes)
0x01    : /
0xB4    : \ These 2 bytes are the Modbus CRC-16 checksum (low byte first)
0x17    : / Calculated using the crccalc.com website

Thank you for your guidance.
If I want to read the output value, should bytes 4 and 5 be added together?
I added bytes 4 and 5, the output is 478.

This is my code, only the PH section is read in the program:
NPK_Rev_TEST.ino (23.2 KB)

No, adding is not how to combine the 2 bytes. There is a function called word() that will do what you need:

I changed it like this:
float ph_reg3and4 = word((ph_values[4]) + ph_values[5]);
There is still output 478. Is the number 478 correct for when it is not in water?

Try:

float ph_reg3and4 = word(ph_values[4],ph_values[5]);

You may need to swap array elements 4 & 5 around if your sensor outputs low byte then high byte. I'm on a very small screen at the moment so can't easily pull up your datasheet.

Now in this situation:
float ph_reg3and4 = word(ph_values[4],ph_values[5]);
The output is 58363.00
And in this situation:
float ph_reg3and4 = word(ph_values[5],ph_values[4]);
The output is 64483.00.
Probably, division should be done to get the final number

Ok, this is the format of the expected Modbus response - the first number is the element of the received bytes array:

0       : Slave device ID
1       : Function code
2       : Byte count of the following data words
3 & 4   : Data word 1
5 & 6   : Data word 2
n & n+1 : Checksum

You should be looking at array elements 3 & 4 for the first word back.

Your code comment suggests you are looking for pH, but the address you have in your canned Modbus message is for the year of manufacture. The user guide indicates that this is a straight integer, NOT a float.

Inside your multi_sensor_ph_detect() function, you should initially uncomment the code inside the for loop that prints out the actual received bytes. That way you can see what is being received whilst you get your code working. You can comment it out later.

This is the new void multi_sensor_ph_detect() code:

void multi_sensor_ph_detect(){

  while (true){
    digitalWrite(DE, HIGH);
    digitalWrite(RE, HIGH);
    delay(100);
    if (mod.write(ph, sizeof(ph)) == 8)
    {
      mod.flush();
      digitalWrite(DE, LOW);
      digitalWrite(RE, LOW);
      mod.readBytes(ph_values, 7);
     // Serial.print("Multi PH Values : ");
      for (byte i = 0; i < 7; i++)
      {
      // Serial.print(ph_values[i], HEX);
        Serial.print(" ");
      }
      Serial.println();
    }
    //if (ph_values[0] == 0x01 && ph_values[1] == 0x03 && ph_values[2] == 0x00){
           Serial.println(ph_values[2]);
    int ph_reg3and4 = word(ph_values[5],ph_values[6]);
     Serial.println(ph_reg3and4);
      ph_reg3and4 = ph_reg3and4/100;
      if (ph_reg3and4 > -1 && ph_reg3and4 < 15){
        Serial.print("PH : "+(String)ph_reg3and4+"");
        Serial.println();
        delay(1000);
        client.publish(toCharArray("/angizeh/security_system/24/1/PH"), toCharArray(String(ph_reg3and4)));
        Serial.println("Published");
        break;
      }
//    }else{
//      Serial.print("PH cant measure");
//    break;
//    }
//  
  }
}

I read bytes 3 and 4 and the number 2019 was displayed, which is the year of manufacture. Therefore, I read bytes 5 and 6 and the number 64509 is displayed, which does not change and is constant.

Bytes 5 & 6 would be the checksum if you are reading 1 register and would be constant as long as the data is constant.

Also, I am not quite sure.of the implications of this line:

It only reads 7 bytes from the receive buffer. My example in post #15 would have 9 bytes. You should clear the receive buffer before requesting another value.

You know, this code is exactly the code with which I started the JXCT npk sensor. The number of bytes is exactly the same. On the other hand, the problem is that the number it reads is constant, that is, even when I put the sensor in water, the number is the same. If there is a problem in updating the reading number, at least it should show a different number the first time in the same situation. I think that I have no output and the number that is shown is not correct.

I told the support about this problem, but unfortunately they still didn't give me any answer.

The answer they gave me was this:

I would suggest you post your current code. You may find it helpful for yourself (and others who may wish to help you) if you were to take out all the additional code so you just have the sensor specific code.

The support told me to read the data registry 2 and send it to him and he sent me this:
01 03 00 02 00 01 CRC

Does the CRC value change here? Or the same code without changing? like this

0x01, 0x03, 0x00, 0x02, 0x00, 0x01, 0xB4, 0x17