How to see 7 in 1 soil sensor register values (JXCT Soil Moisture Sensor)

// This attempt uses the SoftwareSerial & raw Modbus packets.
//
// RS485 module wired up as:
// RS485 DI signal to pin 3
// RS485 RO signal to pin 2
// RS485 RE signal to pin 8
// RS485 DE signal to pin 7
// RS485 VCC to 5V
// RS485 GND to GND
//
#include <SoftwareSerial.h>

#define RE 2
#define DE 3

const uint32_t TIMEOUT = 500UL;

// canned message to your RS485 device
// change this for any other canned modbus message you like
// remember to make sure that the checksum is correct!
uint8_t msg[] = {0x01, 0x03, 0x00, 0x13, 0x00, 0x01, 0x75, 0xCF};
uint8_t rxByte;

SoftwareSerial swSerial(0, 1); // Receive (data in) pin, Transmit (data out) pin

I can't understand how to see that my this line
uint8_t msg[] = {0x01, 0x03, 0x00, 0x13, 0x00, 0x01, 0x75, 0xCF};
is correct or not because I can't make my 7 in 1 soil sensor work

Hello, do yourself a favour and please read How to get the best out of this forum and modify your post accordingly, including code tags (please fix your first post by editing it and adding the tags around the code portion) and necessary documentation for your ask. (what is the 7 in 1 soil sensor you are talking about, how is it wired to your Arduino, which Arduino, ...)

help us help you..

I will rephrase my question. I am working on 7 in 1 soil moisture sensor and trying to get its data.

The code I am attaching is just used for seeing that whether the sensor is responding or not.

// This attempt uses the SoftwareSerial & raw Modbus packets.
//
// RS485 module wired up as:
// RS485 DI signal to pin 3
// RS485 RO signal to pin 2
// RS485 RE signal to pin 8
// RS485 DE signal to pin 7
// RS485 VCC to 5V
// RS485 GND to GND
//
#include <SoftwareSerial.h>

#define RE 2
#define DE 3

const uint32_t TIMEOUT = 500UL;

// canned message to your RS485 device
// change this for any other canned modbus message you like
// remember to make sure that the checksum is correct!
uint8_t msg[] = {0x01, 0x03, 0x00, 0x12, 0x00, 0x02, 0x64, 0x0E};
uint8_t rxByte;

SoftwareSerial swSerial(0, 1); // Receive (data in) pin, Transmit (data out) pin

void setup() {
Serial.begin(4800);
swSerial.begin(4800);
pinMode(RE, OUTPUT);
pinMode(DE, OUTPUT);
digitalWrite(DE, LOW);
digitalWrite(RE, LOW);
delay(1000);
}

void loop() {
uint32_t startTime = 0;

Serial.print("TX: ");
printHexMessage( msg, sizeof(msg) );

// send the command
digitalWrite(DE, HIGH);
digitalWrite(RE, HIGH);
delay( 10 );
swSerial.write( msg, sizeof(msg) );
swSerial.flush();
digitalWrite(DE, LOW);
digitalWrite(RE, LOW);

Serial.print("RX: ");

// read any data received and print it out
startTime = millis();
while ( millis() - startTime <= TIMEOUT ) {
if (swSerial.available()) {
printHexByte(swSerial.read());
}
}
Serial.println();
delay(2000);
}

void printHexMessage( uint8_t values[], uint8_t sz ) {
for (uint8_t i = 0; i < sz; i++) {
printHexByte( values[i] );
}
Serial.println();
}

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

When I am running this code I am only having TX values and the RX is empty. This mean that my sensor isn't responding I just don't what wrong I am doing. I am using Arduino Uno, RS485 and 7 in soil moisture.

I am attaching my circuit picture, and the output.
circuit pic 1

circuit pic 2

output that I am receiving
image

Please help me out on this

you are using an Arduino UNO, pin 0 and 1 are used for Serial, so you should not use them for discussing with the sensor

move the wires to 4 and 5 and use

