Modbus over Arduino

I am using a PID500 temperature controller. I want to read and write the values of the PID control using Modbus on Arduino. To convert the data from RS485, I am using an RS485 to TTL converter. I just want to read and write the set low point and set high point. I am also using Modbus Poll software where I get the values, but when I try to connect with Arduino, it only gives me wrong values. One more thing: I am not able to identify the address of the holding register and input register. Can you please help me with this?

This is the code:

#include <ModbusMaster.h>
#include <SoftwareSerial.h>
//Rx/Tx is hooked up to pins 5, 4

uint8_t temp;
SoftwareSerial mySerial(5,4); // RX, TX
ModbusMaster node;
void setup() {
// Modbus communication runs at 9600 baudrate
Serial.begin(9600);
mySerial.begin(9600);
// Modbus slave ID 1
node.begin(1, mySerial);
}
void loop()
{
tempRead();

}

void tempRead(){

temp = node.readHoldingRegisters(0x001e, 10);
if (temp == node.ku8MBSuccess)
{
uint16_t temp = node.getResponseBuffer(0x01);
Serial.println(temp);
delay(2000);
}
}

Modbus poll software results of high set point and low set point.

Welcome to the forum

Please follow the advice given in the link below when posting code, in particular the section entitled 'Posting code and common code problems'

Use code tags (the < CODE/ > icon above the compose window) to make it easier to read and copy for examination

https://forum.arduino.cc/t/how-to-get-the-best-out-of-this-forum

In my experience the easiest way to tidy up the code and add the code tags is as follows

Start by tidying up your code by using Tools/Auto Format in the IDE to make it easier to read. Then use Edit/Copy for Forum and paste what was copied in a new reply. Code tags will have been added to the code to make it easy to read in the forum thus making it easier to provide help.

Can you be more specific? Which manufacturer? A quick google search suggests maybe from a company called Selec? If that's correct, then a quick search of their documents produced the following document:

PID500-U-MODBUS-address.pdf (1.5 MB)

Hopefully that may be of help.

Is it an auto switching converter? I don't see anything in your code to indicate that you are manually controlling the RS485 transceiver module.

PID500-U-C-1 temprature controller

This is the modified code. Now, I am able to read the values of the set resistor only and have also attempted to write them. However, when I attempt to write any value, only zero is written.

<#include <ModbusMaster.h>
#include <ArduinoRS485.h>
#include <ArduinoModbus.h>

// Modbus communication settings
#define TIMEOUT 1000 // Communication timeout in ms

ModbusMaster node;

void setup() {
Serial.begin(9600); // Initialize serial communication for debugging
Serial1.begin(9600); // Initialize Modbus serial communication on Serial1 (RX: 13, TX: 14)
node.begin(1, Serial1); // Set Modbus slave ID and use Serial1 for communication
}

void loop() {
int temp[100]; // Define an array to store the received data
int result;

// Read data from PID500 controller
result = node.readHoldingRegisters(0x01, 100); // Read 100 registers starting from address 0x01 (example)
if (result == node.ku8MBSuccess) {
// Read the received data into the array
for (int i = 0; i < 100; i++) {
temp[i] = node.getResponseBuffer(i);
}

// Print the received data
for (int i = 0; i < 100; i++) {
  Serial.print("Data read from register ");
  Serial.print(i);
  Serial.print("  : ");
  Serial.println(temp[i]);
  delay(10);
}

} else {
Serial.println("Failed to read data from PID500");
}

result = node.readHoldingRegisters(0x01, 100); // Read 2 registers starting from address 0x0000 (example)
if (result == node.ku8MBSuccess) {
// Read the received data into the array
for (int i = 0; i < 100; i++) {
temp[i] = node.getResponseBuffer(i);
}

result = node.writeMultipleRegisters(0x1f, 0x02);  // Write to holding register 0x0000 (example)
if (result == node.ku8MBSuccess) {
  Serial.println("Setpoint successfully written to holding register");
} else {
  Serial.println("Failed to write setpoint to holding register");
}

delay(3000);  // Delay between readings

}
} />

Can you tell me how to read
SPLL (set point high level)
SPHL ( set point low level)

How to find their address nothing is there in manual.



@sumitingale You are ignored again rules for inserting code into the forum. Please return and edit your previous post.
Read the forum guidelines to see how to properly insert the code and some other good information on making a good post.

Thanks a lot :+1::blush:

Which Arduino are you using?

In post #1, you were using SoftwareSerial, which I assumed meant that you were using an UNO.

In post #4, you appear to be using a hardware serial port. Your code in post #4 includes ArduinoRS485, which I think is for the MKR series of Arduino boards.

So, tell us which Arduino you are using, which RS485 module you are using (perhaps a MKR-485?). Picture 1 in post #4 appears to show a MAX485 based RS-485 module . And also a hand drawn sketch of how you have connected everything together.

And please fix your code in post #4 as previously requested.

I am using mkr 1010 board.

And:

Tried to read and write data using ModbusMaster Library with readHoldingResisters() , writeMultipleResisters() but I only able to read.

Please help me.

Diagram and code is attached below

MODBUS_READ_WRITE_.ino (1.78 KB)

