กลับหน้าหลัก
views
How to Read Soil NPK Sensor with Arduino using RS485 and Modbus RTU
Last updated on

How to Read Soil NPK Sensor with Arduino using RS485 and Modbus RTU


How to Read Soil NPK Sensor with Arduino using RS485 and Modbus RTU

This article explains how to read Nitrogen (N), Phosphorus (P), and Potassium (K) values from the PR-3000-TR-NPK-N01 soil sensor using the RS485 interface with Modbus RTU protocol, and display results on an OLED screen.

Diagram showing Arduino UNO connected to MAX485 module and NPK sensor with 12V power supply

Required Components

  • Arduino UNO R3
  • MAX485 TTL to RS485 conversion module
  • PR-3000-TR-NPK-N01 soil NPK sensor
  • 128x64 I2C OLED display 0.96 inch
  • 12V DC Power Adapter (for sensor)
  • 9V 2A Power Adapter (for Arduino)
  • Jumper wires: Male-to-Male, Male-to-Female 20cm
  • MB-102 Breadboard (optional)

RS485 and Modbus RTU Fundamentals

The PR-3000-TR-NPK-N01 sensor communicates via RS485, a differential signaling standard that offers excellent noise immunity for industrial and field applications. The Modbus RTU protocol organizes data into hexadecimal frames with defined register addresses.

The communication works by sending a Query Frame to request data from specific register addresses, then the sensor responds with a Response Frame containing N, P, and K values stored in separate registers.

Diagram showing Modbus RTU Query and Response frame structure with register addresses

Wiring Connections

Arduino to MAX485 Module

Arduino UNOMAX485 ModuleFunction
5VVCCPower
GNDGNDGround
Pin 2ROReceive Output
Pin 3DIDriver Input
Pin 7DEDriver Enable
Pin 8REReceiver Enable

MAX485 to NPK Sensor

MAX485Wire ColorNPK Sensor
BBlueB
AYellowA

NPK Sensor to 12V Power Supply

12V Power SupplyWire ColorNPK Sensor
+12VBrownPositive wire
GNDBlackNegative wire

Arduino to OLED Display

Arduino UNOOLED 128x64
5VVCC
GNDGND
A4 (SDA)SDA
A5 (SCL)SCL

Important: Connect all GND pins together (Arduino, MAX485, 12V supply) to establish a common signal reference.

Breadboard layout illustration showing all wiring connections between devices

Arduino Code for Reading NPK Values

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

// RS485 pin definitions
#define RS485_RX_PIN 2
#define RS485_TX_PIN 3
#define RS485_DE_PIN 7
#define RS485_RE_PIN 8


// Modbus Register Addresses
#define MODBUS_ADDR       0x01  // Slave Address
#define REG_N             0x0000  // Nitrogen Register
#define REG_P             0x0001  // Phosphorus Register
#define REG_K             0x0002  // Potassium Register

// OLED settings
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);


SoftwareSerial rs485(RS485_RX_PIN, RS485_TX_PIN);

void setup() {
  Serial.begin(9600);
  rs485.begin(9600);
  
  // Configure DE and RE pins
  pinMode(RS485_DE_PIN, OUTPUT);
  pinMode(RS485_RE_PIN, OUTPUT);
  digitalWrite(RS485_DE_PIN, LOW);  // Disable driver temporarily
  digitalWrite(RS485_RE_PIN, LOW);  // Enable receiver

  // Initialize OLED
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("OLED allocation failed"));
    while(1);
  }
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.display();
}

void loop() {
  int nValue = readModbus(REG_N);
  int pValue = readModbus(REG_P);
  int kValue = readModbus(REG_K);
  
  Serial.print("N: ");
  Serial.print(nValue);
  Serial.print(" mg/kg, P: ");
  Serial.print(pValue);
  Serial.print(" mg/kg, K: ");
  Serial.print(kValue);
  Serial.println(" mg/kg");
  
  // Display on OLED
  display.clearDisplay();
  display.setCursor(0, 0);
  display.print("NPK Sensor RS485");
  display.setCursor(0, 16);
  display.print("N: ");
  display.print(nValue);
  display.print(" mg/kg");
  display.setCursor(0, 28);
  display.print("P: ");
  display.print(pValue);
  display.print(" mg/kg");
  display.setCursor(0, 40);
  display.print("K: ");
  display.print(kValue);
  display.print(" mg/kg");
  display.display();
  
  delay(2000);
}

