0
\$\begingroup\$

I'm working with a JXCT-IoT seven parameter soil sensor.

Circuit diagram

I did tests to print each parameter and it prints the correct value but when I put in 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 5 V. The altsoft library uses the pins 6, 7, 8, 9 for the communication between the Arduino Uno and the sensor.

Does anyone know what is wrong?

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

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
...

Why does it print the same value for temperature and pH?

\$\endgroup\$

2 Answers 2

0
\$\begingroup\$

Your function definition for temperature says that it returns a byte:

byte temperature()

A byte is only 8 bits long, yet you're actually trying to return a 16-bit value:

return values[3]<<8|values[4];

In your byte phydrogen() function you're only returning a single byte, but the manual seems to say that pH is a 2-byte value, so I suspect that you'll need to fix this to return a 2-byte (16-bit) value in the same way as temperature. I'm not sure that this is the primary issue with your pH reading though.

\$\endgroup\$
2
  • \$\begingroup\$ Thanks Brhans for your answer. I changed the code but the output still the same \$\endgroup\$
    – Mike
    Dec 21, 2022 at 18:24
  • \$\begingroup\$ If you enable your Serial.print(values[i], HEX); code in each of the functions, what byte values do you see returned? Edit your question with the details as it's probably too much for a comment. \$\endgroup\$
    – brhans
    Dec 21, 2022 at 18:33
0
\$\begingroup\$

Beyond the correct information by brhans above, there are some serious implied typecasting and mathematical operation issues primarily in the loop() function. For example, three snippets:

val1 = moisture();
soil_mois = val1/10.0;

soil_temp = temperature()/10.0;

val4 = phydrogen()/10;
soil_ph = val4;

So val1 is of type (uint8)byte, moisture() returns byte (or maybe now a uint16(word)). What happens now depends somewhat on compiler and FP library, the best case is that the compiler realizes you want to take your uint8 number convert it to a float and then divide it by 10.0 because you wrote /10.0 . The ‘register’ being read is uint16, range 0 to 1000.

soil_temp works the same except in this case the value is definitely a signed value, because negative temperatures happen. The ‘register’ is sint16, range -200 to 800. So the compiler converts to float using the type as guidance (uint8)byte, (uint16)word, or (sint16)int, only one is correct.

The ph register is again (uint16)word, range 300 to 900. Here though the return value of phydrogen() is left integer because the divisor is integer /10 . Still this assigned to a byte val4 which is converted to float when assigned to soil_ph. The integer divide results in an integer with no fractional part so you lose a full significant digit of precision here. Worse the actual divisor should be /100.0 leaving a single digit 3 to 9 for the pH.

The question is not why these values don’t change but why they make no sense. 2.2 C in the lab is damn cold, a 18.00 pH is out of range of the device and more alkaline(basic) than liquid drain cleaner.

There are three issues here for each parameter you’re trying to read from this sensor:

  1. Mishandling of the data from the sensor.
  2. Assignment across types and letting the compiler handle this for you by inference. There should at least be some compiler warnings here.
  3. Not being careful with the math and loosing precision or just getting bogus results.

First, the documentation is an awful, Chinese translation with incorrect and missing information, but it is clear that each ‘register’ is a 16 bit value; passed back in the serial response as 2 (uint8)byte values. In your code these are values[3] and values[4].

Second, explicitly cast any changes in type so the compiler knows what you want not what it infers. Show by the code what needs to be done to handle the 2 bytes and turn them into a useful number.

For example ‘temperature’ just wants to be a floating point number representing degrees C. Something like

float get_temperature() {
  word   a_uint16;
  int    a_sint16;  
  float  a_float;

  // all the code to populate values array
  // should probably make this into a function
  // to eliminate redundant code

  a_uint16 = values[3]<<8;
  a_uint16 |= values[4];
  a_sint16 = (int)a_uint16;
  a_float = (float)a_sint16;
  a_float /=  10.0;

  return a_float;
}

Same code for pH and moisture. Others should return a_sint16, which is an int so can use for math.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.