Using an NPK Sensor with Arduino

arduino NPK sensor wiring diagram

We’ve featured in a previous project how we can make a “plant” send messages when it needs more water. Of course, healthy plants don’t only need water; the soil in which they are planted must also be abundant in a number of chemicals. Is there a sensor that does this?

Yes, there is. Such a sensor is popularly called an NPK sensor because of its three parameters: Nitrogen, Phosphorus, and Potassium. How does it work and how can we interface it with a microcontroller?

NPK Sensor Introduction

An NPK sensor can be of three types: optical, conductive, or electrochemical.

  • Optical NPK sensors rely on the behavior of light when passed through the soil of different concentrations of N, P, and K.
  • Electrochemical NPK sensors use a particular type of field-effect transistor that readily reacts with the target chemicals.
  • Conductive NPK sensors, the least expensive among the three, use electrodes that send out alternating currents to the soil. The conductivity of the soil will then be proportional to the amount of N, P, and K chemicals in the soil.

In this tutorial, we will feature the conductive type NPK sensor.

The sensor consists of a handheld device with a metal probe inserted into the soil. It utilizes a conductive method to assess the nutrient concentration. When the probe comes into contact with the soil, it detects electrical conductivity, which is directly related to the presence and concentration of NPK ions in the soil solution.

The conductive NPK sensor operates on the principle that each nutrient ion carries a specific electrical charge, allowing the device to differentiate between nitrogen, phosphorus, and potassium. The sensor's electrodes transmit a low electrical current through the soil, and the conductivity of the solution is measured. By analyzing the conductivity data, the sensor can accurately determine the nutrient levels in the soil.

A conductive-type NPK sensor typically incorporates an RS485 communication protocol to transmit data. RS485 is a popular standard for serial communication in industrial applications due to its robustness, long-distance capabilities, and multi-node support.

The conductive NPK sensor is connected to other devices or a central control system using RS485 wiring. RS485 uses a differential signaling scheme, which means that two wires are required for communication: one for transmitting data (often labeled "A" or "TX+") and one for receiving data (often labeled "B" or "TX-").

In a typical setup, the conductive NPK sensor operates as a slave device, while another device, such as a data logger, controller, or computer, acts as the master device. The master initiates communication by sending commands or requests to the sensor. Here we will use an Arduino microcontroller as our master device.

NPK Interfacing

However, an Arduino board cannot directly communicate using RS485 because it lacks the necessary hardware support for RS485 communication. Arduino boards typically have UART (Universal Asynchronous Receiver-Transmitter) interfaces, which are commonly used for serial communication like RS232 (Serial) or TTL (Transistor-Transistor Logic) levels.

RS485 communication, on the other hand, requires additional hardware components to facilitate the differential signaling scheme and provide the necessary voltage levels for proper communication. These components include line drivers and receivers, which are responsible for transmitting and receiving RS485 signals.

logic signals - RS232 vs TTL vs RS485

 

To enable RS485 communication with an Arduino, you would need to use additional hardware, such as an RS485 transceiver module. The transceiver module acts as an interface between the Arduino and the RS485 network, converting the Arduino's UART signals to RS485-compatible signals and vice versa.

Here’s the wiring diagram for connecting an NPK sensor, an RS485 transceiver module, and an Arduino UNO:

arduino NPK sensor wiring diagram

 

Note that the VCC wire of the NPK sensor must connect to a 9-24 DC power source. The VCC pin of the RS485, meanwhile, must connect to the +5V pin of the Arduino.

NPK Communication Protocol

When the master device requests data from the sensor, the sensor responds by transmitting the measured NPK nutrient values. The sensor encodes the data into a format suitable for RS485 transmission, typically using a specific data protocol or a custom data structure.

The data structure for requests is as follows:

The RS485 commands required are shown on the NPK sensor datasheet. To request the Nitrogen concentration, the data request is:

For Phosphorous:

For Potassium:

The NPK sensor then creates a response including the chemical concentration requested. Here is the data structure of the response:

For example, after sending a request for Potassium content, the sensor might reply with this:

Here, the chemical content is given in two bytes (0x00, 0x30) and is equivalent to 48 mg/kg. This is because 0x30, a hexadecimal value converts to 48 in decimal.

Arduino Code

Here's an example Arduino code that demonstrates how to request Nitrogen, Phosphorus, and Potassium levels from an NPK sensor using an RS485 transceiver module:

#include <SoftwareSerial.h>

// RS485 transceiver control pins
#define DE_PIN 2
#define RE_PIN 3

// command data for each chemical
const byte nitro[]  = {0x01,0x03, 0x00, 0x1E, 0x00, 0x01, 0xE4, 0x0C};
const byte phosp[]  = {0x01,0x03, 0x00, 0x1F, 0x00, 0x01, 0xB5, 0xCC};
const byte potas[]  = {0x01,0x03, 0x00, 0x20, 0x00, 0x01, 0x85, 0xC0};

byte nitroValue;
byte phospValue;
byte potassValue;

byte nitroBuffer[11];
byte phospBuffer[11];
byte potasBuffer[11];

// SoftwareSerial object for RS485 communication
SoftwareSerial RS485Serial(10, 11); // RX, TX

void setup() {
  Serial.begin(9600); // Serial monitor for debugging
  RS485Serial.begin(9600); // RS485 communication

  pinMode(DE_PIN, OUTPUT);
  pinMode(RE_PIN, OUTPUT);
  digitalWrite(DE_PIN, LOW); // Set DE and RE pins to receive mode
  digitalWrite(RE_PIN, LOW);
}

