MAX485 TTL to RS-485 Modules for soil NPK sensor

09:22:34.516 -> Response Timed Out
09:22:34.516 ->
=(

No problem. This is where the RS485-USB dongle will help. You need a way of monitoring the RS485 messages from your PC. I know how to do this via Windows. What OS are you using?

Mac OS Ventura 13.5. If its not possible, I can try my Windows PC.

I've no experience of Macs, but if you can use Windows, then have a look at this Sparkfun article about Real-Term. Download Real-Term and install it.

On the actual hardware side, you need to connect the USB-RS485 dongle A & B to the Arduino / Sensor A & B wires. You will then have 3 items connected to the A & B pins - that's ok.

When you run Real-Term, select the COM port as appropriate for your PC (see the Sparkfun article) and the baud rate of 4800. Look at the Sparkfun article for the section "adjusting the display" and change the display type to hexadecimal.

You should hopefully see some numbers being displayed which should be the same as those in the "Send Section" of that screenshot you posted back in post #158.

I could not get it on PC and used that other software for Mac. I connected the wires as you said and got the following... I think this is sending data from arduino to the sensor.
[16:57:15] ➞ 0A CE 0A
[16:57:15] ➞ 80 0C 08
[16:57:15] ➞ C2
[16:57:19] ➞ 0A CE 0A
[16:57:19] ➞ 0C 08
[16:57:19] ➞ 0C F8
[16:57:23] ➞ 20 C8 4E
[16:57:23] ➞ 80 0C 08
[16:57:23] ➞ 3F
[16:57:23] ➞ C2

My first thought is that you have A and B swapped over somewhere. Are you sure you have UNO module A connected to RS485-USB A and UNO module B connected to RS485-USB B.

With RS485, you connect the A's together and the B's together. (This is different to TTL serial and RS232 where you connect Tx to Rx.)

I connected RS485-USB A to MAX485 A and RS485-USB B to MAX485 B. Maybe I did not understand the wiring.

2 posts were merged into an existing topic: NPK Sensor to Arduino Mega 2560 Keeps Returning 255

Sorry @markd833 . Is my wiring correct?

Yes, A to A and B to B.

What do you mean 3 items connected to A and B pins?

Yes, you can have your Arduino, your sensor and the USB-RS485 dongle all connected. That way your dongle can listen in to the Comms between the Arduino and the sensor.

I was able to get it working buying the following RS485 to TTL module. I just switched the modules and everything started to work! Thanks @markd833 for the help!

My code:

#include <ModbusMaster.h>
#include <AltSoftSerial.h>

#define HUMIDITY_ADDR   0x00
#define TEMPERATURE_ADDR   0x01

#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(4800);

  // 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;

  // TEMPERATURE
  result = node.readHoldingRegisters(TEMPERATURE_ADDR   , 1);
  if (result == node.ku8MBSuccess)
  {
    Serial.print("Temperature: ");
    Serial.print(node.getResponseBuffer(0x0));
    Serial.println(" degC x 10");
  } else {
    printModbusError( result );
  }

  // HUMIDITY
  result = node.readHoldingRegisters(HUMIDITY_ADDR   , 1);
  if (result == node.ku8MBSuccess)
  {
    Serial.print("Humidity: ");
    Serial.print(node.getResponseBuffer(0x0));
    Serial.println(" % x 10");
  } 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;
  }
}
2 Likes

Vào Th 6, 23 thg 6, 2023 vào lúc 01:10 Sehunrahma via Arduino Forum <notifications@arduino.discoursemail.com> đã viết:

7_NPK_SENSOR_OFFLINE_SCREEN.ino (5.12 KB)

very useful thanks

Hi folks. Sorry I have not posted here for a while @Proncio . I lost access to the NPK sensor I was using few months ago so I ordered a new one. But I have not been able to get this sensor to work unlike the previous one I had. This sensor works at a baudrate of 4800. I was able to get readings from the sensor using the address or interrogation frames for the old sensor but they are not accurate. It seems that this new sensor has different interrogation frame or addresses for the correct data retrieval. Sadly, my sensor did not come with a manual so if anyone here uses an NPK sensor like the image in this post please let me know the code you used to get accurate readings from the sensor.

I take this opportunity to share the code that worked for me on my old sensor from JXCT. I was using an ESP 32

#include <SoftwareSerial.h>

#define MODBUS 18



const uint32_t TIMEOUT = 500UL;
unsigned long lastMillis = 0;