The ArduinoRS485 & ArduinoModbus are the correct libraries for the MKR boards. I think the ArduinoModbus library includes the ArduinoRS485 library so you don't need to include it here.

However, it looks like your code is using the library functions from ModbusMaster - which I believe is compatible with the MKR series of boards. I would remove the includes for ArduinoRS485 and ArduinoModbus to avoid confusion and conflict.

Is that image in your picture representative of the RS485 board you have? It's an auto switching board but it uses a MAX485 which is designed for 5V systems.

EDIT: Oops didn't see your attached sketch!

result = node.readHoldingRegisters(0x01, 100);  // Read 100 registers starting from address 0x01 (example)

Does ModbusMaster work when requesting 100 registers? I've never tried that many myself.

Looking at the manual I provided the link to (see post #3), maybe something like:

result = node.readHoldingRegisters(0x44, 2); 

If you have done this before, could you please provide me with your code for reference? I have tried every possible way, but it's not working.

I think there is a problem with the ModbusMaster library because the writeMultipleRegisters function accepts two parameters: one for the address and another for the number of registers. However, there is no parameter for writing the value to the CONTROLLER. Do you have any other libraries except ArduinoRS485 and ArduinoModbus or resources that work properly? Please let me know.. it will be very helpful..

Thank you!

Sorry, I've not used the writeMultipleRegisters function.

I looked at the source code to the writeMultipleRegisters function in the library and it says:

This function code is used to write a block of contiguous registers (1 
to 123 registers) in a remote device.

The requested written values are specified in the transmit buffer. Data 
is packed as one word per register.

From that I would suggest you make multiple calls to setTransmitBuffer. The documentation says:

Place data in transmit buffer.

@see ModbusMaster::clearTransmitBuffer()
@param u8Index index of transmit buffer array (0x00..0x3F)
@param u16Value value to place in position u8Index of transmit buffer (0x0000..0xFFFF)
@return 0 on success; exception number on failure

If you wanted to write the 3 values 0x1234, 0x5678 & 0x9ABC to 3 consecutive registers starting with register 0x0005, then I would guess that you would need a piece of code like this:

node1.clearTransmitBuffer();
node1.setTransmitBuffer(0x00, 0x1234);
node1.setTransmitBuffer(0x01, 0x5678);
node1.setTransmitBuffer(0x02, 0x9ABC);
node1.writeMultipleRegisters(0x0005, 3);

Thank you for your response.

I have tried this as well, but it's not working.
I think there is an issue with the function definition in the library because we are only passing the address and the number of consecutive register quantities that we want to write.

With writeSingleRegister, there are parameters for address and value, so we are passing values, but this function is not working.
If there's anything you can help with, please check.

Have you filled the transmit buffer with the values you want to write before you called writeMultipleRegisters?

Yes I did as you suggested still not working.

Can you please check writeSingleRegister

I will have a look today and see if I can figure out the writeSingleRegister and writeMultipleRegisters. I have an UNO that I can try it on, but it should be the same for a MKR board too.

Yes, ModbusMaster library is compatible with Arduino UNO. You can try.

Ok, here is a bit of code written for my UNO that writes to a single register and also writes to multiple registers in one message:

// Modbus device simulated using ModRSsim2 available from https://sourceforge.net/projects/modrssim2/
// 
#include <ModbusMaster.h>
#include <AltSoftSerial.h>

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

  // 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 (change to suit your sensor)
  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;

  // empty the transmit registers
  node.clearTransmitBuffer();

  node.writeSingleRegister( 2, 1000 );
  printModbusError( result );
  
  // write the 3 values 1, 2 & 3 to registers 7, 8 & 9
  node.setTransmitBuffer(0x00, 1);
  node.setTransmitBuffer(0x01, 2);
  node.setTransmitBuffer(0x02, 3);
  result = node.writeMultipleRegisters(0x0007, 3);
  printModbusError( result );

  Serial.println();
  delay(5000);
}

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

The line of code:

  node.writeSingleRegister( 2, 1000 );

results in the following bytes appearing on the RS485 bus:

01 06 00 02 03 E8 28 B4 00

This is the correct message for the writeSingleRegister command if you ignore the trailing 00.

The lines of code:

  node.setTransmitBuffer(0x00, 1);
  node.setTransmitBuffer(0x01, 2);
  node.setTransmitBuffer(0x02, 3);
  result = node.writeMultipleRegisters(0x0007, 3);

results in the following bytes appearing on the RS485 bus:

01 10 00 07 00 03 06 00 01 00 02 00 03 8b 5b

This is the correct message for the writeMultipleRegisters command.

However, I also saw a lot of messages like this one when using the writeMultipleRegisters command:

40 04 07 00 03 06 00 01 00 02 00 03 8b 5b

The first 2 bytes are garbage, and should be the 3 bytes 01 10 00.

So, even on my UNO, there seems to be an issue with the writeMultipleRegisters command.

I have used the Max485 TTL to RS485 converter, which has TX, Rx, GND, and VCC pins, but the Enable pins are not present on that module.

Do you think this could be the reason why I am not able to perform write operations?

If yes, then I will try using a module with Enable pins.

Thank you, sir, for your help.