SoftwareSerial swSerial(4,5); // Receive (data in) pin, Transmit (data out) pin

pin 4 of your uno will be the Rx pin, so should be connected to Tx pin of the sensor
pin 5 of your uno will be the Tx pin, so should be connected to Rx pin of the sensor

then use

  Serial.begin(115200);

and open your serial monitor at 115200 bauds


what do you have on pin 2 and 3 ??

Since I am using RS485 so we have pins you can see here in this link:

RS-485 MODBUS Serial Communication with Arduino as Master.

I made the changes in the code so now my new code is

// This attempt uses the SoftwareSerial & raw Modbus packets.
//
// RS485 module wired up as:
// RS485 DI signal to pin 5
// RS485 RO signal to pin 4
// RS485 RE signal to pin 2
// RS485 DE signal to pin 3
// RS485 VCC to 5V
// RS485 GND to GND
//
#include <SoftwareSerial.h>

#define RE 2
#define DE 3

const uint32_t TIMEOUT = 500UL;

// canned message to your RS485 device
// change this for any other canned modbus message you like
// remember to make sure that the checksum is correct!
uint8_t msg[] = {0x01, 0x03, 0x00, 0x13, 0x00, 0x01, 0x75, 0xCF};
uint8_t rxByte;

SoftwareSerial swSerial(4, 5); // Receive (data in) pin, Transmit (data out) pin

void setup() {
Serial.begin(115200);
swSerial.begin(115200);
pinMode(RE, OUTPUT);
pinMode(DE, OUTPUT);
digitalWrite(DE, LOW);
digitalWrite(RE, LOW);
delay(1000);
}

void loop() {
uint32_t startTime = 0;

Serial.print("TX: ");
printHexMessage( msg, sizeof(msg) );

// send the command
digitalWrite(DE, HIGH);
digitalWrite(RE, HIGH);
delay( 10 );
swSerial.write( msg, sizeof(msg) );
swSerial.flush();
digitalWrite(DE, LOW);
digitalWrite(RE, LOW);

Serial.print("RX: ");

// read any data received and print it out
startTime = millis();
while ( millis() - startTime <= TIMEOUT ) {
if (swSerial.available()) {
printHexByte(swSerial.read());
}
}
Serial.println();
delay(2000);
}

void printHexMessage( uint8_t values[], uint8_t sz ) {
for (uint8_t i = 0; i < sz; i++) {
printHexByte( values[i] );
}
Serial.println();
}

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

Still I am getting the same result

  • DI (Data In) ➜ The DI pin is connected to Tx pin of Host Microcontroller UART.
  • RO (Receive Out) ➜ The RO pin is connected to Rx pin of microcontroller.
  • RE (Receive Enable) used to configure the module in Receive Mode.
  • DE (Data Enable) used to Configure the module in Transmit Mode

as you connected DI to 5 and RO to 4 this is fine

SoftwareSerial swSerial(4, 5); // Receive (data in) pin, Transmit (data out) pin

your baud rate though as been set to 115200 for the RS485 line. it probably should be 4800 (to be checked) as you had in the first sketch

try something like this

// This attempt uses the SoftwareSerial & raw Modbus packets.
//
// RS485 module wired up as:
// RS485 DI signal to pin 5
// RS485 RO signal to pin 4
// RS485 RE signal to pin 2
// RS485 DE signal to pin 3
// RS485 VCC to 5V
// RS485 GND to GND
//
#include <SoftwareSerial.h>

const byte DI = 5;
const byte RO = 4;
const byte DE = 3;
const byte RE = 2;

const uint32_t TIMEOUT = 500UL;

// canned message to your RS485 device
// change this for any other canned modbus message you like
// remember to make sure that the checksum is correct!
uint8_t msg[] = {0x01, 0x03, 0x00, 0x13, 0x00, 0x01, 0x75, 0xCF};
uint8_t rxByte;

SoftwareSerial swSerial(RO, DI); // Receive (data in) pin, Transmit (data out) pin


