How do I read registers in different blocks? RTU between slave and master.
pllagunos opened this issue · comments
I want to be able to read holding registers in "blocks" (i.e a group of registers) and also as single registers. Here's my attempt at doing so:
Slave code on an ESP32 s3
#include <Arduino.h>
#include <ModbusRTU.h>
// Define your application variables
float PV;
float SP;
// Create ModbusRTU object
ModbusRTU mb;
uint16_t cbRead(TRegister* reg, uint16_t val) {
uint16_t address = reg->address.address;
Serial.printf("Address read: %d\n", address);
switch (address) {
case 1:
PV = random(0, 100) / 10.0;
Serial.printf("PV: %.2f\n", PV);
return static_cast<uint16_t>(PV * 10);
case 2:
SP = random(0, 100) / 10.0;
Serial.printf("SP: %.2f\n", SP);
return static_cast<uint16_t>(SP * 10);
case 3:
return static_cast<uint16_t>(random(0,100));
default:
return 0;
}
}
void setup() {
Serial.begin(115200); // Configure the serial port for Modbus communication
Serial2.begin(115200, SERIAL_8N1, 32, 26);
mb.begin(&Serial2, 13);
mb.setBaudrate(115200);
mb.slave(1); // Set Modbus ID to 1
// Add holding registers and set callbacks
mb.addHreg(1, 0xF0F0, 3);
mb.onGetHreg(1, cbRead, 3);
}
void loop() {
mb.task(); // Process Modbus communication
delay(10);
}
Master code on a ESP32 devkit
#include <ModbusRTU.h>
#define SLAVE 1
// Create ModbusRTU object
ModbusRTU mb;
void setup() {
Serial.begin(115200); // Configure the serial port for Modbus communication
Serial2.begin(115200, SERIAL_8N1, 19, 20);
Serial2.setTimeout(200);
mb.begin(&Serial2, 21);
mb.setBaudrate(115200);
mb.master();
}
void loop() {
static uint32_t ts;
if (millis() - ts > 1500) {
ts = millis();
uint16_t res[2];
float PV;
float SP;
// Read PV & SP registers from the slave
if (mb.readHreg(SLAVE, 1, res, 2)) {
PV = res[0] / 10.0;
SP = res[1] / 10.0;
Serial.printf("PV: %.2f\n", PV);
Serial.printf("SP: %.2f\n", SP);
}
else {
Serial.println("Failed to read PV and SP");
}
// Read single register from the slave, address 3
uint16_t var;
if (mb.readHreg(SLAVE, 3, &var, 1)) {
Serial.printf("var: %d\n", var);
}
else {
Serial.println("Failed to read var");
}
}
mb.task(); // Process Modbus communication
delay(10);
}
The result is this:
15:35:29.870 -> PV: 6.80
15:35:29.870 -> SP: 1.10
15:35:29.870 -> Failed to read var
15:35:31.348 -> PV: 0.30
15:35:31.348 -> SP: 4.80
15:35:31.348 -> Failed to read var
15:35:32.858 -> PV: 0.60
15:35:32.858 -> SP: 0.10
If I comment the code related to reading PV and SP, var is succesfully read:
15:33:25.531 -> var: 76
15:33:27.043 -> var: 51
15:33:28.555 -> var: 88
15:33:30.033 -> var: 48
I tried adding a delay between the two but it doesn't work.
Main point is that mb.readHreg()
just sends request and returns. That is query runs async. Response processed bt mb.task()
. mb.slave()
returns true
while request is in-progress.
Simplest way to resolve you problem is to wait the response (or timeout)
// Read PV & SP registers from the slave
if (mb.readHreg(SLAVE, 1, res, 2)) {
while (mb.salve()) { // Wait response received
yield();
mb.task();
}
PV = res[0] / 10.0;
SP = res[1] / 10.0;
Serial.printf("PV: %.2f\n", PV);
Serial.printf("SP: %.2f\n", SP);
}
else {
Serial.println("Failed to read PV and SP");
}
// Read single register from the slave, address 3
uint16_t var;
if (mb.readHreg(SLAVE, 3, &var, 1)) {
while (mb.salve()) { // Wait response received
yield();
mb.task();
}
Serial.printf("var: %d\n", var);
}
else {
Serial.println("Failed to read var");
}