RS485 Over Soil Sensor, unable to receive any readings

I'm working on a university capstone project and am running into some issues getting rs485 up and running.

the soil sensor has power to ground connectivity and so do all the pins coming off of the TTL Conversion board. I checked this with a multimeter

Components I am using:
Arduino UNO
DSD TECH SH-U12 RS485 to TTL 5V Board with MAX13487 Chip
https://docs.qq.com/doc/DUGtXbVhXZG1JckJq?newPad=1&newPadType=clone this is the link to the information. From amazon reviews I've read that there are cases where the TX/RX pins are reversed/the A and B Pins are reversed for the RS485 Side of things but I've tried that to no avail https://www.amazon.com/DSD-TECH-SH-U12-MAX13487-Raspberry/dp/B07B667STP/

5 Pin NPK Soil Sensor datasheet:https://cdck-file-uploads-europe1.s3.dualstack.eu-west-1.amazonaws.com/arduino/original/4X/d/f/5/df5ca5c3b195421786e97743713e97298e3eb638.pdf

https://www.amazon.com/Comprehensive-Corrosion-Resistant-Temperature-Conductivity/dp/B08M5PHQZ7/

This isn't the exact data sheet but the register mapping seem consistent across different manufacturers.

#include <SoftwareSerial.h>

SoftwareSerial mySerial(2, 3);  // RX, TX

void setup() {
  Serial.begin(9600);
  mySerial.begin(4800); // NPK communicates at 4800 bps
}

void loop()
{
  byte queryData[] {0x01,0x03,0x00,0x00,0x00,0x07,0x04,0x08};
  byte receivedData[19];
  mySerial.write(queryData,sizeof(queryData)); // send query data to NPK
  delay(1000);
 if (mySerial.available() >= sizeof(receivedData)) {  // Check if there are enough bytes available to read
    mySerial.readBytes(receivedData, sizeof(receivedData));  // Read the received data into the receivedData array

  // Print the received data in HEX format
for (int i = 0; i < sizeof(receivedData); i++) {
      Serial.print(receivedData[i], HEX);
      Serial.print(" ");
    }
    Serial.println();
  }
}

On this code I get an empty output. I tried changing around the TX/RX cables and the board changes from flashing RX to TX but its only ever just that one flash it never sends anything back and changes to TX/RX after sending the message.

I am using a 12 volt power supply to power the soil sensor it accepts 12-24V

Any advice on how to troubleshoot this further would be much appreciated

Please make and post schematics. Pen and paper surely works.

1 Like

Thanks.
It looks okey.
Looking at the code, nothing really upsetting.

The first link shows some lines from FAQ but advancing it turns into Chinese. I see now way to verify the trigger/command data You send.

I don't understand what You mean by "empty output". Try again using another expression.
Use some debug prints showing the number of bytes received waiting in the buffer in case it's not 19.

The Soil datasheet link has the register commands outlined in english along with the register layout. With respect to the faq it mainly outlines the inversion of tx/rx but doesn’t provide much more information.

Could You please reply to the rest of reply #4?

To aid debugging, I would start by displaying any bytes received, rather than waiting for a specific number of bytes.

Replace your mySerial.available with something like this:

int16_t rxByte;

if (mySerial.available() ) { 
  rxByte = mySerial.read();
  Serial.print(rxByte, HEX);
  Serial.print(" ");
}
Serial.println(".");
1 Like

I'm now running:

#include <SoftwareSerial.h>

SoftwareSerial mySerial(2, 3);  // RX, TX

void setup() {
  Serial.begin(9600);
  mySerial.begin(4800); // NPK communicates at 4800 bps
}

void loop()
{
  byte queryData[] {0x01,0x03,0x00,0x00,0x00,0x07,0x04,0x08};
  byte receivedData[19];
  mySerial.write(queryData,sizeof(queryData)); // send query data to NPK
  delay(1000);
  int16_t rxByte;
  if (mySerial.available() ) { 
    rxByte = mySerial.read();
    Serial.print(rxByte, HEX);
    Serial.print(" ");
  }
  Serial.println(".");
}

the output is pure:
.
.
.
.
.
.
.
.
.

I still get the same ... when i swap the tx and rx wires (swapping the 2,3 connections), the only change is that the RXD LED flashes in comparison to the TXD LED Flashing on the TTL-RS485 Driver

I also attempted to change the command I sent to an exact example from the datasheet however this still resulted in only the '.' being printed:

I believe this suggests that the serial is not being available ever which means nothing is coming back the other way? I would think that with half duplex that after sending a message the other direction LED should light up which is not happening.

I also tried changing the voltage to 24 volts since the soil sensor accepts 12-24V but there's no impact. I'm not sure if this is pertinent but it is drawing only 0.005 Amps ~0.12 watts

Correct. Your sensor isn't responding at all. Your wiring schematic looks ok.

Are you sure that the manual you have is for your exact sensor?

9600 baud is more common than 4800 baud. You could change your code for 9600 and retry.

A USB-RS485 dongle can be really useful in these situations. You can use it with the manufacturers own software (if they give it to you) or you can use it with one of the free modbus programs to experiment with messages to/from your sensor.

1 Like

Ok I think it seems that 9600 was the better baud rate. I’m now getting a periodic sequence of:
1 83 2 C0 F1 this is inconsistent with the expected return value that should contain more bytes than what’s coming out. This sequence repeats and then eventually just starts returning only 1 after the program has been running for some tome.