void printHexByte(byte b)
{
  if (b < 0x10) Serial.write('0');
  Serial.print(b, HEX);
  Serial.print(' ');
}

void printHexMessage(const uint8_t * payload, size_t payloadSize) {
  for (size_t i = 0; i < payloadSize; i++) printHexByte( payload[i] );
  Serial.println();
}


void sendCommand(const uint8_t * payload, size_t payloadSize) {
  // send the command
  digitalWrite(DE, HIGH);
  digitalWrite(RE, HIGH);
  delay( 10 );
  swSerial.write( payload, payloadSize);
  swSerial.flush();
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

}

void setup() {
  pinMode(RE, OUTPUT);
  pinMode(DE, OUTPUT);
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

  Serial.begin(115200);
  swSerial.begin(4800); // SELECT THE RIGHT BAUD RATE
  delay(1000);
}

void loop() {
  uint32_t startTime = 0;

  Serial.print("TX: ");  printHexMessage( msg, sizeof msg);

  sendCommand(msg, sizeof msg);

  Serial.print("RX: ");

  // read any data received and print it out
  startTime = millis();
  while ( millis() - startTime <= TIMEOUT ) {
    if (swSerial.available()) {
      printHexByte(swSerial.read());
    }
  }
  Serial.println();
  delay(2000);
}


@areebkhan02 I see you found some of my early code (or a variant of it) that I generated whilst trying to help other users of NPK sensors.

Your sensor will use either 9600 baud (pretty common) or 4800 baud. Generally the NPK sensor has a default device address of 01, so that part of your canned modbus message is correct.

Do you have the user manual for your NPK sensor? If you don't, then it's guesswork as to what soil parameters are in what registers.

The canned modbus message is asking the sensor with device address 01 for 2 registers at register address 0x12 and 0x13, which would normally return 4 bytes of data as part of the response.

If your particular NPK sensor doesn't have registers at these addresses, then it will likely not respond and you will see nothing printed for the raw RX values.

still I am getting the same result. I think there is fault in the registers which I am addressing

@markd833 seems to be onto something esp. if you used his code

Thankyou Markd833 for replying. So regarding the user manual. I just don't know what user manual is correct for this sensor, since my university bought it for my project. I tried contacting JHXCT for where I ordered the sensor. They said to me to contact the local buyer. I am attaching the box in which it came, incase other people can identify what is the correct manual for it. Currently I am using the manual which was released on 2020 and the register values are set according to it.

Ok, well this is a very long shot and prone to an epic number of errors but here goes!

I took your box image and cropped / rotated it and fed it into a simplified chinese OCR online app. That generated a plain text file of Chinese characters that I fed into google translate one line at a time. This is what came out:

Swallow atmospheric environment monitoring
Agricultural parameter monitoring
Heng Industrial Gas Detection
Professional provider of industrial IoT sensors and solutions
Xinsuda ⑩Safe Ginseng and Peace of Mind
High precision || negotiable sensor

Unfortunately of little value I think.

Now, here's another long shot. I've just pulled this bit of code together and done basic testing of it so don't expect too much!

The code sends out a canned Modbus query to device address 01 asking for the contents of register addresses 0x00 up to 0x2F (0 to 47) and then repeats.

I have a specific hardware setup so I use AltSoftSerial which must use pins 8 & 9 on the UNO.

Have a read of the code and get the CRC16 and AltSoftSerial libraries if you don't have them. Or you can stick with SoftwareSerial and change the code accordingly to match your setup.

Hopefully you will get a response from some registers. If you don't, then try changing the software serial port baud rate to 4800 baud and running the code again.

// Attempt to access a JXCT type NPK sensor to read registers 0x00 to 0x2F
// to see if there is any response from the NPK sensor.
//
// This attempt uses the AltSoftSerial & raw Modbus packets.
// Get AltSoftSerial at https://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
//
// Also requires the CRC library by Rob Tillaart - install it via Library Manager
//
// RS485 module wired up as:
// RS485 DI signal to pin 9
// RS485 RO signal to pin 8
// RS485 RE signal to pin 7
// RS485 DE signal to pin 6
// RS485 VCC to 5V
// RS485 GND to GND
//

