modbus_rtu_maitre.ino

Arduino Opta (micro automate, STM32H747) · Fichier : modbus_rtu_maitre.ino · Langage : Arduino · Usage : Interroger un instrument en Modbus RTU sur RS485

Le port RS485 de l'Opta interroge compteurs d'énergie, sondes ou variateurs en Modbus RTU. La trame maître FC3 et le CRC16 sont construits à la main : aucune dépendance hors ArduinoRS485, qui gère la ligne de direction DE/RE du transceiver.

Matériel requis

Une Opta PLC STMicroelectronics (STM32H747 temps réel + coprocesseur sécurisé i.MX RT1052) programmée avec l'environnement Arduino PLC, alimentée en 24 V DIN. Un esclave Modbus RTU (sonde, compteur) avec terminaison 120 Ω sur les extrémités de la ligne.

Câblage

A+ vers A+ de l'esclave, B- vers B-, masse de référence commune si ligne longue. L'Opta gère la direction (broche ENABLE) automatiquement via RS485Class.

Fonctionnement

Le maître envoie adresse+FC3+adresse registre+quantité+CRC16 ; la réponse est vérifiée par CRC avant décodage des registres 16 bits. Timeout simple par available().

Exécution

Installez le gestionnaire de cartes Arduino PLC, sélectionnez « Opta PLC », câblez en USB/DFU puis téléversez. Le programme tourne sur le STM32H747 côté temps réel.

Variantes

Écrire des consignes avec FC6/FC16, interroger plusieurs esclaves en tournus, ou remonter les valeurs en Modbus/TCP (passerelle 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);
}

← Retour à la fiche