Arduinodbus - Coffret I/O sous Modbus

Bonjour,

Depuis peu je m’intéresse au protocole Modbus, particulièrement en TCP/IP. Je me suis créer des coffrets I/O sur une base d’Arduino nano + Shield Ethernet ENC28J60 associé à une cartes à 8 relais 230V.

Jusqu’à présent ces coffrets sont intégrés à mon Jeedom via le plugin JEEDUINO. Ça fonctionnait parfaitement mais le nano avec le sketch Jeeduino était à saturation. Sa réactivité de 3 secondes avec parfois des actions ou des retours qui ne fonctionnait pas mon pour un temps retardé quant a l’implantation physique de ces coffrets.

Il y a peu j’ai croisé la route de @bebel27 et de son plugin Mymodbus (beta), j’avais 2-3 automate en Modbus alors en collaboration on les a intégrés à mon Jeedom. (Adam6050 pour ceux qui connaisse).

Et la une petite idée à germée pourquoi pas mes coffrets en Modbus, Google m’a aidé ainsi que @Mmx.

Ça c’était pour l’histoire. Et fini par un happy end…

A la manière d’une recette voici les ingrédients :

  • Un Arduino pour moi le nano pour l’encombrement,

  • Un Shield Ethernet ENC28J60, pour nano aussi.

  • Une carte relais de 2 à 16 relais (Ps sur 16 relais seulement 14 sont utilisables par le nano)

  • Des fils DuPont

  • Alim 5Vdc 2A

  • Un peu de créativité.

En pièce détaché :
IMG_0104
Et assemblé :
IMG_0110

L’alimentation 5V se fera par le câble réseau au moyen d’un injecteur POE passif (coté alim) et d’un séparateur (côté coffret)

Le coffret physiquement est en ABS et mesure 200x120x80. Pratique mais pas top peut être que j’en imprimerais un plus adapté. Ces coffrets pour moi seront à destination des combles.

Le matériel présenté, entrons dans le vif du sujet.

Il existe des bibliothèque Modbus pour Arduino, plein. Après en avoir testé quelques-unes je me suis arrêté sur Modbus-arduino car elle est compatible avec beaucoup de shield réseau.

W5100, ENC28J60, ESP8266, etc.

Les résultats sont excellents, je peux au travers du plugin Mymodbus piloter mes 8 relais, des essais concluants on était réalisé avec des entrées analogiques avec la participation de @Mmx.
Capture
J’ai donc réalisé un sketch qui va encore évoluer, mais qui pour l’instant permet de commuter simplement les sorties numériques, interroger les entrées analogiques.

/*
 * Essai carte I/O modbus TCP/IP.
 * Hardware: Nano + ethernet shield ENC28J60
 *           Carte 8 relais 5vdc / 230v 10A
 * Fonction: Pilotage de 14 relais par modbus
 * Alimentation auxiliaire requise, si tous les relais sont a "ON" alors le nano ne répond plus.
 * IP fixe.
 * Source : https://github.com/samiralavi/modbus-arduino
*/

#include <ModbusIP_ENC28J60.h> // Décommenter TCP_KEEP_ALIVE dans la biblio.

//<<<<<< RENSEIGNER VOTRE ADRESSE IP ET MAC CI DESSOUS >>>>>
//Adresse MAC de votre shield.
    byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xE0 };
//Adresse IP de votre shield.
    byte ip[] = { 192, 168, 1, 40 };

//<<<<<< RENSEIGNER VOS I/O CI DESSOUS >>>>>
//Pins 10, 11, 12, 13 non dispo pour ENC28J60.
//Modbus Registers Offsets (0-9999)
//Adresse Digital Output
const int Relais1 = 100;  // adresse dans le registre
const int Relais2 = 101;
const int Relais3 = 102;
const int Relais4 = 103;
const int Relais5 = 104;
const int Relais6 = 105;
const int Relais7 = 106;
const int Relais8 = 107;
//const int Relais9 = 108;
//const int Relais10 = 109;
//const int Relais11 = 110;
//const int Relais12 = 111;
//const int Relais13 = 112;

