Soil sensor 7 in 1 print mixed values

I'm working with a 7 parameter soil sensor (JXCT-IoT) Manual SOIL SENSOR . I did tests to print each parameter and it prints the correct value but when I put all the code, the values ​​are mixed for example the soil moisture Temperature: 0.7 % value is printed as the PH Ph: 24.3 PH value.
It should be clarified that I am using arduino uno, rs485, the sensor works with 5v, the altsoft library and the pins (6, 7, 8, 9) for the communication of the arduino uno with the sensor.

Please, if any of you managed to solve this problem, could you guide me or share the code. I will be very grateful for your help.

#include <AltSoftSerial.h>

// RO to pin 8 & DI to pin 9 when using AltSoftSerial
#define RE 6
#define DE 7

const byte temp[] = {0x01,0x03, 0x00, 0x13, 0x00, 0x01, 0x75, 0xcf};//
const byte mois[]  = {0x01,0x03,0x00,0x12,0x00,0x01,0x24,0x0F};
const byte econ[] = {0x01,0x03, 0x00, 0x15, 0x00, 0x01, 0x95, 0xce};
const byte ph[] = {0x01,0x03, 0x00, 0x06, 0x00, 0x01, 0x64, 0x0b};//0x0B64

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];
AltSoftSerial mod;

float envhumidity = 0.0, envtemperature = 0.0, soil_ph = 0.0, soil_mois = 0.0, soil_temp = 0.0;
byte val1 = 0, val2 = 0, val3 = 0, val4 = 0,val5 = 0, val6 = 0, val7 = 0;

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

  // put RS-485 into receive mode
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

  delay(3000);
}

void loop() {
  val1 = moisture();
  soil_mois = val1/10.0;
  delay(1000);
  soil_temp = temperature()/10.0;
  delay(1000);
  val3 = econduc();
  delay(1000);
  val4 = phydrogen()/10;
  soil_ph = val4;
  delay(1000);
  val5 = nitrogen();
  delay(1000);
  val6 = phosphorous();
  delay(1000);
  val7 = potassium();
  delay(1000);

  Serial.print("Moisture: ");Serial.print(soil_mois);Serial.println(" %");
  delay(1000);
  Serial.print("Temperature: ");Serial.print(soil_temp);Serial.println(" C");
  delay(1000);
  Serial.print("EC: ");Serial.print(val3);Serial.println(" us/cm");
  delay(1000);
  Serial.print("ph: ");Serial.print(soil_ph);Serial.println(" ph");
  delay(1000);
  Serial.print("Nitrogen: "); Serial.print(val5);Serial.println(" mg/kg");
  delay(1000);
  Serial.print("Phosphorous: ");Serial.print(val6);Serial.println(" mg/kg");
  delay(1000);
  Serial.print("Potassium: ");Serial.print(val7);Serial.println(" mg/kg");
  Serial.println();
  delay(3000);
}

byte moisture() {
  // clear the receive buffer
  mod.flushInput();

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

  // write out the message
  for (uint8_t i = 0; i < sizeof(mois); i++) mod.write(mois[i]);

  // wait for the transmission to complete
  mod.flush();

  // switching RS485 to receive mode
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

  // delay to allow response bytes to be received!
  delay(200);

  // read in the received bytes
  for (byte i = 0; i < 7; i++) {
    values[i] = mod.read();
    // Serial.print(values[i], HEX);
    // Serial.print(' ');
  }
  return values[4];
}

byte temperature() {
  // clear the receive buffer
  mod.flushInput();

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

  // write out the message
  for (uint8_t i = 0; i < sizeof(temp); i++) mod.write(temp[i]);

  // wait for the transmission to complete
  mod.flush();

  // switching RS485 to receive mode
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

  // delay to allow response bytes to be received!
  delay(200);

  // read in the received bytes
  for (byte i = 0; i < 7; i++) {
    values[i] = mod.read();
    // Serial.print(values[i], HEX);
    // Serial.print(' ');
  }
  return values[3]<<8|values[4];
}

