NPK Soil Sensor Making - Nitrogen Phosphorus Potassium levels in Soil

Hi
Am trying to replicate the below linked program.

but i have some technical questions related to programming.
byte nitrogen(){
digitalWrite(DE,HIGH);
digitalWrite(RE,HIGH);
delay(10);
if(mod.write(nitro,sizeof(nitro))==8){
digitalWrite(DE,LOW);
digitalWrite(RE,LOW);
for(byte i=0;i<7;i++){
//Serial.print(mod.read(),HEX);
values = mod.read();
_ Serial.print(values*,HEX);_
_
}_
_
Serial.println();_
_
}_
_
return values[4];_
_
}*_
i would like to know how this hex conversion works

sharonmathew:
mod.write if its true does it mean we are receiving a response frame. .
for(byte i=0;i<7;i++){
which location the response value is received or saved.

1

2

3

//Serial.print(mod.read(),HEX);
values[i] = mod.read();

is it here.
and where is it getting converted from hex to readable format.

1

2

3

4

Serial.print(values[i],HEX);
}
Serial.println();

}
return values[4];

See: Serial.print() - Arduino Reference.
There are options to display a number as DEC, BIN, OCT, HEX.

Koepel:
See: Serial.print() - Arduino Reference.
There are options to display a number as DEC, BIN, OCT, HEX.

thanks. but what is confusing me is the sensor is giving data in hex and it controller has to decode this to a readable form.
i dont see the conversion anywhere. i assume the returning value ( in the form a response code , probably 8 bit). it could be replacing the string which carries the inquiry code. or it could be saved somewhere. i have not got that identified yet.
as per the code it is still asking to show in hex like you said. but its dec we need. and the finished project does show in dec.

That sensor returns binary data. If you select the right byte and put it into a variable, then the value is in that variable. You can print it as decimal or hexadecimal.

For example 30HEX = 48DEC

byte value;
value = 0x30;       // 0x30 is 48 is 00110000
value = 48;           // same value
value = 0b00110000;      // same value
Serial.print( value, DEC);  // printed as 48
Serial.print( value, HEX);  // printed as 30

I prefer to print the hexadecimal value in a nice way:

// Serial.print(values[i],HEX);
for( int i=0; i<7; i++)
{
  values[i] = mod.read();

  Serial.print( "0x");
  if( values[i] < 0x10)
  {
    Serial.print( "0");
  }
  Serial.print( values[i], HEX);
  Serial.print( ", ");
}
Serial.println();

Sometimes a sensor returns data as readable ASCII text. Then you have to convert it into variables. For example a GPS module returns readable ASCII text. The ModBus uses binary data.

It happens here:

return values[4];

The first byte is index [ 0 ], and the fifth byte has index [ 4 ]. That is the value that is required. That value is returned and used in the sketch. That’s all. That byte is copied.

Do you have the RS-485 running ?

yes it does. the finished stuff will have three units. Ardunino nano v3 , RS485 ttl to rs485, OLED 128x64. the sensor which has VCC gnd, A B & are connected to RS485.
i have some questions and thanks for visiting.
layout of prog.

 #define RE 8
#define DE 7
 
//const byte code[]= {0x01, 0x03, 0x00, 0x1e, 0x00, 0x03, 0x65, 0xCD};
const byte nitro[] = {0x01,0x03, 0x00, 0x1e, 0x00, 0x01, 0xe4, 0x0c};
const byte phos[] = {0x01,0x03, 0x00, 0x1f, 0x00, 0x01, 0xb5, 0xcc};
const byte pota[] = {0x01,0x03, 0x00, 0x20, 0x00, 0x01, 0x85, 0xc0};
 
byte values[11];
SoftwareSerial mod(2,3);
 