//Adresse Analog Input
const int AnalogIn01 = 100; // ce n'est pas la suite des DO, donc tu peux mettre du début
const int AnalogIn02 = 101;
const int AnalogIn03 = 102;
const int AnalogIn04 = 103;
const int AnalogIn05 = 104;


//Used Pins
//avoid pins 10, 11, 12, 13 when using ENC28J60
//Pin Digital Output
const int DO2 = 2; //Relais KA*
const int DO3 = 3;
const int DO4 = 4;
const int DO5 = 5;
const int DO6 = 6;
const int DO7 = 7;
const int DO8 = 8;
const int DO9 = 9;
//const int DO14 = 14;
//const int DO15 = 15;
//const int DO16 = 16;
//const int DO17 = 17;
//const int DO18 = 18;

//Pin Analog Input
const int AI01 = A0;
const int AI02 = A1;
const int AI03 = A2;
const int AI04 = A3;
const int AI05 = A4;


//ModbusIP object
ModbusIP mb;
long ts;

void setup() {

  //Config Modbus IP
  mb.config(mac, ip);
  Serial.begin(9600);

  //Configuration des pins Arduino
  pinMode(DO2, OUTPUT);
  pinMode(DO3, OUTPUT);
  pinMode(DO4, OUTPUT);
  pinMode(DO5, OUTPUT);
  pinMode(DO6, OUTPUT);
  pinMode(DO7, OUTPUT);
  pinMode(DO8, OUTPUT);
  pinMode(DO9, OUTPUT);
  //pinMode(DO14, OUTPUT);
  //pinMode(DO15, OUTPUT);
  //pinMode(DO16, OUTPUT);
  //pinMode(DO17, OUTPUT);
  //pinMode(DO18, OUTPUT);

  pinMode(AI01, INPUT);
  pinMode(AI02, INPUT);
  pinMode(AI03, INPUT);
  pinMode(AI04, INPUT);
  pinMode(AI05, INPUT);

  // Associe l'adresse d'un relais à un coil.Add to register - Use addCoil() for digital outputs
  //Arret des relais au démarage (cas coupure courant)
  mb.addCoil(Relais1, HIGH);
  mb.addCoil(Relais2, HIGH);
  mb.addCoil(Relais3, HIGH);
  mb.addCoil(Relais4, HIGH);
  mb.addCoil(Relais5, HIGH);
  mb.addCoil(Relais6, HIGH);
  mb.addCoil(Relais7, HIGH);
  mb.addCoil(Relais8, HIGH);
  //mb.addCoil(Relais9, HIGH);
  //mb.addCoil(Relais10, HIGH);
  //mb.addCoil(Relais11, HIGH);
  //mb.addCoil(Relais12, HIGH);
  //mb.addCoil(Relais13, HIGH);
  //mb.addCoil(Relais14,HIGH);

  //Use addHreg() for analog outputs
  //mb.addHreg()
  //Use addIsts () for digital inputs
  //mb.addIsts ();
  //Use addIreg () for analog inputs
    mb.addIreg(AnalogIn01, true);
    mb.addIreg(AnalogIn02, true);
    mb.addIreg(AnalogIn03, true);
    mb.addIreg(AnalogIn04, true);
    mb.addIreg(AnalogIn05, true);
  ts = millis();
}

void loop() {
  //Call once inside loop() - all magic here
  mb.task();

  //Attach ledPin to Relais1 register
  digitalWrite(DO2, mb.Coil(Relais1));
  digitalWrite(DO3, mb.Coil(Relais2));
  digitalWrite(DO4, mb.Coil(Relais3));
  digitalWrite(DO5, mb.Coil(Relais4));
  digitalWrite(DO6, mb.Coil(Relais5));
  digitalWrite(DO7, mb.Coil(Relais6));
  digitalWrite(DO9, mb.Coil(Relais8));
  digitalWrite(DO8, mb.Coil(Relais7));
  //digitalWrite(DO14, mb.Coil(Relais9));
  //digitalWrite(DO15, mb.Coil(Relais10));
  //digitalWrite(DO16, mb.Coil(Relais11));
  //digitalWrite(DO17, mb.Coil(Relais12));
  //digitalWrite(DO18, mb.Coil(Relais13));

  //lit chaque 3 secondes pour éviter de faire exploser le Nano :)
  if (millis() > ts + 3000) {
    ts = millis();
    //Setting raw value (0-1024)
    mb.Ireg(AnalogIn01, analogRead(AI01));
    mb.Ireg(AnalogIn02, analogRead(AI02));
    mb.Ireg(AnalogIn03, analogRead(AI03));
    mb.Ireg(AnalogIn04, analogRead(AI04));
    mb.Ireg(AnalogIn05, analogRead(AI05));
  }
} 

