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.

EDIT DU 19/08/20 Mise a jour du script pour de l’ethernet :

/*
   Carte I/O modbus TCP/IP + fonction température DS18B20.
   Hardware: Nano + ethernet shield ENC28J60
             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.
   Source : André Sarmento Barbosa http://github.com/andresarmento/modbus-arduino
*/

/*--------------------------BIBLIOTHEQUES-----------------------------*/
#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, 51};

/**------------------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 1
/*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----------------------*/
//ModbusIP object
ModbusIP mb;

/*TEMPO EN MILLISECONDES*/
long ts;

/*-------------------------------------------------------------------*/
void setup() {
  //Config Modbus IP
  mb.config(mac, ip);
  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], HIGH); //addCoil() pour digital outputs initialise a Off les relais
    }
  }
  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
}

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 d’intensité 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.
   Hardware: Nano + ethernet shield ENC28J60
             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.
   Source : André Sarmento Barbosa http://github.com/andresarmento/modbus-arduino
*/

/*--------------------------BIBLIOTHEQUES-----------------------------*/
#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, 51};

/**------------------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 1
/*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----------------------*/
//ModbusIP object
ModbusIP mb;

/*TEMPO EN MILLISECONDES*/
long ts;

/*-------------------------------------------------------------------*/
void setup() {
  //Config Modbus IP
  mb.config(mac, ip);
  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], HIGH); //addCoil() pour digital outputs initialise a Off les relais
    }
  }
  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+

J’ai voulu tester ta solution, mais je suis bloqué !
j’ai utilisé ton croquis (le dernier avec sonde DS18b20) sur Nano et shield ENC28J60
la température remonte bien dans le moniteur série, donc de ce coté ça ma l’air OK.

coté Jeedom, je suis toujours en V3.3.50
j’ai installé le pluging (stable)…tout est au vert sur ma page santé.

Pour configurer mon équipement:
Adresse ok
port ? 80 je suppose !
Unit ID 1 ? je ne vois pas à quoi ça pourrait correspondre d’autre !

C’est dans la commande info créé que ça ne correspond pas à ton affichage !
Pour Type entrée/sortie
Je n’ai pas « register int » comme choix ? du coup je suis bloqué et pas de remonté de « value »

et autre chose, pour avoir un affichage sur le dashboard ? il faut obligatoirement passer par un virtuel ?
en tous cas, merci pour ce post

Pour le port j’aurais testé 502…

Merci de ton retour et de l’investissement sur ce plugin.
502…j’ai testé, pas mieux et on voit bien dans le sketch de @Fabio133 que 500 est le premier registre.

par contre Est-ce que ce type d’entrée « register int » est présent dans la version stable ?

tu ne serais pas en train de confondre port et registre ?
le port c’est 502 sauf si tu le modifie dans la lib .
Le registre de mémoire il utilise 500 en holding register

C’est bien possible , je ne vois pas de port dans le sketch de Fabio133.
Mais ce qui est sur c’est qu’avec la version stable je n’ai pas d’entrée « register int »
et que la version béta ne passe pas sur mon raspberry pi3+ (version jeedom 3.3.50) les dépendance son NOK.
donc j’y reviendrais plus tard.

Si tu as un accès extérieur à ton Jeedom je veux bien jeter un oeuil.
Si c’est le cas contact moi en MP

Je te remercie, c’est sympa, mais je sais pas faire.
Et suite a plusieurs tentatives en SSH ! Je me retrouve bloqué (en SSH) comme si j’étais dans python (jeedom tourne quand même) mais j’arrive pas à quitter ce truc? J’ai juste 3 signes comme ça
« >>> »
A part 1+1 qui le donne bien 2 en réponse… j’arrive pas à grand chose d’autre…je vais ouvrir un autre sujet sur mon nouveau problème.

Salut,

Z’êtes pas cool les gars !
Jusque là sans wifi tout ça, j’me disais que c’est pas la peine de lancer le projet, ça va pas marcher, tu as pas d’ethernet dans le sous-sol, piloter le portail et la porte de garage ça sert à rien… Mais là ça commence à être exploitable à mon petit niveau. Un chantier de plus dans la maison
:grin:

J’ai repris mon test sur la version stable du plugin Mymodbus (puisque les dépendances passent)

  • un nano (sketch de Fabio133)

  • un shield Ethernet ENC28J60

  • Un ULN 2003 câblé sur 7 leds (de D2 à D8)
    L’équipement est bien sur le port 502 !
    Capturetest01
    détail sur les commandes actions.

et le principal…ça fonctionne !

Bon ba super, si ça marche en stable.
Tu pouras m’envoyer les logs d’install en bêta à l’occasion que je regarde ce qu’il se passe.

mymodbus_update.txt (2,8 Ko)

Je viens de faire une nouvelle tentative ! toujours en NOK
je vais tenter de redémarrer, mais pas trop d’espoir.