void loop() {
  // Send request for NPK levels
  sendRequest();

  Serial.print("Soil N: ");
  Serial.print(nitroValue);
  Serial.println(" mg/kg");
  Serial.print("Soil P: ");
  Serial.print(phospValue);
  Serial.println(" mg/kg");
  Serial.print("Soil K: ");
  Serial.print(potassValue);
  Serial.println(" mg/kg");
  Serial.println();
  delay(2000);
}

void sendRequest() {
 
  // Send request command for each chemical
  nitroValue = getNitro();
  delay(250);
  phospValue = getPhosp();
  delay(250);
  potassValue = getPotass();
  delay(250);
}

byte getNitro(){
  digitalWrite(DE_PIN,HIGH);
  digitalWrite(RE_PIN,HIGH);
  delay(10);
  if(RS485Serial.write(nitro,sizeof(nitro))==8){
    digitalWrite(DE_PIN,LOW);
    digitalWrite(RE_PIN,LOW);
    for(byte i=0;i<7;i++){
      nitroBuffer[i] = RS485Serial.read();
      Serial.print(nitroBuffer[i],HEX);
      Serial.print("\t");
    }
    Serial.println();
  }
  return nitroBuffer[4];
}

byte getPhosp(){
  digitalWrite(DE_PIN,HIGH);
  digitalWrite(RE_PIN,HIGH);
  delay(10);
  if(RS485Serial.write(phosp,sizeof(phosp))==8){
    digitalWrite(DE_PIN,LOW);
    digitalWrite(RE_PIN,LOW);
    for(byte i=0;i<7;i++){
      phospBuffer[i] = RS485Serial.read();
      Serial.print(phospBuffer[i],HEX);
    Serial.print("\t");
    }
    Serial.println();
  }
  return phospBuffer[4];
}

byte getPotass(){
  digitalWrite(DE_PIN,HIGH);
  digitalWrite(RE_PIN,HIGH);
  delay(10);
  if(RS485Serial.write(potas,sizeof(potas))==8){
    digitalWrite(DE_PIN,LOW);
    digitalWrite(RE_PIN,LOW);
    for(byte i=0;i<7;i++){
      potasBuffer[i] = RS485Serial.read();
      Serial.print(potasBuffer[i],HEX);
      Serial.print("\t");
    }
    Serial.println();
  }
  return potasBuffer[4];
}

The provided code involves communication with an RS485 device to obtain soil nutrient levels (NPK: Nitrogen, Phosphorus, and Potassium). Let's break down how the code works:

  1. The code begins by including the necessary library for SoftwareSerial communication (#include <SoftwareSerial.h>).
  2. It defines the pins for controlling the RS485 transceiver (#define DE_PIN 2 and #define RE_PIN 3).
  3. Three arrays (nitro[], phosp[], and potas[]) are defined to store the command data for each chemical. These arrays contain hexadecimal values representing the commands to request NPK levels from the RS485 device.
  4. Variables (nitroValue, phospValue, and potassValue) are declared to store the received NPK values.
  5. Arrays (nitroBuffer[11], phospBuffer[11], and potasBuffer[11]) are defined to store the received data from the RS485 device.
  6. An instance of the SoftwareSerial library is created (SoftwareSerial RS485Serial(10, 11);) to establish RS485 communication on the specified RX and TX pins.
  7. The setup() function initializes the serial communication for debugging (Serial.begin(9600)) and the RS485 communication (RS485Serial.begin(9600)). It also sets the DE and RE pins as outputs and sets them to low, configuring the transceiver for receiving mode.
  8. The loop() function is where the main execution takes place. It sends requests for NPK levels, receives the values, and prints them to the serial monitor. It then adds a delay of 2 seconds before repeating the process.
  9. The sendRequest() function is responsible for sending the request commands for each chemical and storing the received values in the respective variables.
  10. The getNitro(), getPhosp(), and getPotass() functions are used to send the request command for each chemical, receive the response, and store the received data in the respective buffers. The functions also return the NPK values by accessing the appropriate index in the buffers (nitroBuffer[4], phospBuffer[4], and potasBuffer[4]).
  11. Within each of the getNitro(), getPhosp(), and getPotass() functions, the DE and RE pins are set to high to enable transmitting mode, a small delay is introduced, and the request command is sent to the RS485 device using the RS485Serial.write() function. The function then checks if the expected number of bytes (8) was successfully written.
  12. If the write operation was successful, the DE and RE pins are set to low to return to receiving mode. A loop is then used to read the received data byte by byte using the RS485Serial.read() function. The received values are stored in the respective buffers (nitroBuffer, phospBuffer, and potasBuffer) and printed to the serial monitor for debugging purposes.
  13. Finally, the NPK values are returned by accessing the appropriate index in the buffers, and the functions exit.

This code continuously sends requests for NPK levels to the RS485 device, receives the responses, and prints the values to the serial monitor for monitoring and analysis.

NPK + PH + Moisture Sensor

A variation of the 3-pin NPK sensor is the 5-pin sensor which, aside from N, P and K, also includes moisture and PH.

Naturally, additional commands are needed to request PH and soil moisture.

For PH:

For soil moisture:

The master device receives the data transmitted by the sensor via the RS485 communication link. It interprets the received data according to the agreed-upon protocol and extracts the NPK nutrient values. The master device can then store, display, or further process this data as required.

By employing the RS485 communication standard, the conductive NPK sensor can reliably transmit NPK nutrient data over long distances, typically up to several hundred meters. The multi-node capability of RS485 allows for the connection of multiple sensors or other devices on the same communication bus, enabling the monitoring of multiple locations or fields simultaneously.