byte econduc() {
  // clear the receive buffer
  mod.flushInput();

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

  // write out the message
  for (uint8_t i = 0; i < sizeof(econ); i++) mod.write(econ[i]);

  // wait for the transmission to complete
  mod.flush();

  // switching RS485 to receive mode
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

  // delay to allow response bytes to be received!
  delay(200);

  // read in the received bytes
  for (byte i = 0; i < 7; i++) {
    values[i] = mod.read();
    // Serial.print(values[i], HEX);
    // Serial.print(' ');
  }
  return values[4];
}

byte phydrogen() {
  // clear the receive buffer
  mod.flushInput();
  // switch RS-485 to transmit mode
  digitalWrite(DE, HIGH);
  digitalWrite(RE, HIGH);
  delay(1);

  // write out the message
  for (uint8_t i = 0; i < sizeof(ph); i++) mod.write(ph[i]);

  // wait for the transmission to complete
  mod.flush();

  // switching RS485 to receive mode
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

  // delay to allow response bytes to be received!
  delay(200);

  // read in the received bytes
  for (byte i = 0; i < 7; i++) {
    values[i] = mod.read();
    // Serial.print(values[i], HEX);
    // Serial.print(' ');
  }
  return values[4];
}

byte nitrogen() {
  // clear the receive buffer
  mod.flushInput();

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

  // write out the message
  for (uint8_t i = 0; i < sizeof(nitro); i++) mod.write(nitro[i]);

  // wait for the transmission to complete
  mod.flush();

  // switching RS485 to receive mode
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

  // delay to allow response bytes to be received!
  delay(200);

  // read in the received bytes
  for (byte i = 0; i < 7; i++) {
    values[i] = mod.read();
    // Serial.print(values[i], HEX);
    // Serial.print(' ');
  }
  return values[4];
}

byte phosphorous() {
  mod.flushInput();
  digitalWrite(DE, HIGH);
  digitalWrite(RE, HIGH);
  delay(1);
  for (uint8_t i = 0; i < sizeof(phos); i++) mod.write(phos[i]);
  mod.flush();
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);
  // delay to allow response bytes to be received!
  delay(200);
  for (byte i = 0; i < 7; i++) {
    values[i] = mod.read();
    // Serial.print(values[i], HEX);
    // Serial.print(' ');
  }
  return values[4];
}

byte potassium() {
  mod.flushInput();
  digitalWrite(DE, HIGH);
  digitalWrite(RE, HIGH);
  delay(1);
  for (uint8_t i = 0; i < sizeof(pota); i++) mod.write(pota[i]);
  mod.flush();
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);
  // delay to allow response bytes to be received!
  delay(200);
  for (byte i = 0; i < 7; i++) {
    values[i] = mod.read();
    // Serial.print(values[i], HEX);
    // Serial.print(' ');
  }
  return values[4];
}

I think it would be a good idea to increase the baudrate to the serail monitor, e.g. to 115200

Serial.begin(115200);

upload a schematic showing your wiring

Look at File|Examples|02.Digital|BlinkWithoutDelay to use millis() for timing instead of delay().

Why so many delays()? I think changing to a state machine and using millis() for timing would be the way to go with this project.

not tested but an example of not using delay to do the thing.

