NPK Sensor: How to convert hex to dec?

I’d like to ask what code do we need so that we can convert the hexadecimal data we receive from our npk sensor into its actual value? because that’s the only missing part to our device, thank you so much.

"NPK sensor" - are you referring to the 3 (or 5) pin RS485 soil sensor that is seen on here quite frequently?

If so, the data comes back from the sensor as a 16-bit binary value. If you assign that received data to an int16_t or uint16_t data type, then you can use the Serial.print() to simply print it out in decimal.

What do you mean by actual value? Some of the parameters are 10x their actual value - such as temperature - so that you have to divide by 10 to get the value to 1 decimal place.

Maybe like

or like

What is your data?

That's the question. 99.9% of the time these "convert to / from hex" questions mean someone is wrapped around the axle over the concepts of "hex" and "decimal" that are merely human-readable formats while the data is actually binary.

2 Likes

this is our initial code used

// Connections
// - the RO pin to Arduino digital pin 2
// - the DI pin to digital pin 3
// - the DE pin to digital pin 7
// - the RE pin to digital pin 8

const byte ROPin = 2;
const byte DIPin = 3;
const byte DEPin = 7;
const byte REPin = 8;

#include <SoftwareSerial.h>
SoftwareSerial max485Serial(ROPin, DIPin);

// address code, function code, register start, register length, CRC_L, CRC_H
const byte nitro[] = {0x01, 0x03, 0x00, 0x1e, 0x00, 0x01, 0xe4, 0x0c}; 
// expected answer: 
// address code,function code,effective number of bytes; Nitro value H, Nitro value L, CRC_L, CRC_H
// eg: 0x01 0x03 0x02 0x00 0x20 0xB9 0x9C ==> Nitro value = 0x0020 ==> 32 mg/Kg

void setup() {

  pinMode(REPin, OUTPUT);
  pinMode(DEPin, OUTPUT);
  Serial.begin(115200);
  max485Serial.begin(9600);
}

void loop() {

  // switch RS-485 to transmit mode
  digitalWrite(DEPin, HIGH);
  digitalWrite(REPin, HIGH);
  delay(1);

  // send the get nitro command
  max485Serial.write(nitro, sizeof nitro);
  max485Serial.flush();

  // switching RS485 to receive mode
  digitalWrite(DEPin, LOW);
  digitalWrite(REPin, LOW);

  byte index = 0;
  while (index <= 7) { // we will get stuck there if there is no anwser
    if (max485Serial.available()) {
      byte r = max485Serial.read();
      Serial.print("0x");
      Serial.print(r, HEX);
      Serial.print(' ');
      index++;
    }
  }
  Serial.println();
  delay(5000);
}

however the problem is that it lacks the code for the soil nutrients: nitrogen, phosphorus, and potassium and the part where it converts the hexadecimal data from the sensor intro decimal.

Would Serial.print(r); work? That converts the received data into decimal rather than hex.

Search these forums for NPK sensor and there are lots of examples of reading the various parameters.

You need the user manual for your exact sensor as not all NPK sensors use the same register map.

1 Like

could you help me with the code for the phosphorus and potassium part? we've tried it before where we applied the codes for each one of them but it only returned one of the values (ex: we included nitrogen phosphorus and potassium in the code but it only returned the value for ntrogen) we need a code where it will be able to return all the three values at once.

this was the code we used

Did you do this :arrow_up: ?

Post the code you were using for this so the forum can help you with it.

Do you have this :arrow_up: ?

1 Like

we have the manual

// Connections
// - the RO pin to Arduino digital pin 2
// - the DI pin to digital pin 3
// - the DE pin to digital pin 7
// - the RE pin to digital pin 8

const byte ROPin = 2;
const byte DIPin = 3;
const byte DEPin = 7;
const byte REPin = 8;

#include <SoftwareSerial.h>
SoftwareSerial max485Serial(ROPin, DIPin);

// address code, function code, register start, register length, CRC_L, CRC_H
const byte nitro[] = {0x01, 0x03, 0x00, 0x1e, 0x00, 0x01, 0xe4, 0x0c}; 
// expected answer: 
// address code,function code,effective number of bytes; Nitro value H, Nitro value L, CRC_L, CRC_H
// eg: 0x01 0x03 0x02 0x00 0x20 0xB9 0x9C ==> Nitro value = 0x0020 ==> 32 mg/Kg

void setup() {

  pinMode(REPin, OUTPUT);
  pinMode(DEPin, OUTPUT);
  Serial.begin(115200);
  max485Serial.begin(9600);
}

void loop() {

  // switch RS-485 to transmit mode
  digitalWrite(DEPin, HIGH);
  digitalWrite(REPin, HIGH);
  delay(1);

  // send the get nitro command
  max485Serial.write(nitro, sizeof nitro);
  max485Serial.flush();

  // switching RS485 to receive mode
  digitalWrite(DEPin, LOW);
  digitalWrite(REPin, LOW);

  byte index = 0;
  while (index <= 7) { // we will get stuck there if there is no anwser
    if (max485Serial.available()) {
      byte r = max485Serial.read();
      Serial.print("0x");
      Serial.print(r, HEX);
      Serial.print(' ');
      index++;
    }
  }
  Serial.println();
  delay(5000);
}

this is the code @markd833

So instead of printing the 8 bytes out as HEX as you get them, maybe store them in an array, and afterwards print out a combination of "Nitro value H, Nitro value L":

byte inputArray[]= {0x01, 0x03, 0x02, 0x00, 0x20, 0xB9, 0x9C};
...
Serial.print(uint16_t(inputArray[3])<<8 | inputArray[4]);  
1 Like

Does the code you have produce the correct response from your sensor? You should get 7 bytes back starting:

01 03 02 .....

If it does, then I would suggest moving away from the canned modbus messages and use the ModbusMaster library instead as it will take care of all the comms and data handling for you.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.