ESP32 + MAX485 + Soil NPK Sensor (RS485/Modbus) - Always getting 0xE2 (Response Timeout), no data received at all

Hi everyone, I've been stuck on this for a while and would really appreciate a second pair of eyes.

Goal: Read N, P, K values from a soil NPK sensor (RS485/Modbus RTU) using an ESP32 + MAX485 module.

Hardware:

  • ESP32 DevKit (38-pin, standard pinout diagram - possibly a WROVER variant with PSRAM, still confirming)
  • Generic MAX485 RS485-to-TTL module (separate RE and DE pins, not auto-flow)
  • Soil NPK sensor, RS485 output, powered separately at 12V DC

Wiring:

  • MAX485 RO -> ESP32 GPIO33
  • MAX485 DI -> ESP32 GPIO32
  • MAX485 RE -> ESP32 GPIO4
  • MAX485 DE -> ESP32 GPIO5
  • MAX485 VCC -> ESP32 5V
  • MAX485 GND -> ESP32 GND (shared with sensor power supply GND)
  • Sensor A -> MAX485 A
  • Sensor B -> MAX485 B
  • Sensor VCC -> separate 12V DC power supply
  • Sensor GND -> shared common ground

(I originally tried GPIO16/17 for RX2/TX2 since that's the default UART2 on ESP32, but switched to GPIO32/33 after reading that GPIO16/17 can be reserved for PSRAM on WROVER-based boards. Switching pins didn't change the outcome.)

Code (using ModbusMaster library):

#include <ModbusMaster.h>

#define MAX485_RE 4
#define MAX485_DE 5
#define RXD2 33
#define TXD2 32

#define SLAVE_ID 1
#define BAUD_RATE 9600

ModbusMaster node;

void preTransmission() {
  digitalWrite(MAX485_RE, HIGH);
  digitalWrite(MAX485_DE, HIGH);
}

void postTransmission() {
  digitalWrite(MAX485_RE, LOW);
  digitalWrite(MAX485_DE, LOW);
}

void setup() {
  Serial.begin(115200);
  pinMode(MAX485_RE, OUTPUT);
  pinMode(MAX485_DE, OUTPUT);
  digitalWrite(MAX485_RE, LOW);
  digitalWrite(MAX485_DE, LOW);

  Serial2.begin(BAUD_RATE, SERIAL_8N1, RXD2, TXD2);
  node.begin(SLAVE_ID, Serial2);
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}

void loop() {
  uint8_t result = node.readHoldingRegisters(0x001E, 3);
  if (result == node.ku8MBSuccess) {
    Serial.println("Success!");
  } else {
    Serial.print("Error: 0x");
    Serial.println(result, HEX);
  }
  delay(3000);
}

Result: Always get 0xE2 (ku8MBResponseTimedOut) - zero bytes received, every single time.

What I've already tried:

  • Baud rates: 2400, 4800, 9600, 19200, 38400
  • Slave IDs: 0, 1, 2, 3, 4, 5
  • Swapped A/B wires (both orientations)
  • Different GPIO pairs for RX/TX (16/17, then 32/33, then 13/33)
  • Loopback test on A/B (shorting A to B directly) to verify the MAX485+ESP32 side independent of the sensor - haven't gotten a clear result yet, still in progress
  • Verified sensor gets 12V on its power wires with a multimeter

What I haven't fully ruled out yet:

  • Whether my MAX485 module itself might be defective
  • Whether my exact sensor genuinely speaks Modbus RTU over RS485 (it's a generic/OEM sensor without a fully confirmed datasheet)

Has anyone run into a similar "alwa


ys 0xE2, zero bytes ever received" situation? Any diagnostic steps I might be missing, or common rookie mistakes with MAX485 wiring (DE/RE logic, floating pins, etc.) that could cause a complete silence like this? Happy to share more details/photos if needed. Thanks in advance!

If I saw this correctly, you are trying to use a 5 V MAX485 with a 3.3 V ESP32. That is not permissible. In particular, the MAX485's RO output can provide a 5 V logic level directly to the ESP32 GPIO.

You can't do a loopback test by shorting A to B. You may damage the part by shorting A to B.

Unless you paid hundreds or thousands of $ for professional sensors, what you have is a well-known fraud. After they were outed, tey added wording similar to this. You must enter a value for N, P, K manually into the sensor (code really).

There is no known hobby-level sensor that can measure NPK.

before attempting to connect to Modbus it is a good idea to test basic RS485 communication, e.g. using a USB-RS485 module to a PC running a terminal emulator

ESP32 code


// ESP32 Serial1 to RS485 bus
//
// see https://microcontrollerslab.com/rs485-serial-communication-arduino-tutorial/

#define RS485Serial Serial1  // hardware serial port on ESP32

// RS485 VCC ESP32 to to 3.3V
#define RXD2 16  //  Serial1 Rx  GPIO16 to MAX485 RO  (Receiver Output) /RXD or MAX3485 RXD
#define TXD2 17  //  Serial1 Tx  GPIO17 to MAX485 DI  (Driver Input) /TXD    or MAX3485 TXD

// some RS485 modules automatically switch between transmit and receive DE/RE not defined
// RS485 DE (Driver Enable set HIGH to enable)  and RE (Receiver Enable set LOW to enable) to ESP32 pins 18 and 19
#define DE  18  //RS485 Direction control pin
#define RE  19  //RS485 Direction control pin

#define RS485Transmit HIGH
#define RS485Receive LOW

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println("\n\nESP32 connect to RS485 bus - enter/receive text");
#ifdef DE  // if DE and RE are defined
  pinMode(DE, OUTPUT);
  pinMode(RE, OUTPUT);
  digitalWrite(DE, RS485Receive);  // Disable RS485 Transmit
  digitalWrite(RE, RS485Receive);  // Disable RS485 Transmit
#endif
  RS485Serial.begin(115200, SERIAL_8N1, RXD2, TXD2);  // set the RS485 data rate
}