// C++ code
//
int sensorValue = 0;
int SomeValue = 1023 / 2;
void setup()
{
  pinMode(A0, INPUT);
  pinMode(LED_BUILTIN, OUTPUT);
}
unsigned long delayOneK = 1000;
unsigned long pastTime = millis();
int state = 0;
void loop()
{
  switch ( state )
  {
    case 0:
      if ( (millis() - pastTime ) >= delayOneK )
      {
        val1 = moisture();
        soil_mois = val1 / 10.0;
        state = 1;
        Serial.print("Moisture: "); Serial.print(soil_mois); Serial.println(" %");
        mpastTime = millis();
      }
      break;
    case 1:
      if ( (millis() - pastTime) >= delayOneK )
      {
        soil_temp = temperature() / 10.0;
        Serial.print("Temperature: "); Serial.print(soil_temp); Serial.println(" C");
        pastTime = millis();
        state = 2;
      }
      break;
    case 2:
      if ( (millis() - pastTime) >= delayOneK )
      {
        val3 = econduc();
        Serial.print("EC: "); Serial.print(val3); Serial.println(" us/cm");
        pastTime = millis();
        state = 3;
      }
      break;
    case 3:
      if ( (millis() - pastTime) >= delayOneK )
      {
        val4 = phydrogen() / 10;
        soil_ph = val4;
        Serial.print("ph: "); Serial.print(soil_ph); Serial.println(" ph");
        pastTime = millis();
        state = 4;
      }
      break;
    case 4:
      if ( (millis() - pastTime) >= delayOneK )
      {
        val5 = nitrogen();
        Serial.print("Nitrogen: "); Serial.print(val5); Serial.println(" mg/kg");
        pastTime = millis();
        state = 5;
      }
      break;
    case 5:
      if ( (millis() - pastTime) >= delayOneK )
      {
        val6 = phosphorous();
        Serial.print("Phosphorous: "); Serial.print(val6); Serial.println(" mg/kg");
        state = 6;
        pastTime = millis();
      }
      break;
    case 6:
      if ( (millis() - pastTime) >= delayOneK )
      {
        val7 = potassium();
        Serial.print("Potassium: "); Serial.print(val7); Serial.println(" mg/kg");
        state = 0;
        pastTime = millis();
      }
      break;
    default:
      state = 0;
      break;
  }

} //loop()

Thanks dear @Idahowalker for your reply . Run the code by changing the baud rate to 115200 and the above code but the output values are not correct for each parameter.

Data output when the sensor is connected to soil

Moisture: 14.80 %
Temperature: 2.20 C
EC: 129 us/cm
ph: 18.00 ph
Nitrogen: 46 mg/kg
Phosphorous: 65 mg/kg
Potassium: 131 mg/kg

Moisture: 14.80 %
Temperature: 2.20 C
EC: 137 us/cm
ph: 18.00 ph
Nitrogen: 45 mg/kg
Phosphorous: 64 mg/kg
Potassium: 130 mg/kg

Moisture: 14.80 %
Temperature: 2.20 C
EC: 133 us/cm
ph: 18.00 ph
Nitrogen: 47 mg/kg
Phosphorous: 64 mg/kg
Potassium: 129 mg/kg
...

Data output when I take it off from the soil

Temperature: 2.20 C
EC: 0 us/cm
ph: 18.00 ph
Nitrogen: 0 mg/kg
Phosphorous: 0 mg/kg
Potassium: 0 mg/kg

Moisture: 0.00 %
Temperature: 2.20 C
EC: 0 us/cm
ph: 18.00 ph
Nitrogen: 0 mg/kg
Phosphorous: 0 mg/kg
Potassium: 0 mg/kg

Moisture: 0.00 %
Temperature: 2.20 C
EC: 0 us/cm
ph: 18.00 ph
Nitrogen: 0 mg/kg
Phosphorous: 0 mg/kg
Potassium: 0 mg/kg

...

I don't why prints the same value for temperature and ph. Any idea?

thanks for answering my question. Run the code changed to baud rate but the data is not correct for each parameter. I don't have much experience using this sensor.
I hope someone guide me to solve this problem.

Looks close to stated specs.

Added:
While at Uni, a friend constantly complained about his 8-digit scientific calculator: 1/3 * 3 did not equal 1.

Be happy your readings are mostly +/- 1 digit between subsequent displays. If you are woefully unhappy, more expensive sensors are likely available; or you could put in a statistical smoothing bit of code.

I don't think @Droid88
Has the sensor at 2.20C
ph of 18, as the range is only from 0 to 14 !!!!
moisture of 0 or 14,8%, which is very dry.

@Droid88 can you draw and send a picture of your circuit please, the Fritzy is very confusing about the 845 connections to the UNO.

