I want to read this sensor, it is an NPK Sensor. For this, I have this board, and this hat of this board to read the Mobus 485 link.
For this, the origin code that the hat gives us. To read the sensor I use the next tutorial, and finally the config of the NPK sensor:
Then after all of this, I generated this code:
#define PIN_5V_EN 0
#define CAN_TX_PIN 22
#define CAN_RX_PIN 21
#define RS485_TX_PIN 19
#define RS485_RX_PIN 18
#define RS485_EN_PIN 23
#define RE 23
#define DE 0
#include <Arduino.h>
#include <HardwareSerial.h>
#include <ModbusMaster.h>
// instantiate ModbusMaster object
ModbusMaster node;
HardwareSerial Serial485(2);
// Modbus RTU requests for reading NPK values
const byte water[] = {0x01, 0x03, 0x02, 0x01, 0x00, 0x01, 0xd4, 0x72};
const byte temp[] = {0x01, 0x03, 0x02, 0x00, 0x00, 0x01, 0x85, 0xb2};
const byte ec[] = {0x01, 0x03, 0x02, 0x02, 0x00, 0x01, 0x24, 0x72};
const byte ph[] = {0x01, 0x03, 0x02, 0x03, 0x00, 0x01, 0x75, 0xb2};
const byte nitro[] = {0x01, 0x03, 0x02, 0x04, 0x00, 0x01, 0xce, 0x73};
const byte phos[] = {0x01, 0x03, 0x02, 0x05, 0x00, 0x01, 0x95, 0xb3};
const byte pota[] = {0x01, 0x03, 0x02, 0x06, 0x00, 0x01, 0x65, 0xb3};
const byte dev_add[] = {0x01, 0x03, 0x10, 0x00, 0x00, 0x01, 0x80, 0xca};
const byte dev_baud[] = {0x01, 0x03, 0x10, 0x01, 0x00, 0x01, 0xd1, 0x0A};
// A variable used to store NPK values
byte values[11];
void CANTask(void *prarm);
void setup()
{
pinMode(RS485_EN_PIN, OUTPUT);
pinMode(PIN_5V_EN, OUTPUT);
Serial.begin(9600);
Serial485.begin(9600, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
delay(5);
node.begin(2, Serial485);
}
void loop()
{
static uint32_t i;
uint8_t j, result;
uint16_t data[6];
byte val1,val2,val3,val4,val5,val6,val7,val8,val9;
val1 = water_func();
delay(250);
val2 = temperature();
delay(250);
val3 = ec_func();
delay(250);
val5 = ph_func();
delay(250);
val5 = nitrogen();
delay(250);
val6 = phosphorous();
delay(250);
val7 = potassium();
delay(250);
val8 = dev_add_func();
delay(250);
val9 = dev_baud_func();
delay(250);
// Print values to the serial monitor
Serial.print("Water: ");
Serial.print(val1);
Serial.println(" mg/kg");
Serial.print("Tempetature: ");
Serial.print(val2);
Serial.println(" ºC");
Serial.print("EC: ");
Serial.print(val3);
Serial.println(" mg/kg");
Serial.print("PH: ");
Serial.print(val4);
Serial.println(" mg/kg");
Serial.print("Nitrogen: ");
Serial.print(val5);
Serial.println(" mg/kg");
Serial.print("Phosphorous: ");
Serial.print(val6);
Serial.println(" mg/kg");
Serial.print("Potassium: ");
Serial.print(val7);
Serial.println(" mg/kg");
Serial.print("Devive_Address: ");
Serial.println(val8);
Serial.print("Devive_Baud: ");
Serial.println(val8);
delay(20000);
}
byte water_func(){
digitalWrite(DE,HIGH);
digitalWrite(RE,HIGH);
delay(10);
if(Serial485.write(water,sizeof(water))==8){
digitalWrite(DE,LOW);
digitalWrite(RE,LOW);
for(byte i=0;i<7;i++){
Serial.print(Serial485.read(),HEX);
values[i] = Serial485.read();
Serial.print(values[i],HEX);
}
Serial.println();
}
return values[4];
}
byte temperature(){
digitalWrite(DE,HIGH);
digitalWrite(RE,HIGH);
delay(10);
if(Serial485.write(temp,sizeof(temp))==8){
digitalWrite(DE,LOW);
digitalWrite(RE,LOW);
for(byte i=0;i<7;i++){
Serial.print(Serial485.read(),HEX);
values[i] = Serial485.read();
Serial.print(values[i],HEX);
}
Serial.println();
}
return values[4];
}
byte ec_func(){
digitalWrite(DE,HIGH);
digitalWrite(RE,HIGH);
delay(10);
if(Serial485.write(ec,sizeof(ec))==8){
digitalWrite(DE,LOW);
digitalWrite(RE,LOW);
for(byte i=0;i<7;i++){
Serial.print(Serial485.read(),HEX);
values[i] = Serial485.read();
Serial.print(values[i],HEX);
}
Serial.println();
}
return values[4];
}
byte ph_func(){
digitalWrite(DE,HIGH);
digitalWrite(RE,HIGH);
delay(10);
if(Serial485.write(ph,sizeof(ph))==8){
digitalWrite(DE,LOW);
digitalWrite(RE,LOW);
for(byte i=0;i<7;i++){
Serial.print(Serial485.read(),HEX);
values[i] = Serial485.read();
Serial.print(values[i],HEX);
}
Serial.println();
}
return values[4];
}
byte nitrogen(){
digitalWrite(DE,HIGH);
digitalWrite(RE,HIGH);
delay(10);
if(Serial485.write(nitro,sizeof(nitro))==8){
digitalWrite(DE,LOW);
digitalWrite(RE,LOW);
for(byte i=0;i<7;i++){
Serial.print(Serial485.read(),HEX);
values[i] = Serial485.read();
Serial.print(values[i],HEX);
}
Serial.println();
}
return values[4];
}
byte phosphorous(){
digitalWrite(DE,HIGH);
digitalWrite(RE,HIGH);
delay(10);
if(Serial485.write(phos,sizeof(phos))==8){
digitalWrite(DE,LOW);
digitalWrite(RE,LOW);
for(byte i=0;i<7;i++){
Serial.print(Serial485.read(),HEX);
values[i] = Serial485.read();
Serial.print(values[i],HEX);
}
Serial.println();
}
return values[4];
}
byte potassium(){
digitalWrite(DE,HIGH);
digitalWrite(RE,HIGH);
delay(10);
if(Serial485.write(pota,sizeof(pota))==8){
digitalWrite(DE,LOW);
digitalWrite(RE,LOW);
for(byte i=0;i<7;i++){
Serial.print(Serial485.read(),HEX);
values[i] = Serial485.read();
Serial.print(values[i],HEX);
}
Serial.println();
}
return values[4];
}
byte dev_add_func(){
digitalWrite(DE,HIGH);
digitalWrite(RE,HIGH);
delay(10);
if(Serial485.write(dev_add,sizeof(dev_add))==8){
digitalWrite(DE,LOW);
digitalWrite(RE,LOW);
for(byte i=0;i<7;i++){
Serial.print(Serial485.read(),HEX);
values[i] = Serial485.read();
Serial.print(values[i],HEX);
}
Serial.println();
}
return values[4];
}
byte dev_baud_func(){
digitalWrite(DE,HIGH);
digitalWrite(RE,HIGH);
delay(10);
if(Serial485.write(dev_baud,sizeof(dev_baud))==8){
digitalWrite(DE,LOW);
digitalWrite(RE,LOW);
for(byte i=0;i<7;i++){
Serial.print(Serial485.read(),HEX);
values[i] = Serial485.read();
Serial.print(values[i],HEX);
}
Serial.println();
}
return values[4];
}
But when I ran it, this is the output:
I think I have some issues with how to transmit the message and read it, any idea?
Thanks!!!
I don't have that sensor so be aware that I am guessing here but I can give you a couple of pointers that might help.
First of all, the datasheet for the sensor mentions the following:
The default baud rate is 4800bit / s and the default address is 0x01
So the first thing to try is to select that baud rate:
Serial485.begin(4800, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
I have no idea why the tutorial goes for 9600 baud but it seems wrong.
The next thing you need to realize is that you are doing some things in your code that are redundant. You are loading the ModbusMaster.h
library but not using it. You are instead sending and receiving Modbus frames manually.
The easiest thing to do is just drop the library by removing these lines:
#include <ModbusMaster.h>
// instantiate ModbusMaster object
ModbusMaster node;
....
node.begin(2, Serial485);
But my preferred solution would be to actually use the library (do not reinvent the wheel, right?).
This is a short example to read temperature values from your sensor with ModbusMaster.h
:
#define PIN_5V_EN 0
#define CAN_TX_PIN 22
#define CAN_RX_PIN 21
#define RS485_TX_PIN 19
#define RS485_RX_PIN 18
#define RS485_EN_PIN 23
#define RE 23
#define DE 0
#include <Arduino.h>
#include <HardwareSerial.h>
#include <ModbusMaster.h>
// instantiate ModbusMaster object
ModbusMaster node;
HardwareSerial Serial485(2);
// Modbus RTU requests for reading NPK values
// These are not needed, the library will do its magic
// A variable used to store NPK values
byte values[11];
void setup()
{
pinMode(RS485_EN_PIN, OUTPUT);
pinMode(PIN_5V_EN, OUTPUT);
Serial.begin(9600);
Serial485.begin(4800, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN); // Changed to 4800, default value
delay(5);
node.begin(1, Serial485); // Changed to ID=1, default value
}
void loop()
{
uint8_t result;
uint16_t data_temperature;
result = node.readHoldingRegisters(0x0200, 1); // use library to read holding register 0x0200, just 1 register that contains the temperature
// do something with data if read is successful-->store
if (result == node.ku8MBSuccess) {
data_temperature = node.getResponseBuffer(0);
}
// Print values to the serial monitor
Serial.print("Tempetature: ");
Serial.print(data_temperature/100); //scale divinding by 100
Serial.println(" ºC");
delay(20000);
}
As you can see I've changed both the baud rate and the Modbus slave ID to 1 with node.begin(1, Serial485);
to keep the default values as per the datasheet.
If you you use the library you don't have to take care of building, sending and receiving frames. Plus the CRCs are calculated and checked for you.
Finally, a couple of things that are really useful when you are debuggin a project like yours are:
You can get a glimpse of how easy it easy to debug the configuration and map register of a Modbus device just following these steps (you should get the same temperature value you got using the Arduino code above):
-Download, extract and run QModMaster (qModMaster.exe
). You might need to run as administrator.
-Set up your port selecting Modbus RTU from menu Options:
You might need to go to your Device Manager to check your port number. Other settings are the same as for your board.
-Save your settings by clicking OK. Now at the main window change Function Code to Read Holding Registers (0x03) and choose value 512 (0x0200) for Start Address , you should see something like this:
-Finally, go to Connect on menu Commands or click on the third icon starting from the left. Your temperature value (multiplied by 100) should appear on the first register (the box showing -/- just before connecting).
If you don't get the value, the only problem could be either something wrong with your sensor (power or otherwise) or a wiring issue. For my USB adaptor I'd have to connect A+, B- and GND (do not forget that one):