RS485 MODBUS ARDUINO: Print multiple response data frame from soil sensor

Hi everyone. I'm guiding myself from tutorial Measure Soil Nutrient using Arduino & Soil NPK Sensor
and this is the code:

#include <Wire.h>
#include <SoftwareSerial.h> //Library to convert Digital Output pins of the board to transmitter as well as receiver
#define RE 8
#define DE 7
const byte inq[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x07, 0x04, 0x08}; 
//And the response data frame are for all those values: hum,temp, ec, ph, n, p, k like in the attach image
byte values[11];
SoftwareSerial mod(2,3); // RX, TX ( Creates a new SoftwareSerial object )
void setup(){
  Serial.begin(4800);
  mod.begin(4800);
  pinMode(RE, OUTPUT);
  pinMode(DE, OUTPUT);
} 
void loop(){
  float val1;
  val1 = Hum();
  Serial.print(" |HUM : ");
  Serial.print(val1);
  Serial.println();
  delay(2000); 
}
float Hum(){  
  digitalWrite(DE,HIGH);
  digitalWrite(RE,HIGH);
  delay(10);
  if(mod.write(inq,sizeof(inq))==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.print(" ");
    }
    Serial.println();      
  }
  return (values[4]);   
}

Image example test response

But in the example the response is only with 1 data frame,
in my case the response is with 7 data frame. Please, can you guide me to read each one with Arduino uno?.
I'll be very greatful.

The example code shows these three series of bytes to get different values from the sensor

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};

Your series of bytes does not match any of them
const byte inq[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x07, 0x04, 0x08};

Thank you for answer my question. In my case is only one inquiry with 7 data length. And I want to print the reponse data frame
for each: hum,temp, ec, ph, n, p, k
Can I iterate 12 times or can I iterate inside values[4] to get each data (hum,temp, ec, ph, n, p, k)?

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

Thanks in advance.

why doesn't your output show the line with " |HUM : " that prints?

Dear gcjr. The output show incorrect values like in the image

I have been trying to do 13 iterations to display the data from position 4 of the array but I am not sure if it is the correct way.
Please, any ideas i'll appreciate a lot.

#include <SoftwareSerial.h> //Library to convert Digital Output pins of the board to transmitter as well as receiver
#define RE 8
#define DE 7
//REQUEST FRAME
const byte inq[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x07, 0x04, 0x08}; 
//And the response data frame are for all those values: hum,temp, ec, ph, n, p, k like in the attach image
byte values[11];
SoftwareSerial mod(2,3); // RX, TX ( Creates a new SoftwareSerial object )
void setup(){
  Serial.begin(4800);
  mod.begin(4800);
  pinMode(RE, OUTPUT);
  pinMode(DE, OUTPUT);
} 
void loop(){
  float val1;
  val1 = Hum();
  Serial.print(" |HUM : ");
  Serial.print(val1);
  Serial.println();
  delay(2000); 
}
float Hum(){  
  digitalWrite(DE,HIGH);
  digitalWrite(RE,HIGH);
  delay(10);
  if(mod.write(inq,sizeof(inq))==8){
    digitalWrite(DE,LOW);
    digitalWrite(RE,LOW);
    // 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<13;i++){
//    Serial.print(mod.read(),HEX);
	  values[i] = mod.read();
      Serial.print(values[i],HEX);
	  //the values for: hum,temp, ec, ph, n, p, k
	  Serial.print(" hum ");
	  Serial.print(values[4]);
	  Serial.print(" % ");
	  Serial.print(" temp ");
	  Serial.print(values[5]);
	  Serial.print(" º ");
	  Serial.print(" ec ");
	  Serial.print(values[6]);
	  Serial.print("  ");
	  Serial.print(" ph ");
	  Serial.print(values[7]);
	  Serial.print(" ");
	  Serial.print(" n ");
	  Serial.print(values[8]);
	  Serial.print("  ");
	  Serial.print(" p ");
	  Serial.print(values[9]);
	  Serial.print("  ");
	  Serial.print(" k ");
	  Serial.print(values[10]);
	  Serial.print(" ");
    }
    Serial.println();      
  }
  return (values[4]);   //this is only for HUMIDITY VALUE
}

