emelianov / modbus-esp8266

Most complete Modbus library for Arduino. A library that allows your Arduino board to communicate via Modbus protocol, acting as a master, slave or both. Supports network transport (Modbus TCP) and Serial line/RS-485 (Modbus RTU). Supports Modbus TCP Security for ESP8266/ESP32.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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");
    }