Trouble using the KellerModbus library

Hello everyone,
I am trying to communicate with a PR-36XW pressure sensor from Keller using modbus with an Arduino UNO. I am using the SensorModbusMaster.h library.
My sensor works well because I managed to get datas with a dedicated software from Keller using a USB-RS485 converter. So nothing wrong with the sensor.
I want to read the pressure value at the register adress 0x0002 with the function code 3, the ID of my sensor is 2. 2 registers are required to read the pressure value.


// ---------------------------------------------------------------------------
// Include the base required libraries
// ---------------------------------------------------------------------------
#include <Arduino.h>
#include <SensorModbusMaster.h>

// ---------------------------------------------------------------------------
// Set up the sensor specific information
//   ie, pin locations, addresses, calibrations and related settings
// ---------------------------------------------------------------------------

// Define the sensor's modbus address
byte modbusAddress = 0x02;   // The sensor's modbus address, or SlaveID
long modbusBaudRate = 9600; // The baud rate the sensor uses

// Define pin number variables
const int DEREPin = 5;       // The pin controlling Recieve Enable and Driver Enable
                              // on the RS485 adapter, if applicable (else, -1)
                              // Setting HIGH enables the driver (arduino) to send text
                              // Setting LOW enables the receiver (sensor) to send text

// Construct software serial object for Modbus
#if defined(ARDUINO_AVR_UNO)
// The Uno only has 1 hardware serial port, which is dedicated to comunication with the computer
// If using an Uno, you will be restricted to using AltSofSerial or SoftwareSerial
#include <SoftwareSerial.h>
const int SSRxPin = 1; // Recieve pin for software serial (Rx on RS485 adapter)
const int SSTxPin = 0; // Send pin for software serial (Tx on RS485 adapter)
SoftwareSerial modbusSerial(SSRxPin, SSTxPin);
#else
// This is just a assigning another name to the same port, for convienence
// Unless it is unavailable, always prefer hardware serial.
HardwareSerial* modbusSerial = &Serial1;
#endif

// Construct the modbus instance
modbusMaster modbus;