A picture(s) of your project would help also, so we can see your component layout.

Tom.. :smiley: :+1: :coffee: :australia: :santa:

This discussion is a follow on from @Droid88 initial post in a long running discussion on RS485 comms to an NPK sensor.

This particular query involves the 7 parameter sensor whereas most of the posts in the other discussion revolve around the 3 parameter sensor.

I simulated a similar NPK sensor using a PC based modbus tool and the basic code found in various places on the web that uses canned modbus messages.

I've not figured it out yet, but there seems to be something strange with the RS485 comms:

  1. Power on
  2. Ask for parameter A - get zero or garbage
  3. Ask for parameter B - get parameter A
  4. Ask for parameter C - get parameter B
    etc

This might be the issue that @Droid88 is reporting.

I'm hoping to have some time to get to the bottom of this strange activity out of simple curiosity.

Thanks for answering my question dear, @markd833, @mrburnette, @TomGeorge.

Dear, @TomGeorge I made a more understandable diagram about the sensor connection

@Droid88 After a little exploration of the issue, I have figured out what is going wrong. The issue is down to the awful code circulating that is used to read from one of those NPK sensors. Specifically in how the reading of the response from the sensor is handled.

The code transmits a request for the first parameter and then assumes that the response from the NPK sensor is instantly available as soon as the request is transmitted. The code reads in the number of bytes expected to be available without carrying out any checks first. This results in the first request returning only 0xFF (actually -1 signalling no character available).

The code transmits a request for the second parameter and once again reads in the expected number of response bytes without carrying out any checks. However, what is read is actually the response to the first parameter query.

Similarly when requesting the third parameter, what is read in is the response to the second parameter query.

The code then goes back and requests the first parameter and actually reads in the response to the third parameter.

That's why the values that are read back are out of step with the requests.

I'm grateful @markd833 for your answer and explain to me the reason for the printing of the incorrect data. How can it be implemented in the code to check if the response is available? Please, can you guide me dear @Blackfin ?

Check your PMs.

@Droid88 Here is a piece of code I put together for the 3 parameter RS485 sensor. I've simulated the sensor using a Modbus tool that I have. The code doesn't have all the bells and whistles of a proper modbus library to perform checking of the received data.

#include <SoftwareSerial.h>

#define RE 7
#define DE 6

const uint32_t TIMEOUT = 500UL;

//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(8, 9); // Rx pin, Tx pin

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

  delay(500);
}

void loop() {
  byte val1, val2, val3;

  Serial.print("Nitrogen: ");
  val1 = nitrogen();
  Serial.print(val1);
  Serial.print(" mg/kg\n\n");
  delay(1000);

  Serial.print("Phosphorous: ");
  val2 = phosphorous();
  Serial.print(val2);
  Serial.print(" mg/kg\n\n");
  delay(1000);

  Serial.print("Potassium: ");
  val3 = potassium();
  delay(1000);
  Serial.print(val3);
  Serial.print(" mg/kg\n\n");

  delay(5000);
}

byte nitrogen() {
  uint32_t startTime = 0;
  uint8_t  byteCount = 0;
  
  digitalWrite(DE, HIGH);
  digitalWrite(RE, HIGH);
  delay(10);
  mod.write(nitro, sizeof(nitro));
  mod.flush();
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

  startTime = millis();
  while ( millis() - startTime <= TIMEOUT ) {
    if (mod.available() && byteCount<sizeof(values) ) {
      values[byteCount++] = mod.read();
      printHexByte(values[byteCount-1]);
    }
  }
  Serial.println();
  return values[4];
}

byte phosphorous() {
  uint32_t startTime = 0;
  uint8_t  byteCount = 0;
  
  digitalWrite(DE, HIGH);
  digitalWrite(RE, HIGH);
  delay(10);
  mod.write(phos, sizeof(phos));
  mod.flush();
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

  startTime = millis();
  while ( millis() - startTime <= TIMEOUT ) {
    if (mod.available() && byteCount<sizeof(values) ) {
      values[byteCount++] = mod.read();
      printHexByte(values[byteCount-1]);
    }
  }
  Serial.println();
  return values[4];
}

