Modbus: NPK-Sensor does not respond at all

First of all, I'm new to this forum and I'm new to arduino and electronics too. I might miss some very basic things that experienced people can spot within seconds.

I recently bought two NPK sensors on Amazon and I'm struggling to understand how they work. I followed several tutorials to make them run with an arduino, but unlike most other people here I just don't get any response at all. Although it seemed like getting back an FFFFFF, checking it with mySerial.available() reveals that there is no data to read and the FFFFFF seem to be the absence of incoming data (if I interpret this correctly).

My goal is to build a handheld device with battery, maybe a screen (optional) and Bluetooth to read the sensor data from an android device. And over time I would improve an initially very simple app to facilitate making protocols and statistics of the measurements done on various plants.
However, as a first step I would need the sensor to give me some data. But unfortunately none of the sensors do respond.

What I tried so far:

  • following the tutorial on electronicclinic.com.
    • I just realized that I forgot the two 470uf decoupling capacitors. If will order some and try again.
  • two arduino uno with a MAX485 each, connected through modbus to test the modbus connection.
    • Result: The slave is getting the inquiry frame sent by the master correctly. But if I try to send the received data back to the master there seems to be a loop between the two, sending more and more unwanted data back and forth. But if I leave the slave in the receiving state all frames are received correctly without further noises.
  • Connect it to a USB-Dongle and try to send a inquiry frame from the PC with different Modbus monitoring software, but without any response from the sensors. I realized that all of the monitoring software did calculate a different checksum to what the sensor manual mentions.
  • Using WinModbus to simulate a working sensor/slave (thanks to @markd833 again)
    • As it turns out WinModbus did not accept the CRC numbers provided by the manuals of the sensors. I then implemented a function to calculate the CRC automatically and communication between Arduino as master and WinModbus as slave does work as expected.

I should mention that only one of my arduino uno seems to be original, the others have a different chip for which I had to install an extra driver first. But I imagine this shouldn't be the problem in my case.

Questions:

  • Does anybody have a Link to a software that is meant to work with these sensors? I saw in another Topic that @HPsunil got such a software from the seller. Unfortunately he did not respond to my request for a download link so far. I also did write to JXCT an hour ago, asking for a download link and I'm waiting for a response.
  • Do I need terminal resistors in the modbus when using MAX485? (Tutorials on the internet do not use them and it seems to work). And if yes, where to put them exactly?
  • Is there only one way to calculate the CRC-Code for Modbus communication? Or would the manufacturer be free to implement any other calculation to get a different CRC than the one expected by common Modbus monitoring software?
  • any other suggestions what else I could try to test if the sensors do work correctly?

TODO

  • Order decoupling capacitors and try again
  • Get the original sensor monitoring software and test if sensors respond that way.
  • Setting up another Arduino to act as a listener/man in the middle (thanks for this suggestion @markd833)

I'm happy to provide more details if needed. I'm sure I forgot something.

Some additional information about Modbus Communication Protocol described in the manual.

4.1 Communication Basic Parameters
Coding: 8-bit binary
Data bit: 8-bit
Parity bit: no
Error check: CRC(redundant cyclic code)
BAUD RATE: 2400bps/4800bps/9600bps can be set, the factory default is 9600bps

4.2 Data frame format definition
Adopt Modbus-RTU communication protocol, the format is as follows:
Initial structure ≥ 4 bytes of time
Address code = 1 byte (factory default value is 0x01)
Function Code = 1 byte (use 0x03 to read data from the sensor)
Data area = N bytes (note that the 16-bit high byte is in front)
Error check = 16-bit RCR
Ending structure ≥ 4 bytes of time


picture by electronicclinics[.]com, a more detailed version is found here.

@errorntrial the project on Electronic Clinic looks like a modified version of the code talked about in the MAX485 TTL to RS-485 Modules for soil NPK sensor discussion.

I've uploaded a piece of code to that thread. I've tested it using WinModbus and it seems to work correctly with the correct responses going to the correct measurement types.

I don't have the actual hardware to test on, but fingers crossed it works :grinning:

ok, using WinModbus was a good idea. This way I found out that WinModbus does not accept the CRC provided by the manuals.

Here are the inquiry frames provided by the manuals:

Address, Function, Register start address (2 bytes), Register length (2 bytes), CRC_L, CRC_H
0x01, 0x03, 0x00 0x12, 0x00 0x02, 0x65, 0xCB (temp and humidity)
0x01, 0x03, 0x00 0x12, 0x00 0x01, 0x25, 0xCA (moisture (guess humidity too))
0x01, 0x03, 0x00 0x15, 0x00 0x01, 0x95, 0xCE (EC)
0x01, 0x03, 0x00 0x06, 0x00 0x01, 0x64, 0x0B (PH)
0x01, 0x03, 0x00 0x1E, 0x00 0x03, 0x34, 0x0D (NPK)
0x01, 0x03, 0x00 0x1E, 0x00 0x01, 0xB5, 0xCC (Nitrogen)
0x01, 0x03, 0x00 0x1F, 0x00 0x01, 0xE4, 0x0C (Phosphorus)
0x01, 0x03, 0x00 0x20, 0x00 0x01, 0x85, 0xC0 (Potassium)

I am surprised by the fact that both of the manuals do provide exactly the same crc per inquiry frame. The manuals have different version numbers (Ver1.1 and Ver 2.0) and I thought they are from different brands too.
It makes me wonder if there are different methods to calculate the CRC?

However, I then implemented a function to automatically calculate the CRC for the inquiry frames. This worked for the interaction between Arduino as master and WinModbus as slave.
Here is the code of the function, copied and adapted from here:

// Compute the MODBUS RTU CRC
uint16_t ModRTU_CRC(byte buf[], int len) {
  uint16_t crc = 0xFFFF;
  
  for (int pos = 0; pos < len; pos++) {
    crc ^= (uint16_t)buf[pos];          // XOR byte into least sig. byte of crc
  
    for (int i = 8; i != 0; i--) {    // Loop over each bit
      if ((crc & 0x0001) != 0) {      // If the LSB is set
        crc >>= 1;                    // Shift right and XOR 0xA001
        crc ^= 0xA001;
      }
      else                            // Else LSB is not set
        crc >>= 1;                    // Just shift right
    }
  }
  // Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes)
  Serial.println(crc, HEX);
  return crc;  
}

and later in loop() I used the following code to get the resulting CRC into the inquiry_frame:

    int crc = ModRTU_CRC(inquiry_frame, 6);
    inquiry_frame[6] = (crc & 0xFF);
    inquiry_frame[7] = ((crc >> 8) & 0xFF);

So, now I'm one step further. I found out about the (probably) wrong CRC in both manuals of the sensors. And the CRC is getting calculated automatically from now on.

But still, the sensors do not respond to any inquiry frame.

I also already tried to use CAS Modbus Scanner to scan the modbus in case that the device ID might be different to the factory default. But scanning the modbus did not help, none of the sensors did respond. (at least not at 9600bps).
And I also tried to send the inquiry frames with CAS Modbus Scanner, but it keeps telling "invalid data, click poll".

However, I can't imagine that both of the sensors do not work. The best thing that comes to my mind is to get the software from the manufacturer to check if the sensors do work.

Or any other Ideas around?

Did the code I posted in the other thread work with your sensor?

@markd833
Well, I actually did not try your code because I saw that my own code was working as expected when using WinModbus to simulate a working sensor.

Today I got the Sensor Monitoring Software from the customer support of JXCT and as it turns out both of the sensors do not respond to it. So my guess is that there are some Scammers selling broken Sensors on Amazon. Or I had really bad luck to coincidentally buy two different sensors from two different sellers and both are broken.
But again, the user manuals even provided erroneous CRC codes. And looking at the manual again I see another funny detail: the "unblocked" word. Did they just use Tipex to overwrite the company name of a legit manufacturer? :see_no_evil:

Well, I'm done with this project for now. I will send both sensors back to Amazon and hope to get a refund.

Thanks a lot for your dedication and help @markd833 ... the idea to simulate a working sensor with WinModbus was really helpful to understand that there was nothing wrong with my code. (In terms of functionality, not clean code :sweat_smile: )

