LOCODUINO

Forum de discussion
Dépôt GIT Locoduino
Flux RSS

samedi 22 juillet 2017

21 visiteurs en ce moment

La génèse d’un réseau 100% Arduino

De l’architecture matérielle à l’informatique ferroviaire en objet C++

. Par : Dominique, Pierre59

Lorsque j’ai réalisé mon premier réseau, il y a quelques années, devant le choix difficile et le prix d’une solution DCC   du commerce pour un petit réseau, et pour éviter d’être enfermé dans une solution propriétaire limitant les évolutions, j’ai finalement opté pour une centrale à base d’Arduino que j’ai construite moi-même, à partir des exemples qu’on pouvait trouver sur le Web (Locoduino n’existait pas encore, il fallait l’inventer !).

Ayant trouvé le moyen de piloter quelques trains en même temps sur une voie unique, il m’a fallu très rapidement éviter les collisions et réguler les circulations, pour que mes petits enfants puissent profiter du réseau en toute quiétude (surtout pour moi !). Un cantonnement était nécessaire avec quelques capteurs. La programmation en C standard s’est vite avérée laborieuse même pour « synchroniser » ce petit nombre d’éléments.

Aujourd’hui je me lance dans la réalisation d’un réseau plus grand en double voie + une voie unique, avec 4 gares, un vingtaine d’aiguilles et de signaux, une quarantaine de zones, au moins 8 trains en circulation simultanée possibles, et je souhaite disposer de toutes les sécurités et automatismes nécessaires, des signaux qui fonctionnent sur le bord des voies et qui conditionnent les comportements des trains, des itinéraires, des priorités, un beau TCO, des manettes pour les enfants et surtout sans PC ni centrale du commerce (peut-être en option plus tard).

C’est un réseau de taille moyenne, mais l’approche de l’architecture matérielle et logicielle est faite avec soin. Cet article a pour but de décrire succinctement le cheminement de mes réflexions et mes choix, jusqu’à la description de l’informatique ferroviaire à base d’objets C++, possibles dans l’environnement Arduino, que Pierre m’a fait découvrir.

Cet article peut servir à « planter le décor » avant la lecture des articles de Pierre qui sont donnés dans le Post-Scriptum à la fin de cet article.

Pourquoi aurai-je autant confiance dans la réalisation d’un tel système ?

En étudiant de nombreuses réalisations possibles à travers les forums et après avoir choisi mon tracé en fonction des dimensions de mon local, j’ai commencé par la mise en place des ingrédients matériels : châssis porteur, supports des voies, pose des voies avec coupures, conception et pose les détecteurs d’occupation, à travers lesquels les coupons de voies sont alimentées (en DCC   ou en analogique).

Avec la pose des moteurs d’aiguille, j’ai développé un module de commande d’aiguilles, puis un module TCO recevant les signaux des détecteurs d’occupation, puis un module de commande de traction DCC  , tous les trois à base d’Arduino Mega 2560. Ces modules feront l’objet d’une description prochainement.

D’autres modules viendront s’ajouter plus tard (modules de signalisation, modules d’animations des décors, modules sonores, etc..).Tous les modules sont et seront équipés d’une carte de communication avec un bus CAN à 500kb/s.

Figure 1