Your output looks like it was produced by a different version of your code - I don't see hum or % for example.

It's confusing too because it looks like you read characters one at a time but you're printing values you haven't received yet. I suggest that you read them all and then print once.

Finally, you are reading without checking if there is anything there.

Your original image above shows a response containing 19 bytes. You would have to read all of them. Response #5 looks like the values are shifting around from read/read due to not reading them all.

Do you have a datasheet for this sensor the describes all the packets and responses?

As @blh64 says, please post a link to your datasheet for the NPK sensor you are using.

Previous attempts to use this type of sensor that have been discussed in this forum revolve around just nitrogen, phosphate & potassium readings. In those cases, each value had to be requested separately as shown in the original code you linked to in #1.

I've not seen a version of NPK code that requests all readings in one command. That's where the link to the datasheet detailing the supported modbus commands & responses would help us to help you.

Thanks to everyone for answering :+1:
I'm using...
Hardware components:
RS485
Arduino uno
Charger 9V (Only for soil sensor)
Soil moustiure (read 7 values)

Hand tools and fabrication machines:
Arduino IDE

The vendor provide me inquiry code: 0x01 0x03 0x00 0x00 0x00 0x07 0x04 0x08 and the response
are for: hum, temp, ec, ph, n, p, k

Image trying to understand the response data frame

I changed a bit the code:

#include <Wire.h>
//Library to convert Digital Output pins of the board to transmitter as well as receiver
#include <SoftwareSerial.h>
#define RE 8
#define DE 7
const byte requestframe[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x07, 0x04, 0x08};
byte values[19];
SoftwareSerial mod(2, 3); // RX, TX ( Creates a new SoftwareSerial object )
void setup() {
  Serial.begin(4800);
  mod.begin(4800);
  pinMode(RE, OUTPUT);
  pinMode(DE, OUTPUT);
}
void loop() {
  Hum();
  delay(3000);
}
void Hum() {
  digitalWrite(DE, HIGH);
  digitalWrite(RE, HIGH);
  delay(10);
  if (mod.write(requestframe, sizeof(requestframe)) == 8) {
    digitalWrite(DE, LOW);
    digitalWrite(RE, LOW);
    for (byte i = 0; i <19; i++) {
      values[i] = mod.read();
      //VALUES FOR EACH: hum, temp, ec, ph, n, p, k
      Serial.print(values[i], HEX);
      Serial.print(' ');
      delay(10);
    }
    Serial.print("| hum: ");
    Serial.print(values[4]);
    Serial.print(" % ");
    Serial.print(" temp: ");
    Serial.print(values[5]);
    Serial.print(" º ");
    Serial.print(" ec: ");
    Serial.print(values[6]);
    Serial.print("  ");
    Serial.print(" ph: ");
    Serial.print(values[7]);
    Serial.print(" ");
    Serial.print(" n: ");
    Serial.print(values[8]);
    Serial.print("  ");
    Serial.print(" p: ");
    Serial.print(values[9]);
    Serial.print("  ");
    Serial.print(" k: ");
    Serial.print(values[10]);
    Serial.print(" ");
    Serial.println(" ");
  }

}

OutPut:

1.-In the tutorial is looping 7 times and only for one value
2.-In my case I have sensor measure 7 values (hum, temp, ec, ph, n, p, k).
I'm looping 19 times but I'm not sure if it's the rigth way.
Please, Anyone guide me to achive for show the rigth data?

The response data frame that the supplier has given you doesn't look right. If the number of bytes in the response message is expected to be 14, then that's not 0x14, it's 0x0E.