L’objectif est de vous fournir un sketch étoffé, dans un package avec les bibliothèques qui vont bien.
J’ajouterais les analogiques outputs et digital inputs prochainement. Et avec l’ESP8266 pourquoi pas un module Wifi pour modbus…

Je rappelle aussi une chose importante ces cartes relais (chinoiseries) faut l’avoué sont donné pour 10A, attention cependant ne pas trop les surchargés. De la même manière faites très attention lors de câblage sur des tensions de 230V il y a des risques d’électrisation soyez prudents ou ne faites rien si vous ne le sentez pas.

Et pour finir si ces cartes ne vous dise rien sachez que l’Arduino tourne sous 5V et qu’il pourrait piloter des relais plus « Safe » sur rail Din. Ainsi on peut coder seulement l’Arduino en esclave Modbus et l’assembler à ce que l’on veut derrière.

La tension de commutation des bobines des relais devra être en 5V.

Il existe aussi des relais plus compacts, des relais statiques, qui n’ont pas de mécanique (bobines) et qui ont une coupure de tension inférieure au relais traditionnel.

Voilà c’était un peu long mais j’essaye d’être complet.

Je me permets d’inviter @Rigolman69 au sujet vu sont post récent, et de remercier les deux collaborateurs @Bebel27 et @Mmx pour leur participation respective.

A+
Fab.

4 J'aimes

@Fabio133 bonjour et merci du partage.
questions :
Pourquoi implémenter dans cette utilisation le modbus ?
L’idée de l’ESP 8266 ou 32 te simplifierait bien les choses …

Salut,
Alors le pourquoi du comment il n’est pas pertinent pour chacun, mais dans mon cas j’ai assembler mes coffrets il y a de ça 4ans j’avais donc le matériel de prêt et je voulais absolument conserver le câblage filaire rj45.

L’esp fonctionne très bien le code n’a pas l’air complexe effectivement. Mais c’est du wifi et si je peux me passer de wifi surtout sur des coffrets destiner à être dans mes combles c’est mieux.
Et puis c’est un protocole de dialogue industriel éprouvé. Il y a aussi de la demande sur le sujet.

Ok je peut comprendre

juste j’ai vue que tu était un peut court sur le nombre de sorties …
il y a des solutions bon marché …

Excellent je connaissais pas mais rien ne m’étonne avec l’arduino.
A implémenter ça va être un peu long pour moi, si il y a un besoin pourquoi pas.
Je me le garde sous le code, chez moi l’électricité et faites par coffret de répartition donc je vais leur coller les coffrets i-o, 14 par boîtier c’est déjà pas mal.

Édit du coup j’ai cherché et j’ai trouvé ceci aussi mcp23009

1 J'aime

Si tu a besoin d’augmenter les entrées il y a aussi des multiplexeurs

Un parmi d’autres

2 J'aimes

Bonsoir,

Merci @Fabio133 pour ce partage.

Arduino et ESP, je n’y connais rien.

En ce qui me concerne j’ai fait le choix d’utiliser des cartes iO en Modbus RTU.
Je trouve que c’est la meilleure solution :
Rapidité d’exécution des commandes
Simplicité du protocole
Deux fils suffisent en RS_485
Immunité par rapport aux net

Bon, ça c’est pour la théorie.

J’ai donc acheté une carte chinoise 8 inputs et 8 outputs relais à 14€ et deux convertisseurs RS 485 - USB. Le problème c’est que rien ne marche ! Mais j’ai pas lâché ! Ça fait presque deux mois que je suis en relation avec le vendeur et le fabricant.
Concernant les convertisseurs j’ai fini par leur faire admettre que la conception même de leur électronique ne pouvait pas fonctionner !
Concernant la carte je leur ai fait un rapport complet. Documentation fausse et incomplète ! Bug sur la réponse de la carte !
J’attends leur retour.
Bref, certains chinois feraient mieux de vendre des carottes !
Dommage leur hardware est propre et pas cher !

