A follow-up to my previous post. Here is my attempt to use the ModbusMaster library on an Arduino UNO with a software serial port using AltSoftSerial rather than the canned Modbus messages and error prone software that seems to be circulating various websites:
// Attempt to access a JXCT NPK sensor to read Nitrogen, Potassium & Phosphorus
// values using an Arduino UNO clone.
//
// This attempt uses the AltSoftSerial & ModbusMaster libraries. Get the libraries
// via the Arduino IDE or manually:
// Get AltSoftSerial at https://github.com/PaulStoffregen/AltSoftSerial
// Get ModbusMaster at https://github.com/4-20ma/ModbusMaster
//
// RS485 module connected to Arduino UNO as follows:
// RS485 DI signal to pin 9
// RS485 RO signal to pin 8
// RS485 RE signal to pin 7
// RS485 DE signal to pin 6
// RS485 VCC to 5V
// RS485 GND to GND
//
// Assumes that the sensor address is 1, Nitrogen is address 0x1E, Phosphorus is address 0x1F
// and Potassium is address 0x20. Change the code if yours are different.
//
// NOTE: I do not have this sensor, so I simulated it using ModRSsim2 available from https://sourceforge.net/projects/modrssim2/
//
#include <ModbusMaster.h>
#include <AltSoftSerial.h>
#define NITROGEN_ADDR 0x1E
#define PHOSPHORUS_ADDR 0x1F
#define POTASSIUM_ADDR 0x20
#define MAX485_DE 6
#define MAX485_RE_NEG 7
AltSoftSerial swSerial;
ModbusMaster node;
// Put the MAX485 into transmit mode
void preTransmission()
{
digitalWrite(MAX485_RE_NEG, 1);
digitalWrite(MAX485_DE, 1);
}
// Put the MAX485 into receive mode
void postTransmission()
{
digitalWrite(MAX485_RE_NEG, 0);
digitalWrite(MAX485_DE, 0);
}
void setup() {
Serial.begin( 9600 );
// configure the MAX485 RE & DE control signals and enable receive mode
pinMode(MAX485_RE_NEG, OUTPUT);
pinMode(MAX485_DE, OUTPUT);
digitalWrite(MAX485_RE_NEG, 0);
digitalWrite(MAX485_DE, 0);
// Modbus communication runs at 9600 baud
swSerial.begin(9600);
// Modbus slave ID of NPK sensor is 1
node.begin(1, swSerial);
// Callbacks to allow us to set the RS485 Tx/Rx direction
node.preTransmission(preTransmission);
node.postTransmission(postTransmission);
}
void loop() {
uint8_t result;
// NITROGEN
result = node.readHoldingRegisters(NITROGEN_ADDR, 1);
if (result == node.ku8MBSuccess)
{
Serial.print(" Nitrogen: ");
Serial.print(node.getResponseBuffer(0x0));
Serial.println(" mg/kg");
} else {
printModbusError( result );
}
// PHOSPHORUS
result = node.readHoldingRegisters(PHOSPHORUS_ADDR, 1);
if (result == node.ku8MBSuccess)
{
Serial.print("Phosphorous: ");
Serial.print(node.getResponseBuffer(0x0));
Serial.println(" mg/kg");
} else {
printModbusError( result );
}
// POTASSIUM
result = node.readHoldingRegisters(POTASSIUM_ADDR, 1);
if (result == node.ku8MBSuccess)
{
Serial.print(" Potassium: ");
Serial.print(node.getResponseBuffer(0x0));
Serial.println(" mg/kg");
} else {
printModbusError( result );
}
Serial.println();
delay(2000);
}
// print out the error received from the Modbus library
void printModbusError( uint8_t errNum )
{
switch ( errNum ) {
case node.ku8MBSuccess:
Serial.println(F("Success"));
break;
case node.ku8MBIllegalFunction:
Serial.println(F("Illegal Function Exception"));
break;
case node.ku8MBIllegalDataAddress:
Serial.println(F("Illegal Data Address Exception"));
break;
case node.ku8MBIllegalDataValue:
Serial.println(F("Illegal Data Value Exception"));
break;
case node.ku8MBSlaveDeviceFailure:
Serial.println(F("Slave Device Failure"));
break;
case node.ku8MBInvalidSlaveID:
Serial.println(F("Invalid Slave ID"));
break;
case node.ku8MBInvalidFunction:
Serial.println(F("Invalid Function"));
break;
case node.ku8MBResponseTimedOut:
Serial.println(F("Response Timed Out"));
break;
case node.ku8MBInvalidCRC:
Serial.println(F("Invalid CRC"));
break;
default:
Serial.println(F("Unknown Error"));
break;
}
}
And this is the wiring diagram:
I've also added in printing of Modbus errors. Check that your NPK sensor is set for Modbus address 1 and that the Nitrogen, Phosphorus & Potassium addresses are the same as I used.