modbus_rtu_maitre.ino

Arduino Opta (micro PLC, STM32H747) · Archivo : modbus_rtu_maitre.ino · Lenguaje : Arduino · Uso : Interroger un instrument en Modbus RTU sur RS485

El puerto RS485 de Opta consulta contadores de energía, sondas o variadores en Modbus RTU. La trama maestra FC3 y el CRC16 están construidos a mano: sin dependencias salvo ArduinoRS485, que gestiona la línea de dirección DE/RE del transceptor.

Hardware

Una Opta PLC de STMicroelectronics (STM32H747 en tiempo real + coprocesador seguro i.MX RT1052) programada con el entorno Arduino PLC, alimentada a 24 V DIN. Un esclavo Modbus RTU (sonda, contador) con terminación de 120 Ω en los extremos de la línea.

Cableado

A+ al A+ del esclavo, B- al B-, masa de referencia común en líneas largas. Opta gestiona la dirección (pin ENABLE) automáticamente con RS485Class.

Funcionamiento

El maestro envía dirección+FC3+dirección de registro+cantidad+CRC16; la respuesta se verifica por CRC antes de decodificar los registros de 16 bits. Timeout simple con available().

Ejecución

Instala el gestor de placas Arduino PLC, selecciona «Opta PLC», conecta USB/DFU y vuelca el programa. El sketch corre por el STM32H747 en tiempo real.

Variantes

Escribir consignas con FC6/FC16, consultar varios esclavos por turnos, o reenviar los valores por Modbus/TCP (pasarela RTU↔TCP).

modbus_rtu_maitre.ino

// Opta PLC : maitre Modbus RTU sur le port RS485 intégré (ArduinoRS485).
// Lecture de 2 registres holding (FC3) chez l'esclave d'adresse 1.
#include <ArduinoRS485.h>

RS485Class rs485(Serial1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PIN_SERIAL1_ENABLE);

uint16_t crc16(const uint8_t *d, int n) {
  uint16_t crc = 0xFFFF;
  for (int i = 0; i < n; i++) {
    crc ^= d[i];
    for (int b = 0; b < 8; b++)
      crc = (crc & 1) ? (crc >> 1) ^ 0xA001 : crc >> 1;
  }
  return crc;
}

bool lire(uint8_t esclave, uint16_t adr, uint8_t qte, uint8_t *val) {
  uint8_t trame[8] = { esclave, 0x03, adr >> 8, adr & 0xFF, 0, qte };
  uint16_t c = crc16(trame, 6);
  trame[6] = c & 0xFF; trame[7] = c >> 8;
  rs485.begin(9600, SERIAL_8N1);
  rs485.beginTransmission(); rs485.write(trame, 8); rs485.endTransmission();
  int attendu = 5 + qte * 2;
  int n = rs485.available() ? rs485.readBytes((char*)val, attendu) : 0;
  return n == attendu && crc16(val, n - 2) == (val[n-1] << 8 | val[n-2]);
}

void setup() {
  Serial.begin(115200);
  Serial.println("Maitre Modbus RTU 9600 8N1 — esclave 1");
}

void loop() {
  uint8_t rep[32];
  if (lire(1, 0x0000, 2, rep)) {
    uint16_t r0 = rep[3] << 8 | rep[4];
    uint16_t r1 = rep[5] << 8 | rep[6];
    Serial.printf("ESCLAVE1 reg0=%u reg1=%u\n", r0, r1);
  } else {
    Serial.println("pas de reponse (verif. cablage A/B, adresse, 9600 8N1)");
  }
  delay(1000);
}

← Volver a la ficha