Fin de l’histoire dans qq jours, normalement.

Salut, mince t’as pas de bol, la chinoiserie sans être péjoratif c’est parfois une loterie… moi à part les Arduino et les relais je commande que du passif. Plus tranquille.
Pour info il est tous à fait possible de faire du modbus rtu, avec de l’arduino, normalement c’est pas trop compliqué. Je peut regarder pour adapter le sketch.

Bonjour,
J’ai simplifié le code et ajouté l’option onewire / dallas afin de faire du relevé de température depuis le nano. Car depuis que le plugin est out bah mes sondes le sont aussi.
On peut donc relever plusieurs sondes, normalement la limite c’est 100 sondes sur le même slot. A condition de l’alimenter séparément du nano.

J’ai aussi intégrer le Wifi et vais faire des tests prochainement, le temps de faire le montage.

/*
   Carte I/O modbus TCP/IP + fonction température DS18B20. (Wifi possible)
   Hardware: Nano + ethernet shield ENC28J60 ou Wemos D1 Mini
             Carte 8 relais 5vdc / 230v 10A
   Fonction: Pilotage des entrées sorties d'un arduino par modbus. Relève de température DS18B20.
   Commentaires : Alimentation 5Vdc auxiliaire requise, pour eviter une surconsomation sur l'arduino, sinon ça ne répond plus.
   IP / Mac fixe. Wifi comptatible
   Source : André Sarmento Barbosa http://github.com/andresarmento/modbus-arduino
*/
/*--------------------------BIBLIOTHEQUES-----------------------------*/
//#include <ModbusIP_ESP8266AT.h> //Mode WIFI ESP8266
//#include <SoftwareSerial.h>     //Mode WIFI ESP8266
#include <ModbusIP_ENC28J60.h>    //Mode TCP/IP
#include <DallasTemperature.h>
#include <OneWire.h>

/*-----------RENSEIGNER ADRESSE IP ET MAC (MODE TCP/IP)---------------*/
//Adresse MAC de votre shield.
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xE8 };
//Adresse IP de votre shield.
byte ip[] = {192, 168, 1, 50};
/*------------RENSEIGNER SSID & PASSWORD (MODE WIFI)------------------*/
//byte Ssid[] = ########  //Nom de votre box.
//byte Password[] = ######## // Clé wifi.

/**------------------CONFIGURATION DES I/O et PIN --------------------*/
//Pins 10, 11, 12, 13 non dispo pour ENC28J60.
//Digitales
#define NBRE_DO 8
byte DigitalOut [NBRE_DO];                           //Tableau Digital Output
const byte First_pinDO = 2;                          //Numéro 1er DO
#define NBRE_DI 0
byte DigitalIn [NBRE_DI];                            //Tableau Digital Input
const byte First_pinDI = (First_pinDO + NBRE_DO);    //Numéro 1er DI sauf si NBRE_DO=8

//Analogiques
#define NBRE_ANO 0
const byte First_pinANO = 15;                        //Numéro 1er ANO "15" alias "A1"
byte AnalogOut [NBRE_ANO];                           //Tableau Analog Output
#define NBRE_ANI 0
const byte First_pinANI = (First_pinANO + NBRE_ANO); //Numéro 1er ANI
byte AnalogIn [NBRE_ANI];                            //Tableau Analog Input

//Configuration des registres modbus (adr. possible 0-9999) par catégories.
const byte First_RegDO  = 100;                       //Numéro 1er DO
byte REG_DOut [NBRE_DO];                             //Tableau registre Digital Output
const byte First_RegDI  = 200;                       //Numéro 1er DI
byte REG_DIn [NBRE_DI];                              //Tableau registre Digital Input
const byte First_RegANO = 300;                       //Numéro 1er ANO
byte REG_ANO [NBRE_ANO];                             //Tableau registre Analog Output
const byte First_RegANI = 400;                       //Numéro 1er ANI
byte REG_ANI [NBRE_ANI];                             //Tableau registre Analog Input