That said, maybe somebody finds my code helpful to track down sensor problems. It should be working with all kinds of soil sensors. (NPK-Sensor, Nitrogen Sensor, Phosphorus Sensor, Potassium Sensor, EC Sensor, PH Sensor, Moisture Sensor, Temperature Sensor).
The usage is simple. Just type a number (or several) from 0 to 7 into the serial Monitor and you get some information about what is getting sent and what is getting received.
Alternatively there's a standalone Mode which sends a specific frame every 5 seconds.

#include <ArduinoRS485.h>
#include <SoftwareSerial.h>

const int PH = 0;
const int MOISTURE = 1;
const int TEMP = 2;
const int EC = 3;
const int NITROGEN = 4;
const int PHOSPHORUS = 5;
const int POTASSIUM = 6;
const int NPK = 7;

const int ledPin =  13;   // Built-in LED
const int DI = 4;         // pins to connect DI, DE, RE and RO of the MAX485 module
const int DE = 5;
const int RE = 6;
const int RO = 7;
const boolean standaloneMode = false;     // standaloneMode does send an inquiry frame every 4.8 seconds
const int standaloneFrame = NITROGEN;     // defines which frame is getting sent in standalone mode
const byte sensorID = 0x01;
const byte function = 0x03;               // 0x03 means reading from register. Other functions are not needed to read the sensor

byte inquiry_frame[8];      //global variable used by different functions. I did not take the time to figgure out how arrays are passed to functions properly in C language.
const byte inquiry_frames[10][8] = {
  //CRC according to manual. Will get overwritten later in the code to fix wrong CRC numbers
  {sensorID,function, 0x00, 0x06, 0x00, 0x01, 0x64, 0x0B}, //0: PH 1,3,0,6,0,1,228,12
  {sensorID,function, 0x00, 0x12, 0x00, 0x01, 0x25, 0xCA}, //1: Moisture
  {sensorID,function, 0x00, 0x13, 0x00, 0x01, 0x75, 0xcf}, //2: Temp
  {sensorID,function, 0x00, 0x15, 0x00, 0x01, 0x95, 0xce}, //3: EC
  {sensorID,function, 0x00, 0x1e, 0x00, 0x01, 0xB5, 0xCC}, //4: Nitrogen  (N)
  {sensorID,function, 0x00, 0x1f, 0x00, 0x01, 0xE4, 0x0C}, //5: Phosphorus(P)
  {sensorID,function, 0x00, 0x20, 0x00, 0x01, 0x85, 0xc0}, //6: Potassium (K)
  {sensorID,function, 0x00, 0x1e, 0x00, 0x03, 0x34, 0x0D}, //7: NPK... would give back 6 bytes
  {sensorID,function, 0x09, 0x06, 0x09, 0x01, 0x61, 0xc7}, //8: not valid, test frame to avoid bytes with zeros (0x00)
  {sensorID,function, 0x00, 0x12, 0x00, 0x02, 0x65, 0xCB}  //9: Temperature and Humidity
  /* //Correct CRC
  {sensorID,function, 0x00, 0x06, 0x00, 0x01, 0x0b, 0x64}, //0: PH
  {sensorID,function, 0x00, 0x12, 0x00, 0x01, 0x24, 0x0f}, //1: Moisture
  {sensorID,function, 0x00, 0x13, 0x00, 0x01, 0x75, 0xcf}, //2: Temp
  {sensorID,function, 0x00, 0x15, 0x00, 0x01, 0x95, 0xce}, //3: EC
  {sensorID,function, 0x00, 0x1e, 0x00, 0x01, 0xe4, 0x0c}, //4: Nitrogen  (N)
  {sensorID,function, 0x00, 0x1f, 0x00, 0x01, 0xb5, 0xcc}, //5: Phosphorus(P)
  {sensorID,function, 0x00, 0x20, 0x00, 0x01, 0x85, 0xc0}, //6: Potassium (K)
  {sensorID,function, 0x00, 0x1e, 0x00, 0x03, 0x65, 0xcd}, //7: NPK... would give back 6 bytes
  {sensorID,function, 0x09, 0x06, 0x09, 0x01, 0x61, 0xc7}, //8: not valid, test frame to avoid bytes with zeros (0x00)
  {sensorID,function, 0x00, 0x12, 0x00, 0x02, 0x65, 0x0d}  //9: Temperature and Humidity 
  */
};