The expected response sequence starting "0x01 0x03 0x0E...." appears on the top line of your output screenshot. It doesn't appear anywhere else.

Before figuring out how to decode the data, I would suggest that you work towards getting a consistent reply from the sensor, so that each received response starts with the correct preamble.

Have you got the RS485 bus termination resistor setup correctly?

Thank you for answering. I followed like the image scheme:

9v charger
rs485
Arduino uno
What else could be wrong and the data is not being read correctly?

You say you are using an Arduino UNO, but your fritzing sketch shows a Nano. Where is the 9V charger connected, because it seems that everything is being powered from the USB 5V.

The power supply are like the image but without arduino nano and LCD DISPLAY

https://www.rapidtables.com/convert/number/hex-to-decimal.html
01 03 0E 00 00 00 F3 00 00 00 46 00 00 00 00 00 00 C4 96
HEXADECIMAL: 0E
DECIMAL: 14

Is the printing of the values ​​ok? Or should I do otherwise?
Please @markd833, @Faraday, @wildbill, @blh64 , @gcjr . Can you help me to achieve this issue?
Any help or suggestion is welcome. I'll be very greatful :pray:

You are still not using mod.available to check that there is something to read so your positions in the response frame can be off.

Still the same :,

Post your latest attempt and its output.

Dear @wildbill. The code are:

//Library to convert Digital Output pins of the board to transmitter as well as receiver
#include <SoftwareSerial.h>
#define RE 8
#define DE 7
const byte requestframe[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x07, 0x04, 0x08};
byte values[19];
SoftwareSerial mod(2, 3); // RX, TX ( Creates a new SoftwareSerial object )

void setup() {
  Serial.begin(4800);
  mod.begin(4800);
  pinMode(RE, OUTPUT);
  pinMode(DE, OUTPUT);
}

void loop() {
  if (mod.available()) {
    SoilParams();
    // We show the seven records.
    delay(2000);
    for (int i = 0; i < 7; i++) {
      switch (i) {
        case 1:
          Serial.print("| hum: ");
          Serial.print(getInverseInt(values, i) / 100.00);
          Serial.print(" % ");
          break;
        case 2:
          Serial.print(" temp: ");
          Serial.print(getInverseInt(values, i) / 100.00);
          Serial.print(" º ");
          break;
        case 3:
          Serial.print(" ec: ");
          Serial.print(getInverseInt(values, i));
          Serial.print("  ");
          break;
        case 4:
          Serial.print(" ph: ");
          Serial.print(getInverseInt(values, i));
          Serial.print(" ");
          break;
        case 5:
          Serial.print(" n: ");
          Serial.print(getInverseInt(values, i));
          Serial.print("  ");
          break;
        case 6:
          Serial.print(" p: ");
          Serial.print(getInverseInt(values, i));
          Serial.print("  ");
          break;
        case 7:
          Serial.print(" k: ");
          Serial.print(getInverseInt(values, i));
          Serial.print(" ");
          break;
        default:
          break;
      }
    }
    Serial.println(" ");
  }
  delay(1000);
}

void SoilParams() {
  digitalWrite(DE, HIGH);
  digitalWrite(RE, HIGH);
  delay(10);
  if (mod.write(requestframe, sizeof(requestframe)) == 8) {
    digitalWrite(DE, LOW);
    digitalWrite(RE, LOW);
    for (byte i = 0; i < 19; i++) {
      values[i] = mod.read();
      Serial.print(values[i], HEX);
      Serial.print(' ');
      delay(10);
    }
  }
}
// This function takes the buffer answered with the modbus frame, and gets the
// data that is in position pos.
int getInverseInt(byte *bufer, int pos) {
  int a = pos * 2 + 3; // We avoid the id, function and number of data.
  // We mount the word and return it as an int.
  return (int)((word)bufer[a] << 8 | (word)bufer[a + 1]);
} 

and the outp are those:

Any idea or suggestions? What can be wrong?