#include <AltSoftSerial.h>
#include "CRC16.h"

#define RE 6
#define DE 10

const uint32_t TIMEOUT = 500UL;

uint8_t msg[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00};
uint8_t values[11];
uint8_t regAddr = 0;

AltSoftSerial swSerial;

CRC16 crc(CRC16_MODBUS_POLYNOME,
          CRC16_MODBUS_INITIAL,
          CRC16_MODBUS_XOR_OUT,
          CRC16_MODBUS_REV_IN,
          CRC16_MODBUS_REV_OUT);
          
void setup() {
  Serial.begin(9600);
  swSerial.begin(9600);
  pinMode(RE, OUTPUT);
  pinMode(DE, OUTPUT);
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

  Serial.println("\n\nModbus NPK register scanner.\n\n");
  delay(1000);
}

void loop() {
  uint32_t startTime = 0;
  uint16_t crcValue = 0;
  
  // insert the register address
  msg[ 3 ] = regAddr++;

  // generate the CRC16 Modbus checksum
  crc.restart();
  for (uint8_t i=0; i<sizeof( msg )-2; i++ ) {
    crc.add( msg[i] );
  }
  crcValue = crc.calc();

  // and insert it into the test message
  msg[ sizeof(msg)-2 ] = (crcValue & 0xFF);
  msg[ sizeof(msg)-1 ] = ((crcValue >> 8) & 0xFF);
  
  Serial.print("TX: ");
  printHexMessage( msg, sizeof(msg) );

  // send the command
  digitalWrite(DE, HIGH);
  digitalWrite(RE, HIGH);
  delay( 10 );
  swSerial.write( msg, sizeof(msg) );
  swSerial.flush();
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

  Serial.print("RX: ");
  
  // read any data received and print it out
  startTime = millis();
  while ( millis() - startTime <= TIMEOUT ) {
    if (swSerial.available()) {
      printHexByte(swSerial.read());
    }
  }
  Serial.println();
  delay(500);

  // reset register address back to 0x00 once we reach address 48
  if ( regAddr > 0x2F ) {
    regAddr = 0;
    Serial.println("\n\n\Rescan from register address 0x00 again in 5 seconds.\n\n");
    delay( 5000 );
  }
}

void printHexMessage( uint8_t values[], uint8_t sz ) {
  for (uint8_t i = 0; i < sz; i++) {
    printHexByte( values[i] );
  }
  Serial.print("  -   ");
}

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

Thanks Markd833 for your response.

I tried above what you said. Let me attach some pics for what i did
pic 1:


pic2:

I tried your code. In explanation you have written RE to 7 and DE to 6 but in code its RE 6 and DE 10. I tried both. but both the cases didn't work. I am attaching what results I am getting

The code I used is

// Attempt to access a JXCT type NPK sensor to read registers 0x00 to 0x2F
// to see if there is any response from the NPK sensor.
//
// This attempt uses the AltSoftSerial & raw Modbus packets.
// Get AltSoftSerial at https://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
//
// Also requires the CRC library by Rob Tillaart - install it via Library Manager
//
// RS485 module wired up as:
// RS485 DI signal to pin 9
// RS485 RO signal to pin 8
// RS485 RE signal to pin 7
// RS485 DE signal to pin 6
// RS485 VCC to 5V
// RS485 GND to GND
//

#include <AltSoftSerial.h>
#include "CRC16.h"

#define RE 7
#define DE 6

const uint32_t TIMEOUT = 500UL;

uint8_t msg[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00};
uint8_t values[11];
uint8_t regAddr = 0;

AltSoftSerial swSerial;