/*-----------------Configuration onewire-dallas----------------------*/
OneWire oneWire(14); // pin 14 (A0)
DallasTemperature sensors(&oneWire);

/*Configuration sonde multiple*/
#define NBRE_SONDE 3
/*Configuration valeur de départ*/
int REG_SONDE [NBRE_SONDE];              //Tableau registre sonde
const int First_REGSONDE = 500;          //Numéro 1er registre
int SONDE [NBRE_SONDE];                  //Tableau des sondes
const int First_SONDE = 0;               //Numéro 1ere sonde

/*VARAIBLES*/
float TEMPFLOAT;                         //Variables temp flotant negatif possible
int TEMPCALC;                            //Variables temp entier

/*-----------------Configuration des fonctions----------------------*/
//Wifi-ESP8266
//SoftwareSerial wifiSerial(2 , 3);
//ESP8266 wifi(wifiSerial, 9600);
//ModbusIP object
ModbusIP mb;

/*TEMPO EN MILLISECONDES*/
long ts;

/*-------------------------------------------------------------------*/
void setup() {
  //Config Modbus IP
    mb.config(mac, ip);
  //Config Modbus WIFI
  // mb.config(wifi, Ssid,Password");
  Serial.begin(9600);
  sensors.begin();

  if (NBRE_DO > 0) {
    for (int i, j = 0; i, j < NBRE_DO; i++, j++) {
      byte pin = First_pinDO + i;
      DigitalOut[i] = pin;
      pinMode (DigitalOut[i], OUTPUT); //Toutes les pins sont en OUT
      byte reg = First_RegDO + j;
      REG_DOut [j] = reg;
      mb.addCoil(REG_DOut [j], true); //Use addCoil() for digital outputs
    }
  }
  if (NBRE_SONDE > 0){
    for (int i = 0; i < NBRE_SONDE; i++){
    int Sx = First_REGSONDE + i;
    REG_SONDE[i] = Sx;
    sensors.begin();
    mb.addHreg(REG_SONDE[i], true);
    }
  }
  ts = millis();
}
void loop() {
  //Call once inside loop() - all magic here
  mb.task();

/*Gestion des sorties*/
  if (NBRE_DO > 0) {
    for (int i = 0; i < NBRE_DO; i++) {
      byte pin = First_pinDO + i;
      DigitalOut[i] = pin;
      int reg = First_RegDO + i;
      REG_DOut [i] = reg;
      digitalWrite (DigitalOut [i], mb.Coil(REG_DOut[i])); //Association registres/pins
   } //EndFor
  } //Endif

/*Relève des températures*/
if (millis() > ts + 3000) {
    ts = millis();
if (NBRE_SONDE > 0){
  for (int i = 0; i < NBRE_SONDE; i++){
    int Sx = First_SONDE + i;
    SONDE[i] = Sx;
    int Reg = First_REGSONDE + i;
    REG_SONDE[i] = Reg;
    sensors.requestTemperatures();
    TEMPFLOAT = sensors.getTempCByIndex(SONDE[i]);
    TEMPCALC = (int((TEMPFLOAT+100)*100));
    Serial.print("Relève sonde ");
    Serial.print(SONDE[i]);
    Serial.print(" : ");
    Serial.println(TEMPFLOAT);
    Serial.print("Valeur converti :");
    Serial.print(TEMPCALC);
    Serial.print(" envoyé sur le registre :");
    Serial.println(REG_SONDE[i]);
    mb.Hreg(REG_SONDE[i],(TEMPCALC));// Registre
   } //EndFor
  } //Endif NBRE
 } //Endif millis
}

Voilà résultat sous Jeedom :
image
Et dans le plugin mymodbus la configuration :
image

Par contre faut que je me penche sur la conso du raspi, car si je fait dialoguer en permanence le charge du PI est conséquente. Je relève toute les secondes…pour le température mais aussi pour les I/O inutile. Peu être créer deux équipements avec la même IP, pour scinder les températures des I/O.

Voilà le code prend 45% du nano donc on a encore de la place pour d’autres fonction.
Je dois encore ajouté un pullup et d’autres chose.

A+