From scouring multiple data sheets it seems that for the 4 pin soil sensor they all have the same register mapping and command structure, but their baud rate factory defaults appear to be different. I’m going to sit down and attempt to decode the value against the data sheet now but I’m unsure if this data will yield any useful information since the data sheet outlines a return of 13 bytes for the command. When I call a different command that reads more registers I end up seeing the same sequence of 1 83 2 C0 F1. Would this incomplete response frame have to do with the delay on my arduino causing dropped bytes or an incomplete frame or is this more likely to be due to the sensor?

That sequence is useful to know.

Firstly, now you are using 9600 baud, your sensor is responding.

Secondly, that sequence of bytes is a valid and complete modbus message. It decodes like this:

01 - the address of your sensor
83 - is the sensor reporting an error against function code 03
02 - is the error code
C0 - CRC16 LSB
F1 - CRC16 MSB

That would suggest that the user manual you have is not the one for your sensor.

Try this just to see if it works:

byte queryData[] {0x01,0x03,0x00,0x01,0x00,0x01,0xD5,0xCA};

It should ask your sensor for just temperature. It's a guess as to which register holds temperature but the manual - assuming it's at least partially correct - says that temperature is in register 0001.

Hopefully you will get a response that starts 01 03 which indicates that the sensor understands that request.

The problem I see is that if the user manual is incorrect, then you could be guessing what registers are supported and what parameters are held in each register.

EDIT: I've just checked my spreadsheet of various soil sensor configurations and temperature can appear in registers 0x0000, 0x0001 or 0x0013.

You could try each of these messages and see what your sensor responds with:

byte queryData[] {0x01,0x03,0x00,0x00,0x00,0x01,0x84,0x0A};  // Temp in register $0000
byte queryData[] {0x01,0x03,0x00,0x01,0x00,0x01,0xD5,0xCA};  // Temp in register $0001
byte queryData[] {0x01,0x03,0x00,0x13,0x00,0x01,0x75,0xCF};  // Temp in register $0013

In each case, you are looking for a response that is:

01 03 02 aa bb cc dd

aa & bb are the upper (aa) and lower (bb) bytes of a 16-bit word that should hold the temperature and cc & dd are the CRC16 checksum of the message.

You need to combine aa and bb to get the 16-bit hex word $aabb. Temperature is usually reported back by the sensor with a value 10x bigger than the actual temperature so that the end user would divide it by 10 to get temperature to 1 decimal place.

For example, for 25 degC I would expect to see a value of 250 = $00FA and for 27.5 degC I would expect a value of 275 = $0113.

Thank you very much for helping decode that.

It's unfortunately returning the same with the command you provided. I think it might be an issue of the sensor itself after finding an amazon review that appeared to encounter the same 5 bytes issue. Would it be worthwhile just bruteforcing the entire possible candidate address space of registers to confirm that the sensor is faulty or would these 3 command failures provide sufficient information to imply that it is broken.

1 .

83 .

2 .

C0 .

F1 .

I

Here's a bit of code I wrote to scan through registers 0 to 47 using function code 03 (i.e. Read Holding Registers). It uses an RS485 module with manual direction control. However, in your case I think you can simply leave the code as 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 SoftwareSerial & raw Modbus packets.
//
// Also requires the CRC library by Rob Tillaart - install it via Library Manager
//

#include <SoftwareSerial.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;
uint8_t devAddr = 0x01;

SoftwareSerial swSerial(2, 3);  // RX, TX

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 device address
  msg[ 0 ] = devAddr;
  
  // 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( 5 );
  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 2 seconds.\n\n");
    delay( 2000 );
  }
}

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(' ');
}

Post the results so we can see what your sensor responds with.

1 Like

My ttl-rs485 only has tx,rx,gnd and 5v. Do i want to still keep the RE, DE pins? Or should i modify that logic.

You can leave the code as is. If your wiring connections are as per the diagram in post #3, then the code will run just fine without connecting anything to the pins designated for RE & DE.

This is what it looks like upon running it. The data at the end looks more promising however the frames are only 7 bytes. Are these modbus frames non errors?


here is a more complete screenshot. These appear to be like more correct data frames however they are in conflict with various datasheets i've found online.

That looks promising. They are all correct modbus responses. You see 7 bytes because the request (TX message) is only asking for one register at a time.

Any RX frames that start 01 83 mean the sensor doesn't use the register in the TX message.

However, we're interested in the RX frames that start 01 03. Those ones mean the sensor supports that register address.

Register addresses $04 to $0E (14 dec) have got data as have register addresses $12 (18 Dec) to $15 (21 Dec).

I checked my spreadsheet for register mapping and I don't have an NPK sensor recorded that has a block of registers from $04 to $0E.

As a complete guess, register $0A may be temperature - that would decide to 35 degC. However, register $13 might be temperature too. That would decode to 20.1 degC.

I would ask the seller for the correct manual and if they can't provide it, return the sensor for a refund.

i am not getting any responses from the sensor even if i put the same code

So you probably have hardware issue, like incorrect wiring...

1 Like

Or your sensor uses different register addresses, or sometimes the sensor is configured for 4800 baud. Occasionally the sensor address isn't 01. What does your user manual say?