// loop sending data to RS485 and receiving data
void loop() {
  if (Serial.available()) {           // Arduino Serial data avaliable?
#ifdef DE                             // if DE and RE are defined
    digitalWrite(DE, RS485Transmit);  // Enable RS485 Transmit
    digitalWrite(RE, RS485Transmit);  // Enable RS485 Transmit
#endif
    RS485Serial.write(Serial.read());  // Send byte to Remote RS485
    RS485Serial.flush();               // wait for byte to be transmitted
#ifdef DE                              // if DE and RE are defined
    digitalWrite(DE, RS485Receive);    // Disable RS485 Transmit
    digitalWrite(RE, RS485Receive);    // Disable RS485 Transmit
#endif
  }

  if (RS485Serial.available())         // RS485 serial data available?
    Serial.write(RS485Serial.read());  // read character and display it
}



ESP32 serial monitor output

hello from ESP32
test2 from ESP32
test3 from ESP32 1234567890

Teraterm terminal emulator running on a PC

test setup

note that although the MAX485 operating voltage is specified as 5V I am operating it from 3V3 to be compatible with the ESP32 3V3 logic

however, I would recommend using a MAX3485 based module which is specified to work at 3V3 (and 5V)

NOTE: the above MAX3485 module does not have the DE and RE inputs - it automatically switches between transmit and receive

I'd echo @sonofcy comments.

I don't know of any methods that employ a collection of electrodes that cpould possibly give reliable and verifiable results for such a diverse analysis as Nitrogen, Phosphorus, and Potassium (NPK).

All need different means of analysis, from colorimetric to flame spectroscopy.

Nitrogen can exist in soil in many forms, from ammonia compounds, nitrates and nitrites, complex inorganic and organic compounds. Nitrogen alone would need several lab methods if total nitrogen is the aim.

At best, you might get conductivity out of it, but whether it was a meaningful number is debatable.

How on earth do you calibrate the thing? You would need a laboratory to do just one of the three.

On those boards, pins 16 & 17 are not exposed.

it may be a good idea to wait until the transmission is actually complete before switching, for at least the duration of a complete byte. The datasheet should say how long it is before the unit responds to the query or whether it throws out data repeatedly.

As Jim-P already pointed out you can't loop-back test a MAX485 like that. the A & B lines are a pair ow wires the represent a single bus where HIGH and LOW levels a represented by inversion of the polarity of the A & B wires. The protocol switches the directionality of the bus

Where did you get it from, what documentation do you have ?

That is possible, One of the possible tests has already been suggested, another option is to get another unit and with 2 units you could do a loopback test.

Yeo that is what i saw also, i use a simple 3x 1K voltage divider from MAX485 RO -> 1K - ESP-Rx -> 1K -> 1K -> GND
There is a chance that the protection of the ESP32 is kicking in and disturbing the reception.

The jumper cables are always a suspect, verify all connections with a DMM !

on esp32 there is a VIN pin wich is it's a 5v, is it possible that we use the VIN instead rather than we changing the esp32 voltages?

thanks for the advice, but how to solve the problem, any solution?

thanks for the reply, i would to tes this wiring, i would reply again further


I’ve tried using the same circuit and code; the output from the Arduino to Tera Term is correct, but it only displays a single character. However, there is no output at all when sending data from Tera Term to the Arduino.

At least take care of your wiring. Breadboard with flimsy jumper wires is often source of issues.

I mentioned in post 5
note that although the MAX485 operating voltage is specified as 5V I am operating it from 3V3 to be compatible with the ESP32 3V3 logic
it worked OK on my setup when I ran the test but there is no guarantee that a 5V logic device with work on 3.3.V with your setup - this may be your problem
however, if you power the MAX485 from 5V you could damage the ESP32 unless you level shift (or use a potential divider) the MAX485 Tx signal to the ESP32 Rx
I would suggest using a MSX3485 device which will work from 3.3V power

also as @kmin stated jumper wires are an endless source of poor connections and intermittent problems - when I was running the tests in post 5 the connecting wires disconnected every time I touched the PCBs

I have used ~ dozen of these max485 boards supplied at 3.3V and I have never had issues. Out of specs yes, but for testing/hobby use should be fine.

interesting to hear that - I tend to use MSX3485 devices with 3.3V logic MCUs

test by @leenara in post 11 should have worked - maybe poor connections as you suggested in post 12

can you give details of the USB-RS485 module you used in post 11?

have you an oscilloscope to look at the A and B signals?

That's definitely ideal choice, I prefer to work within specs as well. But I bought 20 max485 boards long time ago and just used what I had on hands since I didn't experience hiccups over years.