Je voulais un bus de communication qui réunisse toutes les caractéristiques des bus de commande et des bus de rétro-signalisation (voir L’Arduino au coeur des systèmes de pilotage analogiques ou numériques.

Le bus CAN est le bus de communication que j’ai choisi pour les échanges de messages entre Arduino. Mais cet article s’applique à tous les types de bus tel l’I2C, natif dans chaque Arduino, ou d’autres types de bus pourvu qu’il existe une interface avec l’Arduino, voire aucun bus du tout si tout peut tenir dans un seul Arduino !

Grâce au bus, les fonctions de chaque module sont bien caractérisées et les interactions entre modules sont faciles à réaliser. Par exemple, pour commander les aiguilles à partir des clés du TCO, un changement d’état d’une clé est traduit en un message qui est transmis au module de commande d’aiguilles. Quand ce dernier a exécuté la commande il renvoie un message avec la position de l’aiguille. Ce message est récupéré par le TCO qui affiche la position en allumant une Led à coté de la clé.

Figure 2

Ce bus remplit toutes les fonctions de rétro-signalisation et de commandes d’accessoires. Ainsi, avec une interface "réseau" simple que procure le CAN, mon informatique ferroviaire peut grandir à mon rythme, sans souci de compatibilité, à commencer par l’entrée en jeu du gestionnaire du réseau.

Notons dès maintenant que l’architecture décentralisée en modules que j’ai choisie, et qui fonctionne par échange de messages, n’est pas la seule possible. De toute façon, les différentes entités qui composent votre réseau (les trains, les zones, les aiguilles, les signaux..) doivent s’échanger des messages pour que tout fonctionne harmonieusement. Nous verrons comment dans ce qui suit, le raisonnement étant indépendant de l’architecture.

Je disposais alors d’un réseau opérationnel mais en fonctionnement purement manuel !

Mon but étant de permettre à plusieurs trains de circuler en même temps, je voulais réguler la circulation avec un block système ou un BAL (block automatique lumineux) qui permette de prévenir un convoi suffisamment tôt de la présence d’un obstacle à sa marche (convoi précédent, aiguille mal positionnée, etc..) par un signal ouvert ou fermé. De plus je voulais assurer une régulation de vitesse réaliste en imposant les ralentissements (30 et 60) dans les zones à risque (zones d’aiguilles, entrées de gare, virages serrés).

J’avais donc mis en place, dans ce but, un découpage en zones permettant de réaliser un cantonnement et installé des détecteurs d’occupation. Pour profiter de l’effet "lumineux" au plus vite, j’ai relié les détecteurs d’occupation au TCO, puisque c’est lui qui pilote les Leds d’occupation sur le tableau. Ensuite il transmet un message vers le bus. Ces messages n’étaient pas encore utilisés mais ils existaient. J’envisageais également d’ajouter un module de commande de signaux pour le réalisme du réseau (aux endroits où je choisis d’en installer) et pour le contrôle des trains (signaux virtuels respectés par chaque convoi).

Cerise sur le gateau, je voulais animer le décor en fonction des circulations ou en fonction de certains contextes : allumer les phares dans les tunnels, générer des sons (crissements des roues dans les virages, annonces en gare, etc..), illuminer les maisons à la tombée de la nuit, etc…

Il restait donc à trouver comment faire fonctionner tous ces éléments avec un logiciel « chef d’orchestre » qui aurait une description précise du réseau et de ses éléments (ce que j’appelle « la modélisation du réseau »), qui serait capable de recevoir tous les événements (occupations, comportement des trains, actions utilisateurs, etc.. sous forme de messages) et qui pourrait générer toutes les commandes en conséquence (suivi des trains, commandes des trains, gestion des feux, des itinéraires, affichages, etc.. également sous forme de messages).

J’ai donc ajouté un module de "gestion de réseau » ou « gestionnaire » connecté aux autres modules via le bus.

Figure 3

Par exemple, si je reprend l’exemple de la commande d’aiguille ci-dessus, les messages du TCO ne sont pas directement exécutés par le contrôleur d’aiguilles, mais passent par le gestionnaire qui doit vérifier si cette commande est possible.

Figure 4

Imaginons qu’une aiguille soit protégée pour laisser passer un convoi prioritaire : cette aiguille ne pourra être manœuvrée qu’après le passage du convoi. C’est ce que fera le gestionnaire : si je bascule la clé de l’aiguille sur le TCO, rien ne se passe ! Dès que le convoi est passé, l’aiguille change automatiquement et la Led sur le TCO, à coté de la clé, répercute ce changement.

Bien entendu le gestionnaire pourra faire beaucoup plus : gérer les signaux, des itinéraires, réguler les trains, etc..

D’habitude, cette fonction est gérée par un logiciel sur ordinateur comme RRTC (Train Controler), JMRI, RocRail, etc.., avec la problématique de la rétro-signalisation qui doit être compatible. Ici, au contraire, je fais le pari que tout peut être géré par une carte Arduino puissante, comme la carte DUE, qui comporte en standard 2 interfaces CAN : l’une peut être affectée à la rétro-signalisation et les commandes d’appareils de voie, l’autre à l’animation du décor, si on veut.

Du coup, l’architecture matérielle complète de mon réseau se présentera comme ceci :

Figure 5

Il restait donc à répondre aux deux questions suivantes :

  • 1 Comment modéliser la structure du réseau et ses éléments pour en déduire une définition informatique qui permette de suivre l’état du réseau et des circulations en temps réel ?
  • 2 Comment programmer tout cela avec le seul IDE Arduino ?

Cette question est d’ailleurs en cours de discussion sur le Forum :
http://forum.locoduino.org/index.php?topic=72.0

Il y a certainement plusieurs scenarii possibles, mais voici ma vision personnelle, suivie de son implémentation.

Une autre raison m’a amené inexorablement à choisir cette architecture : je veux que les positions de mes trains (couple train + zone) soient automatiquement reconnues par le gestionnaire pour assurer le pilotage en DCC.

Or cette fonction de reconnaissance n’existe pas avec les centrales et les logiciels du commerce ! [1] Je me devais de relever ce challenge.

Vous verrez dans les prochains articles comment je m’y suis pris !

Une évidence s’impose :

La quantité d’éléments est telle qu’il m’a semblé difficile de conserver la programmation en C standard.

Il saute aux yeux qu’il y a des familles d’éléments dans le réseau qui comportent des similitudes :

  • plusieurs aiguilles,
  • plusieurs zones et cantons (un canton est une zone sans aiguille),
  • plusieurs signaux,
  • plusieurs trains,
  • plusieurs itinéraires,
  • plusieurs manettes ou cabines de conduite,
  • etc..

Chacune de ces familles est caractérisée par un ensemble de variables qui décrivent son état et par un ensemble de fonctions qui réalisent les actions liées aux événements qui les concernent. On peut dire que chaque élément est susceptible de recevoir un message ou un type de message (événement ou commande) et d’en envoyer à d’autres éléments, à un changement d’état ou en réponse à un message reçu.

De plus, dans chaque famille il y a des variantes : tous les éléments ont des variables et fonctions semblables, mais aussi quelques différences.

Par exemple, chaque zone est connectée à une « zone suivante » qui dépend de la position directe ou déviée d’une ou plusieurs aiguilles à traverser et du sens de circulation. La fonction qui retourne ce résultat est donc spécifique à chaque zone.

Or il existe une façon de programmer particulièrement adaptée à ce type de situation : la programmation OBJET, en C++ pour l’Arduino, qui est particulièrement recommandée pour ce type de projet.

Heureusement Locoduino nous guide sur ce sujet, grâce aux articles de Thierry :

Et grâce au Forum Locoduino, en particulier le fil Modélisation logicielle d’un réseau, avec les contributions de Jean-Luc, Denis et Pierre.

Pourquoi envisager la programmation orientée objet ?

En programmation standard (dite aussi structurée) comme le C de l’Arduino, il faut définir d’un coté les variables (globales et locales) et d’un autre coté les fonctions (sous-programmes), puis assembler tout cela dans une suite d’actions programmées dans la loop.
Définir la modélisation ou la topographie d’un réseau revient à écrire des tables d’éléments (variables et pointeurs) et des tables d’automates pour faire vivre le réseau. C’est terriblement compliqué (sauf pour de tout petits réseaux).

En C++, un objet est une structure informatique regroupant :

  • des variables, caractérisant l’état de l’objet,
  • des fonctions, caractérisant le comportement de l’objet.

Les variables s’appellent données-membres de l’objet.
Les fonctions s’appellent fonctions-membres ou encore méthodes de l’objet.
L’originalité dans la notion d’objet, c’est que variables et fonctions sont regroupées dans une même structure.
La classe est le type d’un objet.

Une variable de type classe est un objet qui contient tout ce qui lui est nécessaire pour fonctionner et n’expose que ce qui est nécessaire au monde extérieur : tout ce qui est interne à un objet peut être protégé du reste du programme ce qui garanti que les variables internes ne risquent pas d’être polluées par une erreur de programmation. De plus, les noms des variables sont bien plus faciles à gérer que s’ils devaient être déclarés en variables globales puisque ce nom n’est défini qu’une seule fois dans la définition de la classe. Le nom d’une variable ou d’une fonction de la classe de base est le même dans tous les objets de cette classe. Seul le nom de l’objet varie.

Par exemple un objet aiguille rassemble :

  • une variable « no » numéro qui servira à générer un message de commande vers l’aiguille réelle correspondante
  • une variable « état » pour indiquer si elle est en position droite (vrai) ou déviée (faux)
  • une fonction « devier() » pour la commander en position déviée et faire toutes les actions qui en découlent
  • une fonction « directer() » pour la commander en position directe et faire toutes les actions qui en découlent
  • une fonction « directe() » pour connaitre si sa position est directe
  • une fonction « deviee() » pour connaitre si sa position est déviée

Par exemple un objet zone rassemble (cette liste n’est pas limitative) :

  • une variable « no » numéro qui servira à générer un message de commande ou une requête relative à cette zone
  • une variable « état » pour indiquer s’il est libre (vrai) ou occupé (faux),
  • une variable « train » pour indiquer par quel train et dans quelle direction
  • une variable « signal » correspondant au signal à la fin de la zone
  • une fonction « suivantePaire » qui donnera la zone suivante paire en fonction des éventuelles aiguilles à traverser
  • une fonction « suivanteImpaire » qui donnera la zone suivante impaire (dans l’autre sens) en fonction des éventuelles aiguilles à traverser
  • une fonction « occuper » qui commandera à l’objet de réaliser toutes les actions en cas d’occupation comme, par exemple, allumer la led d’occupation sur le TCO et modifier l’état des signaux en aval de la zone (selon le sens de circulation)
  • une fonction « libérer » qui commandera à l’objet de réaliser toutes les actions en cas de libération
  • une fonction « libre » qui peut être appelée pour savoir si son état est libre ou occupé

En partant seulement de ces 2 exemples d’objets, on peut déjà comprendre comment va fonctionner le réseau :

  • à partir de clés d’aiguilles sur le TCO, on peut commander les aiguilles : un message de demande de changement de l’aiguille J généré par le TCO est décodé par le gestionnaire et entraîne l’appel de la fonction « directer » ou « dévier » de l’aiguille J concernée. Cette fonction va commander l’aiguille en envoyant un message au contrôleur d’aiguille qui retournera ensuite un message de position permettant d’allumer une led correspondant à sa position sur le TCO.
  • quand un train pénètre dans une zone N à la sortie de laquelle se trouve l’aiguille J, un détecteur d’occupation génère un message vers le gestionnaire qui entraîne l’appel de la fonction « occuper » de la zone concernée. Cette fonction va rechercher si le train peut continuer ou non à rouler en appelant la fonction « suivante paire » ou « suivante impaire » (selon le sens de marche) pour connaitre si la zone suivante est accessible en fonction de la position de l’aiguille J et de l’état de cette zone suivante. Le gestionnaire en déduit le signal à appliquer à la marche du train. Ce signal est envoyé par message au module de traction, pour le train concerné, et aux signaux éventuels.

Il est évident que la conduite des trains est aussi soumise au gestionnaire : un ordre de conduite décidé par un conducteur sur sa manette (démarrage, vitesse, direction), ne sera exécuté par le module de traction qu’après validation par le gestionnaire. Par exemple, si un ralentissement 30 s’impose dans une courbe serrée ou une sortie de gare traversant plusieurs aiguilles en position déviée, le train respectera cette consigne même si le pilote pousse la manette de vitesse à fond !

C’est ce principe qui permet de réaliser la sécurité sur le réseau.

Figure 6

Cet exemple est évidemment très simplifié : Dans la réalité il y a de nombreux types d’objets (trains, itinéraires, signaux, etc..) qui seront tous « autonomes » mais capables de coopérer entre eux à chaque événement humain ou événement de circulation.

J’ai surtout découvert une chose essentielle : la déclaration et la description des objets « zone » et « aiguille » suffit à décrire la topographie du réseau. Les autres objets la complètent et l’ensemble constitue bien la modélisation du réseau !

Pour preuve, ce test que j’ai posté sur le Forum : http://forum.locoduino.org/index.php?topic=72.15

Pour bien s’imprégner de la programmation objet, il faut rappeler les 3 principes qui doivent s’appliquer :

L’encapsulation

Il s’agit de définir une structure qui regroupe (ou encapsule) les données et les fonctions qui les utilisent. Une telle structure est appelée classe.
Une classe est donc une définition de structure dont le nom est un nouveau type.

La déclaration d’une variable de type classe est une instance de cette classe : elle est appelée objet.

Les classes sont composées de données membres (que l’on peut comparer aux champs des enregistrements de la programmation structurée) et de fonctions membres, qui définissent les opérations à réaliser sur les données membres.
C’est ce que nous avons esquissé ci-dessus en regroupant les variables et les fonctions appartenant à une même classe.

Nous verrons la description précise des classes Aiguille, Zone, Signal, Train, Itinéraire, etc.. dans les articles de Pierre.

L’héritage

Les classes peuvent être définies et utilisées de manière autonome, chaque classe constituant un ensemble homogène indépendant de toutes les autres classes.
Cependant, il peut être très utile qu’une classe soit construite à partir d’une autre classe, en conservant les propriétés de la classe d’origine et en ajoutant de nouvelles. Ce processus est possible et s’appelle héritage ou dérivation. Il conduit à la notion de hiérarchie de classe.

L’héritage (ou dérivation) est un mécanisme qui permet de construire des classes à partir d’autres classes, en définissant une nouvelle classe, appelée classe dérivée, comme une extension d’une classe existante, appelée classe de base. La dérivation permet à une classe dérivée d’hériter des propriétés d’une classe de base, c’est-à-dire des données et fonctions membres.

Ainsi, il est possible de compléter des classes, en rajoutant des données ou des fonctions membres, et/ou de les personnaliser, en redéfinissant des données (valeurs) ou des fonctions membres.

Pour les zones, par exemple, cela se traduit par une définition des zones suivantes paires et impaires qui diffère selon les zones et les aiguilles qui les occupent : cette définition qui nécessite donc l’héritage, nous conduira à la modélisation du réseau réel au moment de la déclaration des objets, instances de ces classes dérivées.

Le polymorphisme

Des fonctions et des méthodes de même nom peuvent avoir des comportements différents ou effectuer des opérations sur des données de types différents. L’on distingue 2 types de polymorphisme, la surcharge et la redéfinition.

La surcharge est une possibilité offerte par certains langages de programmation, et notamment le C++, qui permet de choisir entre différentes implémentations d’une même fonction ou méthode selon le nombre et le type des arguments fournis.

La notion de polymorphisme est très liée à celle d’héritage. Grâce à la redéfinition, il est possible de redéfinir une méthode dans des classes héritant d’une classe de base. Par ce mécanisme, une classe qui hérite des méthodes d’une classe de base peut modifier le comportement de certaines méthodes héritées pour être adaptées aux besoins de la classe fille.

Ces notions vous paraissent un peu trop théoriques ? Nous verrons dans l’article Un gestionnaire en C++ pour votre réseau comment ces notions s’appliquent concrètement à la définition et au fonctionnement du réseau.

Nous vous invitons ensuite à regarder l’exemple de mise en oeuvre de la modélisation de mon réseau en suivant le fil du forum.

Attention au départ du train !

[1Sauf, peut-être avec l’aide de décodeurs et détecteurs d’occupation compatibles "RailCom", ce qui est beaucoup plus cher !!!

2 Messages

Réagissez à « La génèse d’un réseau 100% Arduino »

Qui êtes-vous ?
Votre message

Pour créer des paragraphes, laissez simplement des lignes vides.

Lien hypertexte

(Si votre message se réfère à un article publié sur le Web, ou à une page fournissant plus d’informations, vous pouvez indiquer ci-après le titre de la page et son adresse.)

Rubrique « Projets »

Un chenillard de DEL

Enseigne de magasin

Feux tricolores

Multi-animations lumineuses

L’Arduino et le système de commande numérique DCC

Un décodeur d’accessoire DCC versatile basé sur Arduino

Un moniteur de signaux DCC

Une barrière infrarouge

Un capteur RFID

Un TCO xpressnet

Une animation sonore

L’Arduino au coeur des systèmes de pilotage analogiques ou numériques

Calcul de la vitesse d’un train miniature avec l’Arduino

La génèse d’un réseau 100% Arduino

Une horloge à échelle H0

Simulateur de soudure à arc

Un automatisme de Passage à Niveau

La rétro-signalisation sur Arduino

Décodeur pour aiguillage à solénoïdes sur Arduino

Un décodeur DCC pour les signaux à deux ou trois feux sur Arduino NANO/UNO

Etude d’un passage à niveau universel

Réalisation pratique d’un système de mesure de vitesse à l’échelle N

Une Passerelle entre le bus S88 et le bus CAN pour la rétro signalisation

Un décodeur DCC pour 16 feux tricolores

Block Automatique Lumineux avec la carte shield "Arduino 4 relays"

Réalisation d’un affichage de gare ARRIVEE DEPART

Comment piloter trains et accessoires en DCC avec un Arduino (1)

Comment piloter trains et accessoires en DCC avec un Arduino (2)

Comment piloter trains et accessoires en DCC avec un Arduino (3)

Comment piloter trains et accessoires en DCC avec un Arduino (4)

SGDD : Système de Gestion DD (1)

SGDD : Système de Gestion DD (2)

SGDD : Système de Gestion DD (3)

La PWM : Qu’est-ce que c’est ? (1)

La PWM : Qu’est-ce que c’est ? (2)

La PWM : Qu’est ce que c’est ? (3)

La PWM : Qu’est ce que c’est ? (4)

Mise en oeuvre du Bus CAN entre modules Arduino (1)

Mise en oeuvre du Bus CAN entre modules Arduino (2)

Un gestionnaire en C++ pour votre réseau (1)

Un gestionnaire en C++ pour votre réseau (2)

Un gestionnaire en C++ pour votre réseau (3)

Un gestionnaire en C++ pour votre réseau (4)

Réalisation de centrales DCC avec le logiciel libre DCC++ (1)

Réalisation de centrales DCC avec le logiciel libre DCC++ (2)

Réalisation de centrales DCC avec le logiciel libre DCC++ (3)

Contrôleur à télécommande infrarouge pour centrale DCC++

Gestion d’une gare cachée (1)

Gestion d’une gare cachée (2)

Gestion d’une gare cachée (3)

Les derniers articles

La PWM : Qu’est ce que c’est ? (4)


Jean-Luc

Block Automatique Lumineux avec la carte shield "Arduino 4 relays"


Christian Bézanger

Réalisation d’un affichage de gare ARRIVEE DEPART


Gilbert

Contrôleur à télécommande infrarouge pour centrale DCC++


Daniel

La PWM : Qu’est ce que c’est ? (3)


Jean-Luc

Réalisation de centrales DCC avec le logiciel libre DCC++ (3)


bobyAndCo

Un gestionnaire en C++ pour votre réseau (4)


Pierre59

Un décodeur DCC pour 16 feux tricolores


Dominique, JPClaude, Thierry

Réalisation de centrales DCC avec le logiciel libre DCC++ (2)


bobyAndCo

Réalisation de centrales DCC avec le logiciel libre DCC++ (1)


Dominique

Les articles les plus lus

Comment piloter trains et accessoires en DCC avec un Arduino (1)

La PWM : Qu’est-ce que c’est ? (1)

L’Arduino et le système de commande numérique DCC

Mise en oeuvre du Bus CAN entre modules Arduino (2)

Un chenillard de DEL

L’Arduino au coeur des systèmes de pilotage analogiques ou numériques

Décodeur pour aiguillage à solénoïdes sur Arduino

Une barrière infrarouge

Mise en oeuvre du Bus CAN entre modules Arduino (1)

Comment piloter trains et accessoires en DCC avec un Arduino (3)