ESP32 with NPK soil sensor

hello, can i ask how do i resolve this issue, and how can i connect the same pin in DE RE. thank you!!

#include <ModbusMaster.h>

// Modbus RTU requests for reading NPK values
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};

// Create a ModbusMaster object
ModbusMaster sensor;

// Define the Serial interface for RS485 communication
#define SerialRS485 Serial1 // Use Serial1 for RS485 (TX on GPIO 17, RX on GPIO 16)
#define DE_RE_PIN 23 // GPIO pin for DE and RE control

void setup() {
  // Set the baud rate for the Serial monitor
  Serial.begin(115200);

  // Set the baud rate for RS485 communication
  SerialRS485.begin(9600, SERIAL_8N1);

  // Set DE and RE pins as output
  pinMode(DE_RE_PIN, OUTPUT);
  digitalWrite(DE_RE_PIN, LOW); // Start in receive mode

  // Initialize Modbus communication
  sensor.begin(1, SerialRS485); // 1 is the slave ID of your sensor
}

void loop() {
  // Read values
  byte val1, val2, val3;
  val1 = nitrogen();
  delay(250);
  val2 = phosphorous();
  delay(250);
  val3 = potassium();
  delay(250);

  // Print values to the serial monitor
  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_RE_PIN, HIGH); // Enable transmitter
  delay(1); // Allow time to switch modes
  SerialRS485.write(nitro, sizeof(nitro)); // Send nitrogen request
  delay(100); // Wait for the response
  digitalWrite(DE_RE_PIN, LOW); // Enable receiver
  delay(1); // Allow time to switch modes

  // Read response
  byte response[8];
  for (byte i = 0; i < 7; i++) {
    response[i] = SerialRS485.read();
    Serial.print(response[i], HEX);
  }
  Serial.println();
  
  return response[4]; // Return nitrogen value
}

byte phosphorous() {
  digitalWrite(DE_RE_PIN, HIGH); // Enable transmitter
  delay(1); // Allow time to switch modes
  SerialRS485.write(phos, sizeof(phos)); // Send phosphorous request
  delay(100); // Wait for the response
  digitalWrite(DE_RE_PIN, LOW); // Enable receiver
  delay(1); // Allow time to switch modes

  // Read response
  byte response[8];
  for (byte i = 0; i < 7; i++) {
    response[i] = SerialRS485.read();
    Serial.print(response[i], HEX);
  }
  Serial.println();
  
  return response[4]; // Return phosphorous value
}

byte potassium() {
  digitalWrite(DE_RE_PIN, HIGH); // Enable transmitter
  delay(1); // Allow time to switch modes
  SerialRS485.write(pota, sizeof(pota)); // Send potassium request
  delay(100); // Wait for the response
  digitalWrite(DE_RE_PIN, LOW); // Enable receiver
  delay(1); // Allow time to switch modes

  // Read response
  byte response[8];
  for (byte i = 0; i < 7; i++) {
    response[i] = SerialRS485.read();
    Serial.print(response[i], HEX);
  }
  Serial.println();
  
  return response[4]; // Return potassium value
}

There is no such thing as an NPK tester, it's a scam and has been reported in the forum numerous times. Throw it in the garbage and don't waste time on it.

2 Likes

Your results indicate you have do not have the RS485 connected.

1 Like

You have Modbusmaster library included and you initialize sensor.begin, but you don't use it in you code.

What board you have? Esp32 default pins for Serial2 are 16/17.

Jumper, split, Y-cable,

1 Like

I spotted that too. The code that @keyneath has posted looks as though it was originally based on the flawed code found on many websites that claim to "work" with those NPK sensors.

The code then seems to include the beginnings of using the ModbusMaster library but then reverts back to those canned Modbus messages.

@keyneath if you search these forums for NPK Sensor, then you should come across a modified version of the code that communicates with the NPK sensor using canned Modbus messages. You will also come across code that uses the ModbusMaster library to communicate with an NPK sensor.

I'm not on a big screen at the moment but I can search tomorrow to find the posts if you can't find them yourself.

1 Like

im using MaxRS485 modbus, the modbus is giving a red light, so i thought its working

im using Dev Module, i just follows what the internet/chatgpt says, sorry im new to this so i dont really have idea :smiling_face_with_tear:

i search about npk sensor, i found some, but i cant see the replies in the post, thats why i decided to post, and im new to this so i dont really have idea about this, but i will keep looking, thanks for the effort, i really appreciate it :smiling_face:

i saw one of the post, you have reply in that post, i have the same type of item like the post, also tried the 5v pin, but the result is unrecognized characteristics.

12v power adapter for sensor and when i test it in sensor configuration, it gives me the value

so i thought that its the code thats not working

For normal Esp32, Serial2 has default pins 16/17.
If you want use those pins on Serial1, you need to define them:
SerialRS485.begin(9600, SERIAL_8N1, 16, 17);

Anyway, I suggest you to use Modbusmaster library code for your approach.

1 Like

thank you, i will try that

So NOT a Nano ESP32, which is a totally different board. Click on the flag under your post and ask a moderator to move your thread, so you get more exposure.
Leo..

1 Like

Try if you get anything out with this:

#include <ModbusMaster.h>

#define MAX485_DE_RE 23//flow control pin for DE and RE

ModbusMaster node; //object node for class ModbusMaster

void preTransmission()
{
  digitalWrite(MAX485_DE_RE, HIGH);
}