SoftwareSerial modbusSerial(RO,DI); 

void setReceive(){
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);
}
void setTransmit(){
  digitalWrite(DE, HIGH);
  digitalWrite(RE, HIGH);
}
void blinking(int t, int count){
  for(int i = 0;i < count; i++){
    digitalWrite(ledPin, HIGH);
    delay(t);
    digitalWrite(ledPin, LOW);
    delay(t);
  }
}

// Compute the MODBUS RTU CRC
uint16_t ModRTU_CRC(byte buf[], int len) {
  uint16_t crc = 0xFFFF;
  
  for (int pos = 0; pos < len; pos++) {
    crc ^= (uint16_t)buf[pos];          // XOR byte into least sig. byte of crc
  
    for (int i = 8; i != 0; i--) {    // Loop over each bit
      if ((crc & 0x0001) != 0) {      // If the LSB is set
        crc >>= 1;                    // Shift right and XOR 0xA001
        crc ^= 0xA001;
      }
      else                            // Else LSB is not set
        crc >>= 1;                    // Just shift right
    }
  }
  // Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes)
  //{Serial.println(crc, HEX);
  return crc;  
}

void prepareInquiryFrame(int frameNumber){
    setTransmit();
    for(int i = 0;i<8;i++){ 
      inquiry_frame[i] = inquiry_frames[frameNumber][i];
    } 

    //calculating CRC
    int crc = ModRTU_CRC(inquiry_frame, 6);
    //extracting low byte and high byte
    inquiry_frame[6] = (crc & 0xFF);
    inquiry_frame[7] = ((crc >> 8) & 0xFF);

    setReceive();
}

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(DE, OUTPUT);
  pinMode(RE, OUTPUT);
  //setTransmit();
  setReceive();
  digitalWrite(ledPin, LOW);

  Serial.begin(9600); // Turn on the "main" serial port for debugging via USB Serial Monitor
  
  modbusSerial.begin(9600);
}

int count = 0;
void loop() {
  // receiving data
  if(Serial.available()){
    blinking(300,1);
    byte input = Serial.read();
    boolean sendFrame = true;
    int loops = 8;

    //getting inquiry Frame blinks twice slow, or blink fast 10x on unusable inputs
    if(input>='0' && input<='9'){
      prepareInquiryFrame(input-48); 
      blinking(300,2);
    } else {
      blinking(50,10);
      sendFrame = false;
    }

    Serial.println("-----------------------------------");
    Serial.print("Inquiry Frame Number: ");
    Serial.println((char)input);    Serial.println();
    Serial.println("Complete Inquiry Frame:");
    for(int i = 0; i<8; i++){
      Serial.print(inquiry_frame[i], HEX);
      Serial.print(", ");
    }
    Serial.println();
    Serial.print("Calculated CRC: ");
    Serial.print(inquiry_frame[6], HEX);
    Serial.print(" ");
    Serial.println(inquiry_frame[7], HEX);
    
    setTransmit();
    if(sendFrame){
      //Send inquiry frame
      modbusSerial.write(inquiry_frame, sizeof(inquiry_frame));
      modbusSerial.flush();
    } else { 
      modbusSerial.write(input);
      modbusSerial.flush();
    }
    setReceive();
  } else if(modbusSerial.available()){
    char input[100];
    blinking(100,2);
    Serial.println();
    Serial.println("modbusSerial.available(): "+String(modbusSerial.available()));
    modbusSerial.readBytes(input, 100);
    Serial.println("strlen(): "+String(strlen(input)));
    Serial.print("data: ");
    for (int i = 0; i<strlen(input); i++){
      Serial.print(input[i], HEX);
      Serial.print(", ");
    }
    Serial.println();
    Serial.flush();
    //empty buffer of modbusSerial
    while(modbusSerial.available()){
      //delay(100);
    }
  } else if (standaloneMode){
    prepareInquiryFrame(TEMP);
    //Send inquiry frame
    modbusSerial.write(inquiry_frame, sizeof(inquiry_frame));
    modbusSerial.flush();
    blinking(300, 3);
    delay(3000);      //blinking 3 times plus delay sums up to 4.8 seconds
  } else {
    if(count>20 && !standaloneMode){
      Serial.print(".");
      count = 0;
    } else {
      count++;
    }
    digitalWrite(ledPin, LOW);
  }  
  Serial.flush();
  delay(50);
  count++;
}