const byte nitro[]  = {0x01, 0x03, 0x00, 0x1e, 0x00, 0x01, 0xe4, 0x0c};
const byte phos[]   = {0x01, 0x03, 0x00, 0x1f, 0x00, 0x01, 0xb5, 0xcc};
const byte pota[]   = {0x01, 0x03, 0x00, 0x20, 0x00, 0x01, 0x85, 0xc0};   
const byte ph[]     = {0x01, 0x03, 0x00, 0x06, 0x00, 0x01, 0x64, 0x0b};
const byte temp[]   = {0x01, 0x03, 0x00, 0x13, 0x00, 0x01, 0x75, 0xcf};
const byte moist[]  = {0x01, 0x03, 0x00, 0x12, 0x00, 0x01, 0x24, 0x0F};
const byte ec[]     = {0x01, 0x03, 0x00, 0x15, 0x00, 0x01, 0x95, 0xce};



byte values[11];
byte values_moist[11];
byte values_temp[11];
byte values_ph[11];
byte values_ec[11];
byte values_nitro[11];
byte values_phos[11];
byte values_pota[11];

float val_moist;
float val_temp;
float val_ph;
int val_ec;
int val_ec_adj;
int val_nitro;
int val_phos;
int val_pota;


float temperature = 0.0f;
float humidity = 0.0f;
bool isDataReady = false;
uint16_t error;
float LastMaxHumidity = 0;

int err99 = 99;
int err1 = 1;
int err2 = 2;
int err3 = 3;


SoftwareSerial mod(23,22); // RX, TX ( Creates a new SoftwareSerial object )

void setup()
{
  Serial.begin(9600);
  mod.begin(9600);
  pinMode(MODBUS, OUTPUT);


}

void loop()
{
    lastMillis = millis();

  Serial.print("  Nitrogen: ");
  val_nitro = nitro_call();
  Serial.print(" = ");
  Serial.print( val_nitro );
  Serial.println(" mg/kg");
  delay(500);  

  Serial.print("  Phosphorus: ");
  val_phos = phos_call();
  Serial.print(" = ");
  Serial.print( val_phos );
  Serial.println(" mg/kg");
  delay(500);  

  Serial.print("  Potassium: ");
  val_pota = pota_call();
  Serial.print(" = ");
  Serial.print( val_pota );
  Serial.println(" mg/kg");
  delay(500);  
    
  Serial.print("  Moisture: ");
  val_moist = moisture();
  Serial.print(" = ");
  Serial.print(  val_moist );
  Serial.println(" %");     
   delay(500); 
  
  Serial.print("  Temp: ");
  val_temp = temperatura();
  Serial.print(" = ");
  Serial.print( val_temp );
  Serial.println(" °C");     
  delay(500);
  

if ( LastMaxHumidity < val_moist )  { LastMaxHumidity = val_moist; }
  else {LastMaxHumidity = LastMaxHumidity;}
val_ec_adj = val_ec * ( 1 + ( LastMaxHumidity - val_moist ) / 100 );
  
   
  Serial.print("  PH: ");
  val_ph = ph_call();
  Serial.print(" = ");
  Serial.print(  val_ph );
  Serial.println();     
  delay(1000);   

  Serial.print("  EC: ");
  val_ec = ec_call();
  Serial.print(" = ");
  Serial.print(  val_ec );
  Serial.println(" uS/m");     
  delay(1000);  

  Serial.println();  
}


float nitro_call(){

  uint32_t startTime = 0;
  uint8_t  byteCount = 0;
  
  digitalWrite(MODBUS,HIGH);
  
  delay(10);
  mod.write(nitro,sizeof(nitro));
    mod.flush();
    digitalWrite(MODBUS,LOW);
    

     startTime = millis();
  while ( millis() - startTime <= TIMEOUT ) {
    if (mod.available() && byteCount<sizeof(values_nitro) ) {
      values_nitro[byteCount++] = mod.read();
    }
  }
  uint16_t combined = ((values_nitro[3]<<8)|values_nitro[4]);
  return combined;
}


float phos_call(){

  uint32_t startTime = 0;
  uint8_t  byteCount = 0;
  
  digitalWrite(MODBUS,HIGH);
  
  delay(10);
  mod.write(phos,sizeof(phos));
    mod.flush();
    digitalWrite(MODBUS,LOW);
    
     startTime = millis();
  while ( millis() - startTime <= TIMEOUT ) {
    if (mod.available() && byteCount<sizeof(values_phos) ) {
      values_phos[byteCount++] = mod.read();
    }
  }
  uint16_t combined = ((values_phos[3]<<8)|values_phos[4]);
  return combined;
}