void setup() {
  Serial.begin(9600);
  mod.begin(9600);
  pinMode(RE, OUTPUT);
  pinMode(DE, OUTPUT);

display initializin code here

void loop() {
  byte val1,val2,val3;
  val1 = nitrogen();
  delay(250);
  val2 = phosphorous();
  delay(250);
  val3 = potassium();
  delay(250);

some serial print codes
some display print codes

byte nitrogen(){
  digitalWrite(DE,HIGH);
  digitalWrite(RE,HIGH);
  delay(10);
  if(mod.write(nitro,sizeof(nitro))==8){
    digitalWrite(DE,LOW);
    digitalWrite(RE,LOW);
// When we send the inquiry frame to the NPK sensor, then it replies with the
 response frame
    // now we will read the response frame, and store the values in the 
values[] arrary, we will be using a for loop.
    for(byte i=0;i<7;i++){
    //Serial.print(mod.read(),HEX);
    values[i] = mod.read();
    Serial.print(values[i],HEX);
    }
    Serial.println();
  }
  return values[4];
}

and another bite for phosphorus and yet another one for potassium. and that’s all the code is. then these 3 functions store their values in 3 different variables for display.
my questions are.
1.

if(mod.write(nitro,sizeof(nitro))==8){

does the above code imply, >> if the returning values from sensor are of length 8 bit then do something.
2.

for(byte i=0;i<7;i++){
    //Serial.print(mod.read(),HEX);
    values[i] = mod.read();
    Serial.print(values[i],HEX);
    }

how many times the loops will repeat. is it 8. it appears to me it exits the loop before 7th round.
he mentioned the response value will be stored in values array. there are two values arrays. values[11] (on the top) & values*.*
so what is the usage for values[11].
since you mentioned ,HEX will be in hex format. is the serial.print printing in hex values. and could you explain how this value is displayed on OLED. is it fed to Arduino, and the conversion takes place inside. or how would it be.
and why do we loop. cant’ we feed the incoming value in just one go.
“return values[4]; // returns the Nitrogen value only, which is stored at location 4 in the array” does it mean it is the result of the function byte nitrogen for display.
i reckon you have given a better expression for values but its almost same as Ru!!ch. i am still trying to figure out :slight_smile:

When a variable has a value, that is called a binary value. It is not okay to say that it is a hexadecimal or decimal or binary number, but it can be printed as hex, dec or bin number.

If you have a value in a variable, then you can print it to the serial monitor: Serial.print( value)
It depends on the OLED library how to use the functions of that library.

Array’s in the ‘C’ language are used like this:

int myArray[10];   // 10 elements from [0] up to (inclusive) [9]

for( int i=0; i<10; i++)
{
  Serial.println( myArray[i] );
}

As long as ‘i’ is below 10, then the for-loop runs.

The SoftwareSerial imitates the real Serial library. The functions are the same.
You can check the functions of SoftwareSerial and Serial.

The mod.write() returns the number of bytes that are written. No one uses that. It will probably always return 8.

I only see one declaration of the values array:

byte values[11];

Are you confused with the return statement ? It can be written as this:

byte ret = values[4];  // get the byte that we want
return( ret);

thanks for helping me wrap my head around this puzzle.
ok so you said its binary data the sensor is feeding. so most likely the loop is for parsing that data.
interrogation signal: 0x01,0x03, 0x00, 0x1e, 0x00, 0x01, 0xe4, 0x0c
sensor returns hex as response frame. 01 03 06 00 6C 00 7E 01 60 D0 DC
Out of which 006C is Nitrogen value
007E is Phosphorus value
0160 is Potassium value in HEX if we convert this we get decimals gram/kg as unit. i have verified it with my sensor using a usb interface and it appears to be correct.

i now got more confusion away. (as long the data response is binary and 8 biit format) the loop makes kind of sense to me in a way.
Values[11] as11 indvidual containers. when mod.read is spitting out each 1’s and 0’s is it stored in the array like this
Values[8] = {1,0,0,0,1,0,0,1} or am i wrong.
or each value is saving as each binary code probably corresponding to the hex values we discussed.
in any case the nitrogen useful value is stored after [start address]
(( [address] [function] [start address] >> [data length] >>[no of valid bytes written] [ nitrogen content coefficent high 16 bits] [nitrogen content coefficient low 16 bits] [check code low] [check code high] ))
ie data length which was in hex, when i used it to convert it online to get N values. it could store two hex values. ie 00 06 for instance.
i was told it returns 8 bit. and considering your example the original code should have written as <8 instead of <7. i dont know why. anyway the useful part is the 4th section.
yes the return value is very confusing for me. i was intuitively reading it.
so now if my understanding is correct the programmer has programmed the return value from the saved array.
slicing only the 4th section, where the N hex value is.

Is the Potassium in two bytes ? Then they must be combined into one 16-bit integer.

I’m a little confused now as well.
Is there a document that describes what the sensor is returning ?
If it returns the readable ASCII text of “01 03 06 00 6C 00 7E 01 60 D0 DC”, then the values must be converted.

A ‘byte’ is 8 bits.
A ‘byte’ can be from 0 to 255 (inclusive).
https://www.arduino.cc/reference/en/language/variables/data-types/byte/

If you want, I can write that in different notations:

  • from 0 to 255
  • from 0DEC up to 255DEC
  • from 0x00 to 0xFF
  • from 00HEX FFHEX
  • from 0b00000000 to 0b11111111
  • from 00000000BIN to 11111111BIN

These are eleven bytes: byte values[11];

After posting you message on this forum, there could be a mistake or wrong link. In the lower-right corner is More/Modify and with “Modify” you can correct it.

about your question,
01 03 06 00 6C 00 7E 01 60 D0 DC
is the return signal. i read from the data sheet provided.
it says leave the frist 6 digits. then copy next 4 characters ie 006C and convert it from hex to dec then i get the value of N.
then take further next 4 characters 007E convert it from Hex to Dec then we can get P value and so is for K. That value is displayed in OLED screen with a unit of gm/Kg.
if my memory serves me right then the first 6 characters which has allocated memory of 1 bit each. but i am not sure about this statement. but for N value it has two bits I think. it is certainly 4 characters when i decoded them.
in any case you have helped me to climb up a lot from where i was today morning : )

This: "00 7E" is five characters because there is a space in between.
This: 0x00, 0x7E is two bytes.

The sketch in the link you gave uses binary data: Measure Soil Nutrient using Arduino & Soil NPK Sensor.

Some sensors can switch between readable ASCII text and binary data.
For example SeeedStudio has a soil sensor that can do both: https://www.seeedstudio.com/RS485-Soil-Moisture-Temperature-EC-Sensor-S-Soil-MTEC-02-p-4633.html.
It can be set as MODBUS-RTU (binary data) and as MODBUS-ASCII (readable ASCII text).

Is this the Instruction Manual ? http://www.jxctiot.com/upload/file/1557126748.pdf.
That uses binary data.

brilliant!! that 's the stuff!

007E doesnt have space. so they are two bytes you say .

the manual i have is slightly diff. but more or less same.
it asked me to do the following.
DTR RTS uncheck
parity none
stop bits 1
modbusRTU checked
input HEX checked
show HEX checked
input ASC unchecked
show ASC unchecked
ignore space,

since you said ModbusRTU is binary. perhaps yes and i got mixed up. when it showed on the pc after connecting via usb interface it might have converted from binary to HEX. atleast that's what i am thinking.

Hi, I'm also trying to use the NPK probe with Arduino and I'm following the same guide (Measure Soil Nutrient using Arduino & Soil NPK Sensor). I am using an Arduino Uno with the same connections, and the same code, but it doesn't work. All values are 255. To be premised that I don't know how the RS485 works, could someone kindly explain to me how I can do to solve the problem?