If that software doesn't work, then I'd not waste any more time with them either! Like you say, the manual looks suspect too.

Can you please share that software link, so that many people who are working with those sensors can make a use of it. As it is very tedious for every person to mail to jxct and then getting the software.

Surprisingly I am also working on the same project. But the difference is I started it on 25th Aug 2021. By gathering all the needed components like the NPK Sensor, RS485 to USB connector (to check whether my sensor is working) and also the MAX485 TTL to RS485 Convertor (to connect between the Arduino and the NPK Sensor) and the Arduino also.

Basically to start with I am pretty new to all these microcontrollers like Arduino, Raspberry pi and the coding part. please forgive me if there is any typical basic thing I miss/ didn't know :sweat_smile:.

I have also gone through this tutorial from electronicclinic and also a tutorial from how2electronics and then finally used the code given by them i.e

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


#define RE 8
#define DE 7

//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(2,3);

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

void loop() {
 byte val1,val2,val3;
 val1 = nitrogen();
 delay(250);
 val2 = phosphorous();
 delay(250);
 val3 = potassium();
 delay(250);
 
 
 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];
}

This is similar but not exactly the same as I am not using the OLED display.

When run the code I found the exact similar response i.e getting the value FFFFFF and then getting 255 mg/kg irrespective of me powering the NPK Sensor.
I am pretty stuck right now regarding this. If possible Can anyone please guide me on how to proceed further i.e in finding/ helping me know

  1. Whether my sensor is working or not. (with providing some software like "Serial Monitoring Software" which is mentioned in the documentation manual, if there is.)

  2. What modification are needed in this code precisely specifying the need to change. (I will try to map all the comments/ reply's given by @errorntrial and @markd833 in two of the forums. Kudos to them :clap:, You guys have done a great job sharing your thoughts on this forum.)

  3. If there is a clear way/ procedure for implementing it using Raspberry Pi 4 ( found this link NPK Sensor Using Raspberry Pi Pico But it's using Raspberry PI Pico, I am not sure whether this code is giving the desired output) -- I hope you both @markd833 and @errorntrial should have tried using this (Not considering the un-usage of the Sensor) may be we could figure it out with it, as I am in the process of getting the python code right. (BTW not sure when will it be -- but trying all the ways possible.)

  4. What are the other methods of getting the values out of the NPK Sensor.

Hope for some reply from @markd833 and @errorntrial. Also thought of pointing it out that It would be great if one of you will summarize the work/effort you put in to one final reply and end it. So that It would be easier for me and also to other people who will refer this. :sweat_smile:

Finally a Hell lot of Thanks :handshake: :handshake: for both @markd833 and @errorntrial for sharing your views and progress in this forum, so that people like me would never feel stuck. :orange_heart:

Can you please share that software link, so that many people who are working with those sensors can make a use of it. As it is very tedious for every person to mail to jxct and then getting the software.

Here you go. Not sure how long it will be available.
Sensor Monitoring Software 485 V3.0

I suggest you try if your sensor works with the software and leave a feedback.

When I simulated the sensor on a PC using WinModbus, I found that I could get 0xFFFFFF. That turned out to be because the software serial port had not received a reply back. Just for the purposes of getting a correct response, I put a delay into the code between sending out the command and trying to read the response.

See post #24 in this discussion.

I've been thinking about this problem and I've put together a bit of code that runs on an Arduino UNO and uses the ModbusMaster library and the AltSoftSerial library. I simulated the sensor using the evaluation version of WinModbus and can receive and display the data correctly.

Here's the code:

// Attempt to access a JXCT NPK sensor to read Nitrogen, Potassium & Phosphorus
// values using an Arduino UNO clone.
//
// This attempt uses the AltSoftSerial & ModbusMaster libraries.
// Get AltSoftSerial at https://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
//
// 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
//
// NOTE: I do not have this sensor, so I simulated it using the evaluation version of WinModbus.

#include <ModbusMaster.h>
#include <AltSoftSerial.h>

#define MAX485_DE      6
#define MAX485_RE_NEG  7

AltSoftSerial swSerial;
ModbusMaster node;

void preTransmission()
{
  digitalWrite(MAX485_RE_NEG, 1);
  digitalWrite(MAX485_DE, 1);
}

void postTransmission()
{
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);
}

void setup() {
  Serial.begin( 9600 );

  pinMode(MAX485_RE_NEG, OUTPUT);
  pinMode(MAX485_DE, OUTPUT);
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);

  // Modbus communication runs at 9600 baud
  swSerial.begin(9600);

  // Modbus slave ID of NPK sensor is 1
  node.begin(1, swSerial);

  // Callbacks allow us to configure the RS485 transceiver correctly
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}

