Bonjour à tous,
J’ouvre ce sujet pour ceux qui aurait comme moi des problèmes pour récupéré des valeurs dans des équipements qui communique en MODBUS RTU sous la norme IEE754, en effet il n’est pas possible de convertir les valeurs de manière simple.
J’ai donc trouvé une solution sur la base d’un Arduino Mega, j’ai passé beaucoup de temps à écrire le code et à faire les essais mais celui-ci est désormais fonctionnel.
Vous aurez besoin d’une carte arduino mega, et de deux convertisseurs de signaux MAX485.
Ce code configure une passerelle Modbus RTU entre un compteur d’énergie SDM230 (esclave), un IPX800 V5 (maître) et une carte Arduino (qui agit à la fois en tant que maître et esclave). L’Arduino permet de lire les valeurs du SDM230, puis les transmet à l’IPX800 via Modbus RTU, en utilisant des modules MAX485 pour la communication RS485.
Fonctionnement :
- Arduino en tant que maître : L’Arduino se connecte au SDM230 (esclave Modbus) et lit plusieurs registres Modbus (comme la tension, le courant, la puissance, etc.). Ces valeurs sont lues toutes les 200 ms.
- Conversion et stockage des données : Les données lues du SDM230 sont converties en valeurs entières adaptées (uint16_t) à l’esclave (IPX800). Par exemple, la tension est multipliée par 100 pour améliorer la précision.
- Arduino en tant qu’esclave : L’Arduino agit également comme un esclave Modbus, permettant à l’IPX800 V5 (maître) de lire les registres contenant les données lues depuis le SDM230. L’IPX800 peut ainsi accéder aux informations via Modbus RTU.
- Communication via MAX485 : Les modules MAX485 sont utilisés pour gérer la communication série sur RS485, où le maître Arduino envoie des requêtes au SDM230 et l’esclave Arduino expose les valeurs lues au réseau Modbus.
Détails techniques :
- Arduino en tant que maître (SDM230) : L’Arduino utilise la bibliothèque
ModbusMaster
pour communiquer avec le compteur d’énergie. - Arduino en tant qu’esclave (IPX800) : L’Arduino utilise la bibliothèque
ModbusRTUSlave
pour exposer les valeurs lues au réseau Modbus, ce qui permet à l’IPX800 de les lire. - Contrôle des pins MAX485 : Le MAX485 gère la communication en utilisant les pins
DE
(Driver Enable) etRE
(Receiver Enable) pour activer ou désactiver la transmission.
#include <ModbusMaster.h>
#include <ModbusRTUSlave.h>
ModbusMaster node;
const int MAX485_CONTROL_MASTER = 2;
void preTransmission() { digitalWrite(MAX485_CONTROL_MASTER, HIGH); }
void postTransmission() { digitalWrite(MAX485_CONTROL_MASTER, LOW); }
#define MODBUS_SERIAL Serial2
#define DE_PIN 13
#define RE_PIN 12
#define MODBUS_BAUD 9600
#define MODBUS_CONFIG SERIAL_8N1
#define MODBUS_UNIT_ID 20
uint16_t holdingRegisters[20];
ModbusRTUSlave modbusSlave(MODBUS_SERIAL);
const uint16_t SDM230_REGISTERS[] = {0x0000, 0x0006, 0x000C, 0x0046, 0x0156, 0x0056};
const int NUM_REGISTERS = sizeof(SDM230_REGISTERS) / sizeof(SDM230_REGISTERS[0]);
int currentRegisterIndex = 0;
unsigned long previousMillisRead = 0;
const unsigned long intervalRead = 200;
uint16_t convertToScaledUint16(float value, float scaleFactor) {
return (uint16_t)(value * scaleFactor);
}
float convertToFloat(uint16_t high, uint16_t low) {
uint32_t combined = ((uint32_t)high << 16) | low;
float result;
memcpy(&result, &combined, sizeof(result));
return result;
}
void setup() {
Serial.begin(9600);
Serial1.begin(9600, SERIAL_8N1);
MODBUS_SERIAL.begin(MODBUS_BAUD, MODBUS_CONFIG);
pinMode(MAX485_CONTROL_MASTER, OUTPUT);
digitalWrite(MAX485_CONTROL_MASTER, LOW);
pinMode(DE_PIN, OUTPUT);
pinMode(RE_PIN, OUTPUT);
digitalWrite(DE_PIN, LOW);
digitalWrite(RE_PIN, LOW);
node.begin(1, Serial1);
node.preTransmission(preTransmission);
node.postTransmission(postTransmission);
modbusSlave.configureHoldingRegisters(holdingRegisters, 20);
modbusSlave.begin(MODBUS_UNIT_ID, MODBUS_BAUD, MODBUS_CONFIG);
Serial.println("Passerelle Modbus RTU initialisée.");
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillisRead >= intervalRead) {
previousMillisRead = currentMillis;
uint8_t result = node.readInputRegisters(SDM230_REGISTERS[currentRegisterIndex], 2);
if (result == node.ku8MBSuccess) {
uint16_t high = node.getResponseBuffer(0);
uint16_t low = node.getResponseBuffer(1);
float value = convertToFloat(high, low);
switch (currentRegisterIndex) {
case 0: holdingRegisters[currentRegisterIndex] = convertToScaledUint16(value, 100); break;
case 1: holdingRegisters[currentRegisterIndex] = convertToScaledUint16(value, 1000); break;
case 2: holdingRegisters[currentRegisterIndex] = convertToScaledUint16(value, 1); break;
case 3: holdingRegisters[currentRegisterIndex] = convertToScaledUint16(value, 100); break;
case 4: holdingRegisters[currentRegisterIndex] = convertToScaledUint16(value, 1); break;
case 5: holdingRegisters[currentRegisterIndex] = convertToScaledUint16(value, 100); break;
}
Serial.print("Registre ");
Serial.print(SDM230_REGISTERS[currentRegisterIndex], HEX);
Serial.print(": ");
Serial.println(value);
}
currentRegisterIndex = (currentRegisterIndex + 1) % NUM_REGISTERS;
}
digitalWrite(DE_PIN, LOW);
digitalWrite(RE_PIN, LOW);
modbusSlave.poll();
}
J’espère que cette " passerelle " aidera beaucoup d’entre nous en attendant une mise à jour de l’IPX800 v5 sur la partie Modbus. Bonne lecture à tous.