CRC16 crc(CRC16_MODBUS_POLYNOME,
          CRC16_MODBUS_INITIAL,
          CRC16_MODBUS_XOR_OUT,
          CRC16_MODBUS_REV_IN,
          CRC16_MODBUS_REV_OUT);
          
void setup() {
  Serial.begin(4800);
  swSerial.begin(4800);
  pinMode(RE, OUTPUT);
  pinMode(DE, OUTPUT);
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

  Serial.println("\n\nModbus NPK register scanner.\n\n");
  delay(1000);
}

void loop() {
  uint32_t startTime = 0;
  uint16_t crcValue = 0;
  
  // insert the register address
  msg[ 3 ] = regAddr++;

  // generate the CRC16 Modbus checksum
  crc.restart();
  for (uint8_t i=0; i<sizeof( msg )-2; i++ ) {
    crc.add( msg[i] );
  }
  crcValue = crc.calc();

  // and insert it into the test message
  msg[ sizeof(msg)-2 ] = (crcValue & 0xFF);
  msg[ sizeof(msg)-1 ] = ((crcValue >> 8) & 0xFF);
  
  Serial.print("TX: ");
  printHexMessage( msg, sizeof(msg) );

  // send the command
  digitalWrite(DE, HIGH);
  digitalWrite(RE, HIGH);
  delay( 10 );
  swSerial.write( msg, sizeof(msg) );
  swSerial.flush();
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

  Serial.print("RX: ");
  
  // read any data received and print it out
  startTime = millis();
  while ( millis() - startTime <= TIMEOUT ) {
    if (swSerial.available()) {
      printHexByte(swSerial.read());
    }
  }
  Serial.println();
  delay(500);

  // reset register address back to 0x00 once we reach address 48
  if ( regAddr > 0x2F ) {
    regAddr = 0;
    Serial.println("\n\n\Rescan from register address 0x00 again in 5 seconds.\n\n");
    delay( 5000 );
  }
}

void printHexMessage( uint8_t values[], uint8_t sz ) {
  for (uint8_t i = 0; i < sz; i++) {
    printHexByte( values[i] );
  }
  Serial.print("  -   ");
}

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

I can't get the sensor to respond back

Oops! Sorry about that. RE and DE can go to any unused pins. Change the code accordingly.

The 2 fixed pins are DO and RI, but that's only because the way the AltSoftSerial library works with the 328P hardware.

If you are using SoftwareSerial, then you can use other pins.

Re pic2, the rectangular 9v battery could be a possible issue. That type of battery is not much use in powering devices other than smoke alarms.

Is there any information printed on you actual sensor that would indicate its operating voltage range?

The sensor says that it will work on 5-30V
I checked the battery it is supplying 8.5 Volts so the sensor should receive the power

Ok, lets try something else to check out the basics.

  • Disconnect your sensor from the A & B inputs.
  • Take out the end of the RE wire from the RS485 module that goes to the UNO and connect it to GND so that the RS485 module RE pin now goes to GND.

What that will do is enable the receiver circuit in the MAX485.

Make sure you are running the code that uses the AltSoftSerial library - this test doesn't work if you are using the SoftwareSerial library.

Run my test code again.

What you should see is the RX output is now showing the data that the TX is producing. Basically the UNO is listening to itself transmitting.

i did what you said.
Here is my circuit pic

and the result that I am getting is essentially the same there is no change in it

the code that i am using is

// Attempt to access a JXCT type NPK sensor to read registers 0x00 to 0x2F
// to see if there is any response from the NPK sensor.
//
// This attempt uses the AltSoftSerial & raw Modbus packets.
// Get AltSoftSerial at https://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
//
// Also requires the CRC library by Rob Tillaart - install it via Library Manager
//
// RS485 module wired up as:
// RS485 DI signal to pin 9
// RS485 RO signal to pin 8
// RS485 RE signal to pin 7
// RS485 DE signal to pin 6
// RS485 VCC to 5V
// RS485 GND to GND
//

