@Proncio From the images i sent, the NPK values from the soil sensor I bought and the readings i obtained on my serial monitor are the same. I will perform extensive test on how the readings will vary when i increase the concentration of NPK in my soil sample.
Hi Bright,
interesting, my guess is that those NPK readings could be the same between the two sensors if they start from the same EC level, and from there they apply the same formula to split N-P-K.
But, as stated in the beginning, I'd love to be contradicted... were u able to test with different NPK concentrations? If yes, can you post you code and reading keys?
Hello Mark
When I try your codes, I get response timed out
hi
can you please share the working code with esp32?
my email is pdusabe03@gmail.com
Which code are you referring to?
I assume from your request for code to @madhukarji that you are using an ESP32 board?
Which RS485 module are you using?
How have you connected your ESP32, RS485 module and NPK sensor? Please provide a wiring diagram - hand drawn and photographed is ok.
The code that I am using:
#include <SoftwareSerial.h>
SoftwareSerial mod(3, 1); // Use SoftwareSerial for RS485 communication
const byte N[] = {0x01, 0x03, 0x00, 0x87, 0x00, 0x01, 0x94, 0x0A};
const byte P[] = {0x01, 0x03, 0x00, 0x8A, 0x00, 0x01, 0x25, 0xCA};
const byte K[] = {0x01, 0x03, 0x00, 0x8E, 0x00, 0x01, 0x64, 0x0A};
const byte pH[] = {0x01, 0x03, 0x02, 0xAE, 0x00, 0x01, 0x25, 0xE9};
const byte soil_moist[] = {0x01, 0x03, 0x01, 0x64, 0x00, 0x01, 0x94, 0x0A};
const byte temp[] = {0x01, 0x03, 0xFF, 0xDD, 0x00, 0x01, 0x95, 0xDA};
const byte ec[] = {0x01, 0x03, 0x04, 0xD2, 0x00, 0x01, 0x65, 0xE9};
byte values[7];
void setup() {
Serial.begin(4800);
mod.begin(4800);
pinMode(25, OUTPUT); // Use a digital pin for the RE (Receive Enable) control
pinMode(26, OUTPUT); // Use a digital pin for the DE (Receive Enable) control
}
void loop() {
byte val1, val2, val3, val4, val5, val6, val7;
val1 = readParameter(N);
delay(250);
val2 = readParameter(P);
delay(250);
val3 = readParameter(K);
delay(250);
val4 = readParameter(pH);
delay(250);
val5 = readParameter(soil_moist);
delay(250);
val6 = readParameter(temp);
delay(250);
val7 = readParameter(ec);
delay(250);
Serial.print("N: ");
Serial.print(val1);
Serial.println(" mg/kg");
Serial.print("P: ");
Serial.print(val2);
Serial.println(" mg/kg");
Serial.print("K: ");
Serial.print(val3);
Serial.println(" mg/kg");
Serial.print("pH: ");
Serial.print(val4);
Serial.println(" pH");
Serial.print("Soil Moisture: ");
Serial.print(val5);
Serial.println(" %");
Serial.print("Temperature: ");
Serial.print(val6);
Serial.println(" C");
Serial.print("Electrical Conductivity: ");
Serial.print(val7);
Serial.println(" mS/m");
delay(1000);
}
byte readParameter(const byte* queryData) {
digitalWrite(25, HIGH); // Set RE high for receiving data
digitalWrite(26, HIGH); // Set DE high for receiving data
mod.write(queryData, 8);
delay(1000);
digitalWrite(25, LOW); // Set RE low to allow transmission
digitalWrite(26, LOW); // Set DE low to allow transmission
delay(10);
if (mod.available() >= 7) { // Check if there are enough bytes available
mod.readBytes(values, 7);
// Optionally, print the received data in HEX format
// for (byte i = 0; i < 7; i++) {
// Serial.print(values[i], HEX);
// Serial.print(" ");
// }
// Serial.println();
return values[3]; // Return the relevant data byte based on your sensor's response format
} else {
return 0; // Return 0 if the data was not received correctly
}
}```
That's not the code you were using back in post #145. If you get a response timeout message, then you were likely using a proper Modbus library.
The code you have posted is not my code! This bit of your code is wrong and will never hear the sensor reply.
What that code is doing is:
- Put RS485 transceiver into Tx mode
- Write out 8 bytes
- Wait 1 second
- Put RS485 transceiver into Rx mode
Have a search for NPK sensor on these forums for discussions on how to query the sensor correctly.
Hello @markd833 ! I tried your code and configuration with npk sensor from ComWinTop and got no response from the sensor. Could you help me to figure out what is happening?
I changed the baudrate to 4800 and the register address.
My 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 the libraries
// via the Arduino IDE or manually:
// Get AltSoftSerial at https://github.com/PaulStoffregen/AltSoftSerial
// Get ModbusMaster at https://github.com/4-20ma/ModbusMaster
//
// RS485 module connected to Arduino UNO as follows:
// 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
//
// Assumes that the sensor address is 1, Nitrogen is address 0x1E, Phosphorus is address 0x1F
// and Potassium is address 0x20. Change the code if yours are different.
//
// NOTE: I do not have this sensor, so I simulated it using ModRSsim2 available from https://sourceforge.net/projects/modrssim2/
//
#include <ModbusMaster.h>
#include <AltSoftSerial.h>
#define NITROGEN_ADDR 0004
#define PHOSPHORUS_ADDR 0x1F
#define POTASSIUM_ADDR 0x20
#define MAX485_DE 6
#define MAX485_RE_NEG 7
AltSoftSerial swSerial;
ModbusMaster node;
// Put the MAX485 into transmit mode
void preTransmission()
{
digitalWrite(MAX485_RE_NEG, 1);
digitalWrite(MAX485_DE, 1);
}
// Put the MAX485 into receive mode
void postTransmission()
{
digitalWrite(MAX485_RE_NEG, 0);
digitalWrite(MAX485_DE, 0);
}
void setup() {
Serial.begin( 9600 );
// configure the MAX485 RE & DE control signals and enable receive mode
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(4800);
// Modbus slave ID of NPK sensor is 1
node.begin(1, swSerial);
// Callbacks to allow us to set the RS485 Tx/Rx direction
node.preTransmission(preTransmission);
node.postTransmission(postTransmission);
}
void loop() {
uint8_t result;
// NITROGEN
result = node.readHoldingRegisters(NITROGEN_ADDR, 1);
if (result == node.ku8MBSuccess)
{
Serial.print(" Nitrogen: ");
Serial.print(node.getResponseBuffer(0x0));
Serial.println(" mg/kg");
} else {
printModbusError( result );
}
// PHOSPHORUS
result = node.readHoldingRegisters(PHOSPHORUS_ADDR, 1);
if (result == node.ku8MBSuccess)
{
Serial.print("Phosphorous: ");
Serial.print(node.getResponseBuffer(0x0));
Serial.println(" mg/kg");
} else {
printModbusError( result );
}
// POTASSIUM
result = node.readHoldingRegisters(POTASSIUM_ADDR, 1);
if (result == node.ku8MBSuccess)
{
Serial.print(" Potassium: ");
Serial.print(node.getResponseBuffer(0x0));
Serial.println(" mg/kg");
} else {
printModbusError( result );
}
Serial.println();
delay(2000);
}
// print out the error received from the Modbus library
void printModbusError( uint8_t errNum )
{
switch ( errNum ) {
case node.ku8MBSuccess:
Serial.println(F("Success"));
break;
case node.ku8MBIllegalFunction:
Serial.println(F("Illegal Function Exception"));
break;
case node.ku8MBIllegalDataAddress:
Serial.println(F("Illegal Data Address Exception"));
break;
case node.ku8MBIllegalDataValue:
Serial.println(F("Illegal Data Value Exception"));
break;
case node.ku8MBSlaveDeviceFailure:
Serial.println(F("Slave Device Failure"));
break;
case node.ku8MBInvalidSlaveID:
Serial.println(F("Invalid Slave ID"));
break;
case node.ku8MBInvalidFunction:
Serial.println(F("Invalid Function"));
break;
case node.ku8MBResponseTimedOut:
Serial.println(F("Response Timed Out"));
break;
case node.ku8MBInvalidCRC:
Serial.println(F("Invalid CRC"));
break;
default:
Serial.println(F("Unknown Error"));
break;
}
}
The wiring is like your schematic. The only difference is that I am using an Arduino Nano.
I bought an USB-RS485 dongle. It probably arrives tomorrow. Maybe it should help us.
Thanks a lot!
Do you have the datasheet / user manual for your specific NPK sensor?
Generally the sensor address is 01 and the baud rate is 9600, but some have reported 4800 baud.
As for the actual registers, there seems to be quite a variation and your specific user guide should detail those.
The USB-RS485 dongle will be really useful in diagnosing the issue.
Thanks for your response.
NPK type manual.pdf (454,8,KB)
Yes I have the manual. I am sending it.
The Nitrogen address ties in with the document, however the Phosphorous and Potassium addresses need to be changed. Your document indicates that these should be 5 and 6 respectively.
The document says that the default device address is 1 and you should use 4800 baud. Those bits are correct in the code.
When you ran your modified version of my code, what did it print out? Generally you should see either the returned value or a text string showing what the error/issue was.
I got the following!
11:06:46.113 -> Response Timed Out
11:06:48.159 -> Response Timed Out
11:06:50.173 -> Response Timed Out
11:06:50.173 ->
We can theorise on what may be wrong, but I would wait until your USB-RS485 dongle arrives as that will give us a much clearer idea of where the communications process is breaking down.
Hello my friend @markd833 !
The following is what I got when tried to communicate with the sensor using the dongle.
That looks like good news. You know for sure that the required baud rate is 4800. You know the sensor address is 01 and that the sensor is working.
According to your manual, register 1 is temperature. You are getting a valid response from the sensor and it is saying that the measured temperature is 0x00F1 = 241. That indicates that the temperature is 24.1C.
Modify the code you used in post #152 to ask for temperature at register address 1.
Hopefully you will get a valid response.
Sorry! How to change de register address to 1?
This is the code.
#include <ModbusMaster.h>
#include <AltSoftSerial.h>
#define NITROGEN_ADDR 0x01
#define PHOSPHORUS_ADDR 0x1F
#define POTASSIUM_ADDR 0x20
#define MAX485_DE 6
#define MAX485_RE_NEG 7
AltSoftSerial swSerial;
ModbusMaster node;
// Put the MAX485 into transmit mode
void preTransmission()
{
digitalWrite(MAX485_RE_NEG, 1);
digitalWrite(MAX485_DE, 1);
}
// Put the MAX485 into receive mode
void postTransmission()
{
digitalWrite(MAX485_RE_NEG, 0);
digitalWrite(MAX485_DE, 0);
}
void setup() {
Serial.begin( 9600 );
// configure the MAX485 RE & DE control signals and enable receive mode
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(4800);
// Modbus slave ID of NPK sensor is 1
node.begin(1, swSerial);
// Callbacks to allow us to set the RS485 Tx/Rx direction
node.preTransmission(preTransmission);
node.postTransmission(postTransmission);
}
void loop() {
uint8_t result;
// NITROGEN
result = node.readHoldingRegisters(NITROGEN_ADDR, 1);
if (result == node.ku8MBSuccess)
{
Serial.print(" Nitrogen: ");
Serial.print(node.getResponseBuffer(0x0));
Serial.println(" mg/kg");
} else {
printModbusError( result );
}
// PHOSPHORUS
result = node.readHoldingRegisters(PHOSPHORUS_ADDR, 1);
if (result == node.ku8MBSuccess)
{
Serial.print("Phosphorous: ");
Serial.print(node.getResponseBuffer(0x0));
Serial.println(" mg/kg");
} else {
printModbusError( result );
}
// POTASSIUM
result = node.readHoldingRegisters(POTASSIUM_ADDR, 1);
if (result == node.ku8MBSuccess)
{
Serial.print(" Potassium: ");
Serial.print(node.getResponseBuffer(0x0));
Serial.println(" mg/kg");
} else {
printModbusError( result );
}
Serial.println();
delay(2000);
}
// print out the error received from the Modbus library
void printModbusError( uint8_t errNum )
{
switch ( errNum ) {
case node.ku8MBSuccess:
Serial.println(F("Success"));
break;
case node.ku8MBIllegalFunction:
Serial.println(F("Illegal Function Exception"));
break;
case node.ku8MBIllegalDataAddress:
Serial.println(F("Illegal Data Address Exception"));
break;
case node.ku8MBIllegalDataValue:
Serial.println(F("Illegal Data Value Exception"));
break;
case node.ku8MBSlaveDeviceFailure:
Serial.println(F("Slave Device Failure"));
break;
case node.ku8MBInvalidSlaveID:
Serial.println(F("Invalid Slave ID"));
break;
case node.ku8MBInvalidFunction:
Serial.println(F("Invalid Function"));
break;
case node.ku8MBResponseTimedOut:
Serial.println(F("Response Timed Out"));
break;
case node.ku8MBInvalidCRC:
Serial.println(F("Invalid CRC"));
break;
default:
Serial.println(F("Unknown Error"));
break;
}
}
I keep getting the following unforfunately.
07:51:40.743 -> Response Timed Out
07:51:42.755 -> Response Timed Out
07:51:44.799 -> Response Timed Out
Try this code just to see if it will respond with temperature:
#include <ModbusMaster.h>
#include <AltSoftSerial.h>
#define TEMPERATURE_ADDR 0x01
#define MAX485_DE 6
#define MAX485_RE_NEG 7
AltSoftSerial swSerial;
ModbusMaster node;
// Put the MAX485 into transmit mode
void preTransmission()
{
digitalWrite(MAX485_RE_NEG, 1);
digitalWrite(MAX485_DE, 1);
}
// Put the MAX485 into receive mode
void postTransmission()
{
digitalWrite(MAX485_RE_NEG, 0);
digitalWrite(MAX485_DE, 0);
}
void setup() {
Serial.begin( 9600 );
// configure the MAX485 RE & DE control signals and enable receive mode
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(4800);
// Modbus slave ID of NPK sensor is 1
node.begin(1, swSerial);
// Callbacks to allow us to set the RS485 Tx/Rx direction
node.preTransmission(preTransmission);
node.postTransmission(postTransmission);
}
void loop() {
uint8_t result;
// TEMPERATURE
result = node.readHoldingRegisters(TEMPERATURE_ADDR , 1);
if (result == node.ku8MBSuccess)
{
Serial.print("Temperature: ");
Serial.print(node.getResponseBuffer(0x0));
Serial.println(" degC x 10");
} else {
printModbusError( result );
}
Serial.println();
delay(2000);
}
// print out the error received from the Modbus library
void printModbusError( uint8_t errNum )
{
switch ( errNum ) {
case node.ku8MBSuccess:
Serial.println(F("Success"));
break;
case node.ku8MBIllegalFunction:
Serial.println(F("Illegal Function Exception"));
break;
case node.ku8MBIllegalDataAddress:
Serial.println(F("Illegal Data Address Exception"));
break;
case node.ku8MBIllegalDataValue:
Serial.println(F("Illegal Data Value Exception"));
break;
case node.ku8MBSlaveDeviceFailure:
Serial.println(F("Slave Device Failure"));
break;
case node.ku8MBInvalidSlaveID:
Serial.println(F("Invalid Slave ID"));
break;
case node.ku8MBInvalidFunction:
Serial.println(F("Invalid Function"));
break;
case node.ku8MBResponseTimedOut:
Serial.println(F("Response Timed Out"));
break;
case node.ku8MBInvalidCRC:
Serial.println(F("Invalid CRC"));
break;
default:
Serial.println(F("Unknown Error"));
break;
}
}