int readModbus(uint16_t regAddr) {
  // Build Query Frame
  uint8_t query[8];
  query[0] = MODBUS_ADDR;        // Slave Address
  query[1] = 0x03;               // Function Code (Read Holding Registers)
  query[2] = highByte(regAddr);  // Register Address High
  query[3] = lowByte(regAddr);   // Register Address Low
  query[4] = 0x00;               // Quantity High
  query[5] = 0x01;               // Quantity Low (read 1 register)
  
  // Calculate CRC16
  uint16_t crc = modbusCrc(query, 6);
  query[6] = lowByte(crc);
  query[7] = highByte(crc);
  
  // Send Query
  digitalWrite(RS485_DE_PIN, HIGH);  // Enable driver
  digitalWrite(RS485_RE_PIN, HIGH);
  rs485.write(query, sizeof(query));
  rs485.flush();
  digitalWrite(RS485_DE_PIN, LOW);   // Disable driver
  digitalWrite(RS485_RE_PIN, LOW);
  
  // Wait for Response
  delay(100);
  
  uint8_t response[9];
  int bytesReceived = 0;
  while (rs485.available() > 0) {
    response[bytesReceived] = rs485.read();
    bytesReceived++;
    if (bytesReceived >= 9) break;
  }
  
  // Verify CRC and extract value
  if (bytesReceived >= 7) {
    uint16_t recvCrc = (response[5] << 8) | response[4];
    uint16_t calcCrc = modbusCrc(response, 5);
    if (recvCrc == calcCrc) {
      return (response[3] << 8) | response[4];
    }
  }
  
  return -1;  // Return -1 on read error
}

uint16_t modbusCrc(uint8_t *data, uint8_t len) {
  uint16_t crc = 0xFFFF;
  for (uint8_t i = 0; i < len; i++) {
    crc ^= data[i];
    for (uint8_t j = 0; j < 8; j++) {
      if (crc & 0x0001) {
        crc = (crc >> 1) ^ 0xA001;
      } else {
        crc >>= 1;
      }
    }
  }
  return crc;
}

Reference Video

Demonstration video: Using NPK sensor with Arduino

Field Installation Guidelines

  1. Prepare the soil: Water the ground until moist but not waterlogged. Very dry or saturated soil will give inaccurate readings
  2. Insert the probe: Push the sensor vertically into the soil at 5-10 cm depth, ensuring the sensing area contacts the soil directly
  3. Wait for stabilization: Allow 30 seconds to 1 minute for readings to stabilize before recording values from Serial Monitor or OLED
  4. Avoid recent fertilization: Do not measure immediately after applying fertilizer or chemicals as values will be artificially elevated

Modbus Register Reference Table

FunctionFunction CodeRegister AddressQuantity
Read Holding Registers0x030x0000 - 0x0002As needed
Read Input Registers0x04See Data SheetAs needed

Troubleshooting Guide

No Response from Sensor:

  • Verify B (blue) and A (yellow) wires are connected to correct terminals
  • Confirm sensor receives 12V DC power
  • Check that all GND connections are shared common

Reading Returns -1:

  • Verify CRC16 calculation implementation
  • Check Register Address against sensor Data Sheet

OLED Display Not Working:

  • Verify SDA, SCL connections to A4, A5 pins
  • Try changing I2C address from 0x3C to 0x3D

อยากทำโปรเจคแบบนี้?

รับทำโปรเจค Arduino / IoT จบงานไว ส่งงานครบ พร้อมสอน

If you need Arduino project service or urgent IoT development, see full service details on the home page

จ้างทำโปรเจคเลย

ความคิดเห็น