// ---------------------------------------------------------------------------
// Main setup function
// ---------------------------------------------------------------------------
void setup()
{
    // Set various pins as needed
    if (DEREPin >= 0)
    {
        pinMode(DEREPin, OUTPUT);
    }
    
    // Turn on the "main" serial port for debugging via USB Serial Monitor
    Serial.begin(57600);

    // Turn on your modbus serial port
#if defined(ARDUINO_AVR_UNO)
    modbusSerial.begin(modbusBaudRate);
    // NOTE:  Software serial only supports 8N1
#else
    Serial1.begin(modbusBaudRate, SERIAL_8O1);
    // ^^ use this for 8 data bits - odd parity - 1 stop bit
    // Serial1.begin(modbusBaudRate, SERIAL_8E1);
    // ^^ use this for 8 data bits - even parity - 1 stop bit
    // Serial1.begin(modbusBaudRate, SERIAL_8N2);
    // ^^ use this for 8 data bits - no parity - 2 stop bits
    // Serial1.begin(modbusBaudRate);
    // ^^ use this for 8 data bits - no parity - 1 stop bits
    // Despite being technically "non-compliant" with the modbus specifications
    // 8N1 parity is very common.
#endif

    // Turn on debugging, if desired
    //modbus.setDebugStream(&Serial);

    // Start the modbus instance
    modbus.begin(modbusAddress, modbusSerial, DEREPin);

// ---------------------------------------------------------------------------
// Main setup function
// ---------------------------------------------------------------------------
void loop()
{

    // All values will be read as bigEndian

    uint16_t pressure = 0;

  
    pressure = modbus.float32FromRegister(0x03, 0x02, bigEndian);

    Serial.print("Pressure:");
    Serial.println(temperature);
    Serial.println();

    }

Here is the response :

pressure:0

RS485 Driver/Master Tx Enabled
RS485 Receiver/Slave Tx Enabled
Raw Request >>> {0x02, 0x03, 0x00, 0x02, 0x00, 0x02, 0x65, 0xF8}
Raw Response (63 bytes) <<< {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
Response is not from the correct modbus slave!

The raw request seems to be okay to me, but I don't understand why I am receiving a response that weird.
Do you have any idea of where the problem is ? Is it hardware or soft?

Thank you

not sure but found this documentation online.
Capture

if that is indeed the case, my guess is that the CRC byte order is not right as the library you are using follows 'standard' Modbus rules.

since you have the decicated keller software, maybe you can confirm this by monitoring the raw data stream when you send a read request to the sensor.

hope that helps...

Thanks for your answer.
I've check and the data stream is the same in both the software (where it works) and the arduino. The CRC byte order is good.

I have tried to disconnect the power of the sensor and the response is the same... Maybe it has something with the hardware.

In my RS485 board for arduino I have three jumpers , one for the termination resistor the others are for bias enabling. I don't know what is bias enabling and if I have to remove the jumpers?

this makes no sense:

why are you using softserial on HW Serial pins?
In setup you init (begin) again HW-Serial.
Doesn't seem to be a good idea for me.

--> use other pins for soft serial.
if it still doesn't work:
--> make a clear drawing what is connected to what
--> make clear pictures to show us what you have wired
--> upload the new sketch
--> upload the datasheet of your sensor
--> upload the datasheet / link to your Arduino RS485 "module"

Thanks for your answer,

The comments says "Recieve pin for software serial (Rx on RS485 adapter)". In my adapter, RX is connected to D1 and TX to D0. This is why I put :

I don't think I can because of how the hardware is made. here is the link for the RS485 converter I use : RS485 click 5V - Breakout board for ADM485 transciever IC

Since I can't upload the datasheet and more than one link (because I am new to the forum), you will easily find it on the web : the sensor is the PR-36XW from KELLER-DRUCK and the the shield is the Arduino Uno Click Shied from Mikroe.

Thank's for your time

that's your work, but anyhow, here I found that mikro shield:
https://www.tme.eu/Document/00f93f24d10ffe3efbd8b9d6af926f4c/mikroe-1581.pdf

If you connect it to D0/D1 you have literally no chance to debug your program.

I suggest you connect the RS485 with dupont wires to GND/5V and free pins for SoftSerial. D5 for TX enable is ok.

and again: Softserial on D0/D1 for RS485 and the parallel usage of HW Serial for printouts on your Serial Monitor makes no sense.

Ok I understand my mistake.
So now I am trying with dupont wires according to the following diagram :


This is not the same error but it still doesn't work :

RS485 Driver/Master Tx Enabled
RS485 Receiver/Slave Tx Enabled
Raw Request >>> {0x02, 0x03, 0x00, 0x02, 0x00, 0x02, 0x65, 0xF8}
No response received.
pressure:0

I have only changed the pin numbers in the code :

const int SSRxPin = 11; // Recieve pin for software serial (Rx on RS485 adapter)
const int SSTxPin = 12; // Send pin for software serial (Tx on RS485 adapter)

since we know the full command to send and u are just using the UNO, might I suggest you simply send the RAW request to prove confrim it is indeed a software issue. something like this may be:
(Compiles, NOT tested!)

#include <SoftwareSerial.h>

SoftwareSerial RS485(11, 12); // RX, TX

uint8_t Raw_Req[8] = {0x02, 0x03, 0x00, 0x02, 0x00, 0x02, 0x65, 0xF8};

void setup()
{
  // Open serial communications and wait for port to open:
  Serial.begin(115200);

  // set the data rate for the SoftwareSerial port for RS485 communication
  RS485.begin(9600);

  Serial.println("Ready!");
}

void loop() // run over and over
{
  if (RS485.available()) {
    //print out any received response from sensor
    Serial.print("\nReceived Response: ");
    while (RS485.available()) {
      uint8_t val = RS485.read();
      Serial.print(val, HEX);
      Serial.print(" ");
    }
  }

  Serial.print("\nSending New Request: ");
  for (uint8_t i = 0; i < 8; ++i) {
    RS485.write(Raw_Req[i]);
    Serial.print(Raw_Req[i], HEX);
    Serial.print(" ");

  }

  delay(1000);

}

With the above code, you should be able to see what you are sending and any response in the Serial monitor of the IDE (make sure you have the correct baudrate setting in the serial monitor!)

If you still don't get any response try swapping the Software serial connections and try again to rule out connection issue :wink:

hope that helps...

Thanks for the answer again,

if (RS485.available()) 

is always 0, so it's not working... Don't know why. I am trying with AltsoftSerial , I'll keep you in touch

Same problem with altsoftSerial using the pin 9 and 8... Could be an hardware problem . Could the Bias and termination resistor be the cause ?

just to confrim, did you try swapping the software serial connections ie did you try

D12 <-> RS485_Click TX
D11 <-> RS485_Click RX

AND

D11 <-> RS485_Click TX
D12 <-> RS485_Click RX

was there no response in both cases? that would rule out any connection issue if there is/was one...

the baudrate to the R485 is set to 9600 in the code I shared, that is correct, right?

hope that helps...

Yes I confirm I tried swapping RX and TX but this was the same error.
Yes the baudrate must be 9600 for the pressure sensor, it was working with the modbus software.

a testprogram like #9 is a good idea, but you need to pullup the TX enable pin D5 before writing and disable it after sending to receive data.

don't forget to declare pinMode output of D5 in setup().

good point! forgot about that!

Revised code:
(compiles, NOT tested!)

#include <SoftwareSerial.h>

SoftwareSerial RS485(11, 12); // RX, TX
const uint8_t DEREPin = 5;

uint8_t Raw_Req[8] = {0x02, 0x03, 0x00, 0x02, 0x00, 0x02, 0x65, 0xF8};

void setup()
{
  // Open serial communications and wait for port to open:
  Serial.begin(115200);

  pinMode(DEREPin, OUTPUT);

  // set the data rate for the SoftwareSerial port for RS485 communication
  RS485.begin(9600);

  Serial.println("Ready!");
}

void loop() // run over and over
{
  if (RS485.available()) {
    //print out any received response from sensor
    Serial.print("\nReceived Response: ");
    while (RS485.available()) {
      uint8_t val = RS485.read();
      Serial.print(val, HEX);
      Serial.print(" ");
    }
  }

  digitalWrite(DEREPin, HIGH); // enable pin R/T before writing
  delay(1);
  Serial.print("\nSending New Request: ");
  for (uint8_t i = 0; i < 8; ++i) {
    RS485.write(Raw_Req[i]);
    Serial.print(Raw_Req[i], HEX);
    Serial.print(" ");
  }
  digitalWrite(DEREPin, LOW);

  delay(1000);

}

hope that helps...

What do you mean by disable it ? Should I put a resistor between D5 and +5V when it's sending and remove it when it's receiving ?

It does the same... damn

exactly like sherzaad has done ... put it to HIGH before transmission, put it to low after transmission.

please upload

  • pictures of your setup - we must be able to see and identify each wire and each module.
  • make a screenshot of your serial monitor.

I may have missed it but which of the 2 positions are you plugging your click board into on the shield? Position 1 uses D6 and position 2 uses D5 to control the tx/rx state of the line driver.

I am using the second one , so with tx/rx controlled by D5