byte potassium() {
  uint32_t startTime = 0;
  uint8_t  byteCount = 0;
  
  digitalWrite(DE, HIGH);
  digitalWrite(RE, HIGH);
  delay(10);
  mod.write(pota, sizeof(pota));
  mod.flush();
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

  startTime = millis();
  while ( millis() - startTime <= TIMEOUT ) {
    if (mod.available() && byteCount<sizeof(values) ) {
      values[byteCount++] = mod.read();
      printHexByte(values[byteCount-1]);
    }
  }
  Serial.println();
  return values[4];
}

void printHexByte(byte b)
{
  Serial.print((b >> 4) & 0xF, HEX);
  Serial.print(b & 0xF, HEX);
  Serial.print(' ');
}

It is written for an UNO and one of the 5V RS485 modules. You can take out the serial handling code and use it in your setup and see if it works for you.

2 posts were split to a new topic: Rs485 soil sensor code needed

I've tested out your code with my 3 parameter RS485 sensor. It gives values for nitrogen and potassium, but not phosphorous

I will not link the code, as it is already posted in #14

However, if I use this code:

#include <SoftwareSerial.h>
#include <Wire.h>


#define RE 33 //esp32
#define DE 32


// Modbus RTU requests for reading NPK values

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

// A variable used to store NPK values
byte values[11];

SoftwareSerial mod(26,27);//RO,DI
 
void setup() {
  // Set the baud rate for the Serial port
  Serial.begin(9600);

  // Set the baud rate for the SerialSoftware object
  mod.begin(4800);

  // Define pin modes for RE and DE
  pinMode(RE, OUTPUT);
  pinMode(DE, OUTPUT);
  
  delay(500);
}
 
void loop() {
  // Read values
  byte val1,val2,val3;
  val1 = nitrogen();
  delay(250);
  val2 = phosphorous();
  delay(250);
  val3 = potassium();
  delay(250);
  

  // Print values to the serial monitor
  Serial.print("Nitrogen: ");
  Serial.print(val1);
  Serial.println(" mg/kg");
  Serial.print("Phosphorous: ");
  Serial.print(val2);
  Serial.println(" mg/kg");
  Serial.print("Potassium: ");
  Serial.print(val3);
  Serial.println(" mg/kg");
  
  delay(2000);
}
 
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[i] = mod.read();
    //Serial.print(values[i],HEX);
    }
    Serial.println();
  }
  return values[4];
}
 
byte phosphorous(){
  digitalWrite(DE,HIGH);
  digitalWrite(RE,HIGH);
  delay(10);
  if(mod.write(phos,sizeof(phos))==8){
    digitalWrite(DE,LOW);
    digitalWrite(RE,LOW);
    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];
}
 
byte potassium(){
  digitalWrite(DE,HIGH);
  digitalWrite(RE,HIGH);
  delay(10);
  if(mod.write(pota,sizeof(pota))==8){
    digitalWrite(DE,LOW);
    digitalWrite(RE,LOW);
    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];
}

image
(edited to show the hex values)

It gives me nitrogen and potassium but not phosphorous. Clearly the representation of values are not true because the numbers are identical, just attached to a different parameter.

My first thought is that something is wrong with the crc address of one of the parameters, but if that was the case, wouldn't I be getting 255 as a value? My NPK Sensor did not come with a manual, so I'm kind of taking a stab in the dark. What could be wrong?

I had a look at why things get out of sync a short while ago and simulated an NPK sensor to investigate. Have a read of reply #115 in this discussion for my theory as to why this happens:

So I've used your code and modified it to run with my esp32. This is the output:

image

I'm having trouble believing that nitrogen is 0 in this case.

I would say that the values being returned are being decoded correctly. Whether the sensor is working correctly is another matter.

You could just ask for nitrogen repeatedly and see what response you get.

Hello! I am attempting to set up this sensor with the same schematics. Did you ever get it to work reliably?