#include <AltSoftSerial.h>
#include "CRC16.h"
#define RE 6
#define DE 7

const uint32_t TIMEOUT = 500UL;

uint8_t msg[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00};
uint8_t values[11];
uint8_t regAddr = 0;

AltSoftSerial swSerial;

CRC16 crc(CRC16_MODBUS_POLYNOME,
          CRC16_MODBUS_INITIAL,
          CRC16_MODBUS_XOR_OUT,
          CRC16_MODBUS_REV_IN,
          CRC16_MODBUS_REV_OUT);
          
void setup() {
  Serial.begin(4800);
  swSerial.begin(4800);
  pinMode(RE, OUTPUT);
  pinMode(DE, OUTPUT);
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

  Serial.println("\n\nModbus NPK register scanner.\n\n");
  delay(1000);
}

void loop() {
  uint32_t startTime = 0;
  uint16_t crcValue = 0;
  
  // insert the register address
  msg[ 3 ] = regAddr++;

  // generate the CRC16 Modbus checksum
  crc.restart();
  for (uint8_t i=0; i<sizeof( msg )-2; i++ ) {
    crc.add( msg[i] );
  }
  crcValue = crc.calc();

  // and insert it into the test message
  msg[ sizeof(msg)-2 ] = (crcValue & 0xFF);
  msg[ sizeof(msg)-1 ] = ((crcValue >> 8) & 0xFF);
  
  Serial.print("TX: ");
  printHexMessage( msg, sizeof(msg) );

  // send the command
  digitalWrite(DE, HIGH);
  digitalWrite(RE, HIGH);
  delay( 10 );
  swSerial.write( msg, sizeof(msg) );
  swSerial.flush();
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);

  Serial.print("RX: ");
  
  // read any data received and print it out
  startTime = millis();
  while ( millis() - startTime <= TIMEOUT ) {
    if (swSerial.available()) {
      printHexByte(swSerial.read());
    }
  }
  Serial.println();
  delay(500);

  // reset register address back to 0x00 once we reach address 48
  if ( regAddr > 0x2F ) {
    regAddr = 0;
    Serial.println("\n\n\Rescan from register address 0x00 again in 5 seconds.\n\n");
    delay( 5000 );
  }
}

void printHexMessage( uint8_t values[], uint8_t sz ) {
  for (uint8_t i = 0; i < sz; i++) {
    printHexByte( values[i] );
  }
  Serial.print("  -   ");
}

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

is there a problem with my RS485, I have two of them both are giving me the same result.

Ok, lets go right back to basics.

  1. Disconnect all the wires from your UNO board.
  2. Connect a single wire between pin 8 and pin 9 on your UNO.
  3. Run the Modbus scan code.

I've done this on an UNO I have here and here's what I get:

Modbus NPK register scanner.


TX: 01 03 00 00 00 01 84 0A   -   RX: 01 03 00 00 00 01 84 0A 
TX: 01 03 00 01 00 01 D5 CA   -   RX: 01 03 00 01 00 01 D5 CA 
TX: 01 03 00 02 00 01 25 CA   -   RX: 01 03 00 02 00 01 25 CA 
TX: 01 03 00 03 00 01 74 0A   -   RX: 01 03 00 03 00 01 74 0A 
TX: 01 03 00 04 00 01 C5 CB   -   RX: 01 03 00 04 00 01 C5 CB 
TX: 01 03 00 05 00 01 94 0B   -   RX: 01 03 00 05 00 01 94 0B 
TX: 01 03 00 06 00 01 64 0B   -   RX: 01 03 00 06 00 01 64 0B 
TX: 01 03 00 07 00 01 35 CB   -   RX: 01 03 00 07 00 01 35 CB 
TX: 01 03 00 08 00 01 05 C8   -   RX: 01 03 00 08 00 01 05 C8 

Hopefully that works on your UNO too.

I did exactly what u said

  1. remove all the wires
  2. connect only a single wire between 8 and 9

This is my result same as yours

1 Like