void loop() {
  uint8_t result;

  // NITROGEN
  result = node.readHoldingRegisters(0x1E, 1);
  if (result == node.ku8MBSuccess)
  {
    Serial.print("   Nitrogen: ");
    Serial.print(node.getResponseBuffer(0x0));
    Serial.println(" mg/kg");
  }

  // PHOSPHORUS
  result = node.readHoldingRegisters(0x1F, 1);
  if (result == node.ku8MBSuccess)
  {
    Serial.print("Phosphorous: ");
    Serial.print(node.getResponseBuffer(0x0));
    Serial.println(" mg/kg");
  }

  // POTASSIUM
  result = node.readHoldingRegisters(0x20, 1);
  if (result == node.ku8MBSuccess)
  {
    Serial.print("  Potassium: ");
    Serial.print(node.getResponseBuffer(0x0));
    Serial.println(" mg/kg");
  }
  Serial.println();
  delay(2000);
}

I suppose that this is the proper way to talk to the sensor as it is a Modbus device. See if it works for you with a real sensor.

Thanks a lot for sharing the software @errorntrial. So, Basically I connected the sensor which is also powered using 12v-500mA battery and connecting it USB convertor and installed a specific driver mentioned here in my laptop. And then used the software which you shared. I was observing Sent Data : 01 03 00 1e 00 01 e4 0c repeatedly i.e the inquiry frame for nitrogen. simply I am not receiving any response from the sensor.

So, I felt there might be 3 possible cases.

  1. There might be some powering issue with the sensor. (That might be the case why the sensor is not working) -- So, I ordered another charger adaptor. I'll check whether It will work with the new one or not and then proceed to the next point.

  2. There is a change that the USB Convertor is not working. So, I ordered another USB Convertor. Now after getting it I will try implementing this Video and then rule out if any of the USB Convertor is not working and then proceed to the next point.

  3. The Sensor is itself not working. (Might be a damaged one like you have mentioned earlier.) -- If that's the case then I'll see whether I am able to get the code right by using the WinModbus and then move on to the 5 probe Sensor 5 probe Sensor which I also have.

I'll update when I have done any progress in here. Once again Thanks @errorntrial

@markd833 Thanks for sharing the code. I'll definitely look into the coding part after the clarification of whether my sensor is working or not and then update you. I'm just going step by step slowly as I see there has been a lot which has been discussed by you both @markd833 and @errorntrial. As I am pretty new to all this, It's taking time to me to understand each comment and mapping it all. It would have been very helpful if there is a documentation of what all you have done while trying to implement the sensor. I am doing my progress documentation and will share it at the end. As it might be helpful for someone.

I've not seen any documentation on the sensor. All I've had to work with is the arrays of bytes defined at the start of the sketches I've seen. It's clear from them that they are the byte sequences of Modbus messages.

I've seen a couple of variations of the code, one with a display and one that prints to the serial console. There have been reports that neither of them seem to work correctly. Early discussions indicated that reading 1 parameter (e.g. Nitrogen) worked, but trying to read 2 different parameters didn't.

I saw something similar when I simulated the sensor, so I suspect that is more about how the sketch has implemented the communications rather than the sensor.

See if the modbus code works for you. If it does, then you have a known working starting point.

You could make a slave with an arduino to check if the modbus connection is working.
I'm not sure if the following code is working because I already put away my arduino stuff. But the intention is, that you connect an arduino as a slave to the computer and run the sensor monitoring software. It should send back everything that got received, and it also sends it to the serial monitor of Arduino IDE.
Additionally the arduino should blink twice slowly on receiving data and twice fast on every character sent back to the master.
That way you can check if the communication over modbus is working.