The normal temperature here is above 21º degrees and the humidity is over 60% percent.

The purpose of mod.available is to see whether there is anything to read. You may find that it takes the soil device a while to respond. Similarly, there's no guarantee that because one character has arrived that they all have.

In consequence, you should never use read anything from serial until you know it's there. Your current code isn't doing that yet.

4800 baud is slow compared to how fast your processesor is running. Something like this should help

#include <Wire.h>
//Library to convert Digital Output pins of the board to transmitter as well as receiver
#include <SoftwareSerial.h>
#define RE 8
#define DE 7
const byte requestframe[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x07, 0x04, 0x08};
SoftwareSerial mod(2, 3); // RX, TX ( Creates a new SoftwareSerial object )
void setup() {
  Serial.begin(4800);
  mod.begin(4800);
  pinMode(RE, OUTPUT);
  pinMode(DE, OUTPUT);
}
void loop() {
  Hum();
  delay(3000);
}
void Hum() {
  int valueRaw;
  float valueReal;

  unsigned long startTime = millis();

  const unsigned long timeout = 5000; // milliseconds to wait
  byte values[19];
  int idx = 0;

  digitalWrite(DE, HIGH);
  digitalWrite(RE, HIGH);
  delay(10);

  if (mod.write(requestframe, sizeof(requestframe)) == 8) {
    digitalWrite(DE, LOW);
    digitalWrite(RE, LOW);

    // we expect the preamble 0x01,0x03, the count 0x0E, the data, and the CRC 2 bytes == 18
    while ( idx < 18 ) {
      if ( mod.available() ) {
        values[idx++] = mod.read();
      }
      if ( millis() - startTime >= timeout ) {
        Serial.println( "Timout" );
        return;
      }
    }
    for (byte i = 0; i < idx; i++) {
      Serial.print(values[i], HEX);
      Serial.print(' ');
      delay(10);
    }
    Serial.print(" | ");

    idx = 2;  // skip preamble
    
    // first humidity
    valueRaw = values[idx] << 8 | values[idx + 1];
    valueReal = valueRaw / 10.0;
    idx += 2;
    Serial.print("hum: ");
    Serial.print(valueReal, 2);
    Serial.print(" %");

    // temperature
    valueRaw = values[idx] << 8 | values[idx + 1];
    valueReal = valueRaw / 10.0;
    idx += 2;
    Serial.print(", temp: ");
    Serial.print(valueReal, 2);
    Serial.print(" º");

    // ec
    valueRaw = values[idx] << 8 | values[idx + 1];
    valueReal = valueRaw / 10.0;
    idx += 2;
    Serial.print(", ec: ");
    Serial.print(valueReal, 2);

    // ph
    valueRaw = values[idx] << 8 | values[idx + 1];
    valueReal = valueRaw / 10.0;
    idx += 2;
    Serial.print(", ph: ");
    Serial.print(valueReal, 2);

    // n
    valueRaw = values[idx] << 8 | values[idx + 1];
    valueReal = valueRaw / 10.0;
    idx += 2;
    Serial.print(", n: ");
    Serial.print(valueReal, 2);

    // p
    valueRaw = values[idx] << 8 | values[idx + 1];
    valueReal = valueRaw / 10.0;
    idx += 2;
    Serial.print(", p: ");
    Serial.print(valueReal, 2);

    // k
    valueRaw = values[idx] << 8 | values[idx + 1];
    valueReal = valueRaw / 10.0;
    idx += 2;
    Serial.print(", k: ");
    Serial.print(valueReal, 2);

    // the CRC
    valueRaw = values[idx] << 8 | values[idx + 1];

    // TODO: validate it is correct

    Serial.println();
  }
}

Initialising the state of RE & DE in setup() may also help. Something like:

  pinMode(RE, OUTPUT);
  pinMode(DE, OUTPUT);
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

Have you got your A & B wires crossed over, as there doesn't appear to be a repeatable pattern received in response to each of your commands.