float pota_call(){

  uint32_t startTime = 0;
  uint8_t  byteCount = 0;
  
  digitalWrite(MODBUS,HIGH);
  
  delay(10);
  mod.write(pota,sizeof(pota));
    mod.flush();
    digitalWrite(MODBUS,LOW);
    

     startTime = millis();
  while ( millis() - startTime <= TIMEOUT ) {
    if (mod.available() && byteCount<sizeof(values_pota) ) {
      values_pota[byteCount++] = mod.read();
    }
  }
  uint16_t combined = ((values_pota[3]<<8)|values_pota[4]);
  return combined;
}

float ph_call(){
  float PH3and4;    
  uint32_t startTime = 0;
  uint8_t  byteCount = 0;

  digitalWrite(MODBUS,HIGH);
  
  delay(10);
  mod.write(ph,sizeof(ph));
    mod.flush();
    digitalWrite(MODBUS,LOW);
    
     startTime = millis();
  while ( millis() - startTime <= TIMEOUT ) {
    if (mod.available() && byteCount<sizeof(values_ph) ) {
      values_ph[byteCount++] = mod.read();
    }
  }
//  PH3and4 = (((values_ph[3] * 256.0) + values_ph[4])/10); // converting hexadecimal to decimal
PH3and4 = (( (values_ph[3]<<8)|values_ph[4])+0.0)/100;
//  uint16_t combined = ((values_ph[3]<<8)|values_ph[4])/10;
  return PH3and4;
}

float temperatura(){
  float TEMP3and4;    
  uint32_t startTime = 0;
  uint8_t  byteCount = 0;

  digitalWrite(MODBUS,HIGH);
  
  delay(10);
  mod.write(temp,sizeof(temp));
    mod.flush();
    digitalWrite(MODBUS,LOW);
    
     startTime = millis();
  while ( millis() - startTime <= TIMEOUT ) {
    if (mod.available() && byteCount<sizeof(values_temp) ) {
      values_temp[byteCount++] = mod.read();
    }
  }
    //TEMP3and4 = (((values_temp[3] * 256.0) + values_temp[4])/10); // converting hexadecimal to decimal
    TEMP3and4 = (( (values_temp[3]<<8)|values_temp[4])+0.0)/10;
  
  //float combined = ((values_temp[4])/10);
  return TEMP3and4;
}

float moisture(){
  float MOIST3and4;
  
  uint32_t startTime = 0;
  uint8_t  byteCount = 0;
  
  digitalWrite(MODBUS,HIGH);
  
  delay(10);
  mod.write(moist,sizeof(moist));
    mod.flush();
    digitalWrite(MODBUS,LOW);
    

     startTime = millis();
  while ( millis() - startTime <= TIMEOUT ) {
    if (mod.available() && byteCount<sizeof(values_moist) ) {
      values_moist[byteCount++] = mod.read();
      //printHexByte(values_moist[byteCount-1]);
    }
  }
  
//    MOIST3and4 = (((values_moist[3] * 256.0) + values_moist[4])/10); // converting hexadecimal to decimal
      MOIST3and4 = (( (values_moist[3]<<8)|values_moist[4])+0.0)/10; 
//      float combined = (((values_moist[3]<<8)|values_moist[4])/10 + 0.0);
  return MOIST3and4;
}


float ec_call(){
  float EC3and4;  
  uint32_t startTime = 0;
  uint8_t  byteCount = 0;
  

  digitalWrite(MODBUS,HIGH);
  
  delay(10);
  mod.write(ec,sizeof(ec));
    mod.flush();
    digitalWrite(MODBUS,LOW);
    

     startTime = millis();
  while ( millis() - startTime <= TIMEOUT ) {
    if (mod.available() && byteCount<sizeof(values_ec) ) {
      values_ec[byteCount++] = mod.read();
    }
  }
  
//    EC3and4 = (((values_ec[3] * 256.0) + values_ec[4])/10); // converting hexadecimal to decimal
  uint16_t combined = (values_ec[3]<<8)|values_ec[4];
  return combined;
}

Looking at the configuration "dots" on the side of the sensor I would say that you have the 7 parameter version.

I assume that you discovered this by chance as you say you didn't get a manual with your sensor?

Hi, Yes I forgot to mention that this is a 7 parameter version @markd833 . I'm dissapointed that the manufacturer did not add a manual because I cannot use it without the addresses but if you have any suggestions, I would be glad to hear them

Does the site you got it from mention a manufacturer name or a model number?

I have collected a few user manuals for the various NPK type sensors whilst helping other forum members. Here's what I have so far relating to the 7-parameter variant:
THCPH-S (RS485 type)_5PIN.pdf (350.7 KB)
image

I would suggest trying to locate a register that reports back temperature as you would have a pretty good idea of the expected value. It will likely be reported back 10x the actual temperature, so 24.5C is reported as 245 decimal.

I did try and get an online Chinese to English OCR program to try and decode the line of Chinese text. Apparently it says the same as the plain English text just above it.