#include <ArduinoRS485.h>
#include <SoftwareSerial.h>

const int ledPin =  13;  // Built-in LED
const int DI = 4;
const int DE = 5;
const int RE = 6;
const int RO = 7;


SoftwareSerial modbusSerial(RO,DI);

void setReceive(){
  digitalWrite(DE, LOW);
  digitalWrite(RE, LOW);
}
void setTransmit(){
  digitalWrite(DE, HIGH);
  digitalWrite(RE, HIGH);
}
void blinking(int t, int count){
  for(int i = 0;i < count; i++){
    digitalWrite(ledPin, HIGH);
    delay(t);
    digitalWrite(ledPin, LOW);
    delay(t);
  }
}

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(DE, OUTPUT);
  pinMode(RE, OUTPUT);
  setReceive();
  digitalWrite(ledPin, LOW);
  
  Serial.begin(9600); // Turn on the "main" serial port for debugging via USB Serial Monitor
  
  modbusSerial.begin(9600);
  modbusSerial.setTimeout(1000);
}

void loop() {
  if(modbusSerial.available()){
    blinking(300, 2); //blinks twice slowly if there is data to read

    char input[128];
    modbusSerial.readBytes(input, 128);
    
    setTransmit();
    for (int i = 0; i<strlen(input); i++){
      Serial.print(input[i], HEX);
      Serial.print(", ");
      Serial.flush();
      modbusSerial.write(input[i]);
      blinking(50,2); //blinks twice fast on any character sent back to the master
    }
    delay(200);
    setReceive();
  } else {
    digitalWrite(ledPin, LOW);
  }
  delay(200);
}

So, sorry for the delay. I followed the 3 points which I mentioned above. i.e

  1. When I tried to change the charging adaptor, I was receiving the same output i.e The sensor is not responding using the software provided above. Then moved to the next possibility.

  2. When I changed the USB convertor also I was not getting any response from the sensor using the software provided above. Then moved to the next step. This finally gave us some clarity on the sensor being defective.

  3. So, To basically understand the complete Modbus Rtu Protocol which the NPK sensor uses, We brought a cheap Modbus Rtu Protocol based sensor i.e Temperature and Humidity Sensor which is working. When We tried the Software which you mentioned above i.e Sensor Monitoring Software 485 V3.0 Then I was getting some values, But was not able to interpret them into desired values. Then I found another software i.e hercules setup which was used here, then we were getting the desired values.

So, Then we started to understand how to get these desired values by using the code (we used python which run on command prompt and tried to communicate to the sensor using the USB convertor). Slowly we found a way to code it and then get the desired values which were shown by the hercules setup software.

The conclusion till here was, we were having basic understanding of the Modbus RTU protocol and how to know whether the Modbus sensor is working or not by using both the software and code(python).

As, Told in the previous comment that we were having the 5 probe sensor i.e Soil Integrated Sensor. We tried both the software (In this case we used another modbus software i.e CAS Modbus Scanner which we got to know when I saw this video) and code, We finally found that this 5 probe sensor is working and also we were able to get the same values from both the software and our code.

Finally, We were able to completely get out the values form a working sensor be it by a software or a code(python when used in windows command prompt). Now, We will be ordering a new NPK sensor and check whether that sensor is working or not immediately and then complete the purchase and then move to getting it's reading out by using an arduino/raspberry pi/esp8266.

It's a lot to understand but finally thanks @markd833 and @errorntrial for directing me from the point where I was struck i.e getting FFFFFF's :joy:.

Thankyou,
Vamsi Akula.

@errorntrial
I think I got same behavior as you, using that link you posted to Serial Monitor I could download and install that serial interface sw. I choose a similar sensor on the options menu (since my specific sensor it´s not on the list and I duno why) and I got it "Connected" by the software, but, when the inquiry frame is sent there is no reply from that sensor. I can´t accept the fact that these guys are selling broken sensors, it just don´t make sense to me, I already ask for refund money, but now my project is check :´(

@vamsiakula0390
I have the same 5 probe (7 in 1) sensor as you, will try using CAS Modbus Scanner...fingers crossed.