void postTransmission()
{
  digitalWrite(MAX485_DE_RE, LOW);
}

void setup()
{
  pinMode(MAX485_DE_RE, OUTPUT);
  digitalWrite(MAX485_DE_RE, LOW);

  Serial.begin(115200);
  // Transmission mode: MODBUS-RTU, Baud rate: 9600bps, Data bits: 8, Stop bit: 1, Check bit: no
  Serial2.begin(9600, SERIAL_8N1, 16, 17);
  
  node.begin(1, Serial2); //Slave address 1
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}
//Setup ends here

void loop()
{
  uint8_t result;
  uint8_t j;
   
  result = node.readHoldingRegisters(0x001E, 3);
  
  if (result == node.ku8MBSuccess)
  {
  for (j = 0; j < 3; j++)
    {
      Serial.println(node.getResponseBuffer(j));
    }
  } 
  else 
  {
    Serial.print("Modbus Error: ");
    Serial.println(result, HEX);
  }

  Serial.println("\n ------------  \n ");

  delay(1000);
}

If you get error E2, control your wiring...

1 Like

That screenshot is good news. It shows you a lot of information. You know for sure that your serial comms should be 9600 baud and that the device address is 1. You also know that the 3 parameters are in registers at addresses 1E, 1F and 20, and that the sensor is working from an electrical connection point of view.

You need to be sure which Arduino board you have as the advice you receive depends on it. Post a photo of the board you have if you are unsure.

1 Like

Update guys, the npk sensor work now

12v power supply
TTL to RS485

Pin that works in esp32 dev module

GPIO26 RX
GPIO27 TX
3.3 v in esp32

#include <SoftwareSerial.h>
// Modbus RTU requests for reading NPK values
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};
// Array variable used to store NPK values
byte values[8];
SoftwareSerial mod(26, 27);
void setup() {
// Set the baud rate for the Serial port & Soft serial
Serial.begin(9600);
mod.begin(9600);
delay(500);
}
void loop() {
// Read values
byte val1,val2,val3;
val1 = nitrogen();
delay(500);
val2 = phosphorous();
delay(500);
val3 = potassium();
delay(500);
// Print values to the serial monitor
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(){
mod.write(nitro,8);
for(byte i=0;i<7;i++){
values[i] = mod.read();
Serial.print(values[i],HEX);
}
Serial.println();
return values[4];
}
byte phosphorous(){
mod.write(phos,8);
for(byte i=0;i<7;i++){
values[i] = mod.read();
Serial.print(values[i],HEX);
}
Serial.println();
return values[4];
delay(100);
}
byte potassium(){
mod.write(pota,8);
for(byte i=0;i<7;i++){
values[i] = mod.read();
Serial.print(values[i],HEX);
}
Serial.println();
return values[4];
delay(100);
}

I moved your topic to a more appropriate forum category @keyneath .

The Nano ESP32 category you chose is only used for discussions directly related to the Arduino Nano ESP32 board.

In the future, please take the time to pick the forum category that best suits the subject of your question. There is an "About the _____ category" topic at the top of each category that explains its purpose.

Thanks in advance for your cooperation.

1 Like

Glad you got it working. Note that the readings returned by these NPK sensors are usually garbage.

However, the "values" returned are not quite right as the serial comms has a flaw. It's the same issue that lots of other NPK sensor code found on the web has as they all copy eachother.

Post back here if you would like an explanation.

1 Like

u are using esp32, how can u use include softwareserial for this, i also facing problem that only get 255mg/kg value on it

A value of 255 is usually a sign that the serial interface is returning the value -1. This generally means that you are trying to read from the serial port when there is no data available.

In this case, I suspect that your particular sensor isn't responding to the canned modbus messages. That's either a wiring issue or incorrect parameters in the modbus messages. Note that there are several variations of the NPK "sensor" and they use different baud rates (some 4800, most 9600) and different register addresses for the various parameters. You need the user guide for your specific sensor in order to see what registers are used.

I have try to change the baud rates but it give me the sames value.

// Modbus RTU requests for reading NPK values
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};

// Array variable used to store NPK values
byte values[8];

// Define Serial2 for ESP32
#define mod Serial2

void setup() {
// Set the baud rate for the Serial port and Serial2
Serial.begin(9600);
mod.begin(9600, SERIAL_8N1, 26, 27); // RX = GPIO 26, TX = GPIO 27
delay(500);
}

void loop() {
// Read values
byte val1, val2, val3;
val1 = nitrogen();
delay(500);
val2 = phosphorous();
delay(500);
val3 = potassium();
delay(500);

// Print values to the serial monitor
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() {
mod.write(nitro, 8);
delay(50); // Small delay for sensor response
for (byte i = 0; i < 7; i++) {
if (mod.available()) {
values[i] = mod.read();
Serial.print(values[i], HEX);
}
}
Serial.println();
return values[4];
}

byte phosphorous() {
mod.write(phos, 8);
delay(50);
for (byte i = 0; i < 7; i++) {
if (mod.available()) {
values[i] = mod.read();
Serial.print(values[i], HEX);
}
}
Serial.println();
return values[4];
}

byte potassium() {
mod.write(pota, 8);
delay(50);
for (byte i = 0; i < 7; i++) {
if (mod.available()) {
values[i] = mod.read();
Serial.print(values[i], HEX);
}
}
Serial.println();
return values[4];
}

Now, I am using this code, and my RS485 is TTL to RS485. The value that I get in the serial monitor is all 0 mg/kg.