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.
Other
534 stars 190 forks source link

How do I read registers in different blocks? RTU between slave and master. #353

Closed pllagunos closed 3 weeks ago

pllagunos commented 5 months ago

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.

emelianov commented 5 months ago

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