Reading register of Modbus RTU sensor with Arduino Uno using TTL-RS485 converter

Hi everyone,

I want to read the differential pressure register at address 8 on the Belimo 22ADP-15Q Using an Arduino Uno with a TTL -RS485 converter.

Following code only outputs "1111111111111111".

Thanks in advance for any help!

#include <SoftwareSerial.h>

#define RX_PIN 10
#define TX_PIN 11
#define DERE_PIN 12

SoftwareSerial modbusSerial(RX_PIN, TX_PIN);

void setup() {
  pinMode(DERE_PIN, OUTPUT);
  digitalWrite(DERE_PIN, LOW);
  modbusSerial.begin(9600);
  Serial.begin(9600);
}

void loop() {
  byte address = 1;
  byte function = 3;
  int startReg = 8;
  int numRegs = 1;
  
  byte request[] = {address, function, (startReg >> 8) & 0xFF, startReg & 0xFF, (numRegs >> 8) & 0xFF, numRegs & 0xFF, 0x24, 0x68};
  // Note: the last two bytes are the CRC checksum, which is calculated by the sensor
  
  digitalWrite(DERE_PIN, HIGH);
  
  modbusSerial.write(request, sizeof(request));
  
  uint16_t response = modbusSerial.read() << 8;
  response |= modbusSerial.read();
  Serial.println(response,BIN);
  
  digitalWrite(DERE_PIN, LOW);
  delay(1000);  // wait a second before sending another request
}

Wiring:

Is the hard coded checksum in your canned modbus message correct for the contents of the message?

You also have to set your RE/DE control pin low once the transmission completes before you can read the reply from your sensor, otherwise the sensor can't take control of the RS485 bus and respond.

That I don't know I also think the error is in the request, but I don't see where.
I also tried using libraries so I don't have to compose the request myself, but there I get an "E2" HEX error.

#include<ModbusMaster.h>
#include <SoftwareSerial.h>

#define RX_PIN 11
#define TX_PIN 10
#define MAX485_REDE_PIN 12
SoftwareSerial modbusSerial(RX_PIN, TX_PIN);

ModbusMaster node; //object node for class ModbusMaster

void preTransmission() //Function for setting stste of Pins DE & RE of RS-485
{
  digitalWrite(MAX485_REDE_PIN, 1);
}
void postTransmission()
{
  digitalWrite(MAX485_REDE_PIN, 0);
}

void setup()
{
  pinMode(MAX485_REDE_PIN, OUTPUT);

  digitalWrite(MAX485_REDE_PIN, 0);
  
  Serial.begin(9600); //Baud Rate as 9600
  Serial.println( MAX485_REDE_PIN );
  
  modbusSerial.begin(9600); //Baud Rate as 9600
  node.begin(1, modbusSerial); //Slave ID as 1 on SoftwareSerial

  node.preTransmission(preTransmission); //Callback for configuring RS-485 Transreceiver correctly
  node.postTransmission(postTransmission);
}

void loop()
{
  uint8_t result = node.readHoldingRegisters(8,1); // Reads Holding Register 8
  
  Serial.println(result,HEX);

  if (result == node.ku8MBSuccess)
  {
    Serial.print("data : ");
    Serial.print(node.getResponseBuffer(0));
  }
  Serial.print("\n");

  delay(3000);
}

E2 is the error code for a response timeout - i.e. the remote device didn't respond.

Do you have a common ground between your Arduino and the sensor?

Also, I thing you have your RX and TX swapped over. If RX is on pin 10, then that should connect to the RO pin on the MAX485 module. TX should go to the DI pin.

No, the sensor is powered through it's own 24VDC power supply. I just added the wiring to the post.

Oh you're correct The RX and TX were swapped and I should also put a common ground between GND of the sensor and the GND of the Uno?

There's debate of the addition of the common ground. Some say it has to be there, others say no. Over workbench type distances you probably do not need it. The specifics on why you need it are a bit over my head. I do note that there are several questions regarding this if you do a google search for "RS485 common ground".

1 Like

I changed the pin definition so the RX and TX are connected to the correct pin on the converter. And added a common ground. Still get the E2 Error.

Looking at the datasheet, what have you got the DIP switches set to.

The Modbus DIPS: DIP1 -> Only 1 is on. DIP2 -> Default (All off).
ON DIP: Only 5 is on.
(Oh just noticed that the second datasheet is only available in Dutch, But I don't think you need that one for the Modbus communication)

On DIP2, I wonder if the "no parity 1 stop bit" (4 & 5 ON) selection would help.

Also it may be worth setting DIP2 SW1 to ON to enable termination. It probably won't matter much if you are experimenting on the bench.

1 Like

It responds (No E2 error). But the data of my requested register is 0?

I do have the problem that there is no output on the analog output. Could be that It's a faulty sensor. But the fact it responds and doesn't give the E2 is already a great achievement haha.

That's good. You have established comms with the sensor.

I would try with querying a parameter that you know what the value should be - roughly!

What does the sensor respond with if you were to query temperature, which the manual says is at register address 0?

You could also try register 500 which the manual says should return the value 0x0700 (assuming the 16 subscript means base 16 - i.e. hexadecimal - and not a note 16.

Register address 0? With this code it says "data: 0".

#include<ModbusMaster.h>
#include <SoftwareSerial.h>

#define RX_PIN 11
#define TX_PIN 10
#define MAX485_REDE_PIN 12
SoftwareSerial modbusSerial(RX_PIN, TX_PIN);

ModbusMaster node; //object node for class ModbusMaster

void preTransmission() //Function for setting stste of Pins DE & RE of RS-485
{
  digitalWrite(MAX485_REDE_PIN, 1);
}
void postTransmission()
{
  digitalWrite(MAX485_REDE_PIN, 0);
}

void setup()
{
  pinMode(MAX485_REDE_PIN, OUTPUT);

  digitalWrite(MAX485_REDE_PIN, 0);
  
  Serial.begin(9600); //Baud Rate as 9600
  Serial.print("REDE_pin: ");
  Serial.println(MAX485_REDE_PIN);
  
  modbusSerial.begin(9600); //Baud Rate as 9600
  node.begin(1, modbusSerial); //Slave ID as 1 on SoftwareSerial

  node.preTransmission(preTransmission); //Callback for configuring RS-485 Transreceiver correctly
  node.postTransmission(postTransmission);
}

void loop()
{
  uint8_t result = node.readHoldingRegisters(0,1);

  if (result == node.ku8MBSuccess)
  {
    Serial.print("data : ");
    Serial.print(node.getResponseBuffer(0));
  }
  Serial.print("\n");

  delay(3000);
}

I would have expected register 0 to give you temperature. Regardless of the units of temperature I would have thought you should get some non-zero value back.

A little update. I'm finally getting some data back from register 8 (differential pressure). When I blow on one end the value seems to go up and cap at a certain value. Which looks good. Going to try and translate it to the actual value now.

It works! Thanks for all the help Mark.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.