Ir al contenido

← Maestro Modbus RTU en un PLC ESP32 sobre RS-485

Monitorización textil (tejeduría)ESP32 PLCModbus RTURS485Comunicación

Maestro Modbus RTU en un PLC ESP32 sobre RS-485 — ejemplo completo

Maestro Modbus RTU en un PLC ESP32: lecturas de registros FC3 sobre RS-485 con timeouts, conversión a valores de 32 bits y salida JSON lista para MQTT.

Programa completo y ejecutable para el ESP32 PLC (modbus-rtu-master-32bit.ino): incluye cabecera de conexionado, requisitos y notas de integración.

Descarga el pack completo del proyecto — gratisEste ejemplo + los relacionados + lista de materiales

Vista de solo lectura.

/*
 * COMPLETE EXAMPLE — Modbus RTU master with timeout and 32-bit conversion
 *
 * Hardware: ESP32 PLC (Industrial Shields, with RS-485 port)
 * Based on: textile monitoring project, ModbusModule.cpp
 *
 * Wiring:
 *   RS-485 A/B  Bus towards the Modbus slave (power analyzer, VFD,
 *               machine controller...) — twisted pair, 120 Ω termination
 *
 * Architecture:
 *   1. modbusRequestFC3(): generic holding-register read with timeout —
 *      if the slave does not answer, it returns an empty vector and the
 *      loop keeps running (never blocked by a switched-off device)
 *   2. Helpers to combine 2 16-bit registers into 32-bit values,
 *      signed and unsigned (counters, energies, totalizers)
 *   3. Periodic poll every 5 s and JSON packaging ready for MQTT
 *
 * Works together with other catalog examples:
 *   - mqtt-events-sd-buffering.ino (JSON output)
 *   - sd-daily-file-datalogging.ino (offline backup)
 */

#include 
#include 

const uint32_t BAUDRATE   = 9600;
const uint8_t  SLAVE_ID   = 1;       // slave address on the bus
const uint16_t REG_BASE   = 0x0000;  // first holding register to read
const uint16_t NUM_REGS   = 4;       // 4 regs = 2 32-bit values
const uint32_t TIMEOUT_MS = 200;
const uint32_t POLL_PERIOD_MS = 5000;

ModbusRTUMaster master(RS485);       // RS-485 port of the PLC
uint32_t tPoll = 0;

// ------------------------------------------------- FC3 request with timeout
// Returns the n requested registers, or an empty vector on error/timeout
std::vector modbusRequestFC3(uint8_t slave, uint16_t addr,
                                       uint16_t n, uint32_t timeout) {
  std::vector out;
  if (!master.readHoldingRegisters(slave, addr, n)) return out;

  uint32_t t0 = millis();
  while (millis() - t0 < timeout) {
    if (master.isWaitingResponse()) {
      ModbusResponse r = master.available();
      if (r) {
        if (!r.hasError())
          for (uint16_t i = 0; i < n; i++) out.push_back(r.getRegister(i));
        return out;                  // response (good or with error): exit
      }
    }
  }
  return out;                        // timeout: empty vector
}

// ------------------------------------------------- 16+16 -> 32-bit conversion
// Big-endian word order (high first); swap the order if the slave
// uses little-endian word order
uint32_t regsToU32(uint16_t hi, uint16_t lo) {
  return ((uint32_t)hi << 16) | lo;
}

int32_t regsToS32(uint16_t hi, uint16_t lo) {
  return (int32_t)regsToU32(hi, lo);  // reinterpret as two's complement
}

void setup() {
  Serial.begin(115200);
  RS485.begin(BAUDRATE, SERIAL_8N1);
  master.begin(BAUDRATE);
  Serial.println("Modbus RTU master ready at " + String(BAUDRATE) + " bps");
}

void loop() {
  if (millis() - tPoll >= POLL_PERIOD_MS) {
    tPoll = millis();

    std::vector regs =
        modbusRequestFC3(SLAVE_ID, REG_BASE, NUM_REGS, TIMEOUT_MS);

    if (regs.size() < NUM_REGS) {
      Serial.println("Modbus: no response from slave " + String(SLAVE_ID));
      return;                        // the rest of the firmware keeps running
    }

    // Regs 0-1: unsigned totalizer / Regs 2-3: signed value
    uint32_t totalizer   = regsToU32(regs[0], regs[1]);
    int32_t  measurement = regsToS32(regs[2], regs[3]);

    String json = "{\"slave\":" + String(SLAVE_ID) +
                  ",\"totalizer\":" + String(totalizer) +
                  ",\"measurement\":" + String(measurement) + "}";
    Serial.println(json);
    // In production: publishMessage(TOPIC_DATA, json) -> MQTT + SD backup
  }

  delay(20);
}
Descarga el pack completo del proyecto — gratisEste ejemplo + los relacionados + lista de materiales