LOCODUINO

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

mardi 19 mars 2024

Visiteurs connectés : 37

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

Aiguilles et Zones

.
Par : Pierre59

DIFFICULTÉ :

On va, dans cette série d’articles, développer pas à pas le noyau d’un programme de gestion de réseau en utilisant la programmation orientée objet en C++.

La programmation orientée objet peut se résumer en trois mots : encapsulation, héritage, polymorphisme. Il faut faire les trois choses pour vraiment faire de la programmation objet.

L’accent sera essentiellement mis sur la conception objet, les exemples seront donc écrits dans un C++ simplifié. Cela reste du vrai C++, mais pour pouvoir le compiler, il faudra ajouter quelques trucs techniques qui polluent un peu le programme et qui nuisent un peu à sa compréhension. On trouvera en fin d’article le programme C++ complet compilable avec aussi plus d’exemples.

<

Pour réaliser un programme de gestion de réseau à partir de ce noyau, il faut écrire des classes (en suivant les modèles joints), déclarer des objets, choisir les options désirées et écrire toute la partie d’interface avec le matériel : commande des aiguilles, des alimentations (analogiques ou digitales), ou des signaux, rétro-signalisation, gestion du TCO, gestion des bus, …

Pour bénéficier de toutes les possibilités du noyau, toutes les actions de l’utilisateur (commande d’aiguilles, d’itinéraires, de sens et vitesse des trains, ...) doivent êtres contrôlées par le programme avant leur application.

Ce noyau objet est très puissant et très facilement modifiable. On peut modifier le réseau facilement, sans tout remettre en cause dans le programme. On peut ajouter progressivement, suivant ses envies, des "gadgets" comme le suivi des trains (par exemple, afficher le nom du train sur le TCO, mais aussi le contrôle des sons pour les machines sonores, ..), la poursuite en canton (appelée aussi conduite sélective, elle concerne principalement le fonctionnement en analogique, un train étant contrôlé sur tout le réseau avec la même souris par commutation automatique des alimentations sur les zones), le cabsignal (l’affichage des signaux en cabine de conduite des machines, par exemple sur la souris qui affiche le signal réel que voit le train), etc …

Ce noyau objet est plutôt orienté vers la gestion de réseaux avec commande par itinéraires, signalisation, rétro-signalisation, circulation manuelle et automatique, .. ceci quelque soit leur taille. Mais il peut être aussi utilisé sur des réseaux simples en laissant tomber les parties optionnelles.

Comme le noyau est écrit en C++, il peut être utilisé avec un Arduino, un mini PC (genre PCduino) ou un vrai PC. Un Arduino UNO sera très vite insuffisant au niveau mémoire vive voire au niveau mémoire flash, un méga ou mieux un due est plus réaliste.

Première étape : l’encapsulation

La première chose à faire en programmation objet, c’est d’identifier les objets potentiels.
Dans notre cas, ceux ci ne manquent pas : aiguilles, zones, cantons, signaux, trains, TCOs, souris, …

Ensuite il faut écrire des classes pour chacun des objets retenus. Dans ce premier article on va s’intéresser aux objets aiguille et zone.

Les classes sont des modèles pour fabriquer des objets, à partir d’une classe on peut fabriquer autant d’objets que l’on veut, ces objets sont appelés instances de la classe. Par exemple si on a 10 aiguilles on fera 10 instances de la classe ’aiguille’ pour avoir 10 objets, un pour chaque aiguille.

On va commencer avec la classe aiguille, pour cela on écrit une classe en C++ :

class Aiguille {};

Dans une classe les variables sont souvent appelées "attributs" et les fonctions "méthodes".

Il faut maintenant remplir la classe. Pour cela il faut se demander quelles sont les informations que l’on a besoin de savoir sur une aiguille et quelles sont les actions que l’on veut effectuer sur une aiguille.

Pour les informations, on a besoin de savoir si l’aiguille est en position directe ou en position déviée.
On peut faire cela avec deux méthodes à résultat booléen : directe() et deviee(), l’état de l’aiguille étant mémorisé dans une variable booléenne : état.

Pour les actions, on a besoin de manœuvrer l’aiguille, cela peut se faire avec deux méthodes : directer() et devier(). Ces méthodes ne font la manœuvre que si nécessaire, elles renvoient un booléen pour savoir comment cela s’est passé (true=OK). Bien évidemment ces méthodes changent l’état. La méthode : manoeuvrer() fera la commande effective (voir plus loin).

Cela donne :

class Aiguille {
	booleen etat; // directe (true) ou deviee (false)

	boolean directe()  { return etat; }  // méthode d'acces
	boolean deviee()  { return !etat; } // méthode d'acces

	boolean directer() { 
		if (directe())  return true; 
		etat=true;  
		return manoeuvrer(true); 
	}
	boolean devier()   { 
		if (deviee())  return true; 
		etat=false; 
		return manoeuvrer(false); 
	}
};

Pour rendre la classe utilisable, il faut une méthode : manoeuvrer() qui va faire l’interface avec le matériel. Cette méthode a besoin d’un numéro pour pouvoir commander le matériel (directement ou par un bus). On va donc rajouter à la classe un entier (ou plusieurs si besoin) qui est une sorte de numéro d’aiguille. La méthode : manoeuvrer() utilisera ce numéro pour manœuvrer effectivement l’aiguille. Cette méthode devra être complétée par l’utilisateur. Ce numéro d’aiguille va être donné en utilisant un constructeur.

Reste un problème d’initialisation de l’état. Deux cas se présentent, si on peut connaitre la position des aiguilles à l’initialisation du programme de gestion (dans le setup() ) alors on appelle la méthode : init1(), sinon il faut appeler la méthode : init2() qui force la position de l’aiguille en la manœuvrant.

On va profiter de la présence du constructeur pour ajouter une information optionnelle qui est la zone dans laquelle est l’aiguille. Cela peut être utile pour réaliser un PRS (Poste à Relais à transit Souple) où on a besoin de savoir si l’aiguille est manœuvrable (si la zone correspondante n’est pas occupée), la méthode : manoeuvrable() est prévue pour cela.

De façon générale les objets seront manipulés par le biais de pointeurs. Cela permet de profiter des mécanismes objets que nous utiliserons par la suite. Manipuler les objets (ou les structures) avec des pointeurs c’est normal en C++, il existe d’ailleurs un opérateur( -> ) prévu pour cela.

Ce qui donne :

class Aiguille {
	boolean etat; // directe (true) ou déviée (false)
	int no;       // numéro de l'aiguille
	Zone* zone;   // la Zone ou est l'aiguille (optionnel)

        Aiguille(int n) { no=n; } // constructeur mini
	Aiguille(int n,Zone* z) { no=n; zone=z; } // constructeur maxi

	boolean directe()  { return etat; }  // méthode d’accès
	boolean deviee()  { return !etat; } // méthode d’accès

	boolean directer() { 
		if (directe())  return true; 
		etat=true;  
		return manoeuvrer(true); 
	}  

	boolean devier()   { 
		if (deviee())  return true; 
		etat=false; 
		return manoeuvrer(false); 
	}

	boolean manoeuvrable()  { return zone->libre(); } // (optionnel)

	boolean manoeuvrer(boolean e) { // commande de l'aiguille avec le numéro  
		return true; // return false si cela c'est mal passe
 	}

	void init1(boolean e) { etat=e; }
	void init2(boolean e) { if (e) commander(e); else commander(!e);  etat=e; }
};

Voici deux exemples d’aiguilles (instanciations) :

	Aiguille* a1=new Aiguille(1);  // aiguille N°1 
	Aiguille* a2=new Aiguille(2, z9);  // aiguille N°2 dans la zone z9

Maintenant passons à la classe Zone.

Comme pour Aiguille, la zone a deux états : libre et occupé, un booléen convient donc.

A l’instar d’Aiguille, on va avoir deux méthodes, libre() et occupee() pour tester l’état. Deux méthodes occuper() et liberer(), pour modifier l’état, ces deux méthodes doivent êtres appelées par la rétro-signalisation, une table de pointeurs sur les zones facilitera la tâche.
Ces deux méthodes appellent les méthodes actions() et desactions() qui sont destinées à faire les actions spécifiques à une zone particulière à l’occupation de la zone particulière et à sa libération.

class Zone { 
	boolean etat; // libre (false) ou occupé (true)
   
	boolean occupee() { return etat; }  // méthode d’accès
	boolean libre()   { return !etat; } // méthode d’accès

	void occuper() { // appelée par la rétro-signalisation
   	// fait tout ce qu'il y a à faire en cas d'occupation (actions communes à toutes les zones)
   	actions(); // fait les actions spécifiques à une zone
	}

 	void liberer() { // appelée par la rétro-signalisation
  		// fait tout ce qu'il y a à faire en cas de libération (actions communes à toutes les zones)
   	desactions(); // fait les actions spécifiques à une zone
	}

	void actions() {} // les actions spécifiques à faire en cas d'occupation
	void desactions() {} // les actions spécifiques à faire en cas de libération
 
        void init(boolean e) { etat=e; }
};

Les deux méthodes : occuper() et liberer(), font tout ce qu’il y a faire lors de l’occupation d’une zone et de sa libération. Pour l’instant c’est presque vide mais il faudra faire la mise à jour du TCO, le suivi des trains, le cabsignal, etc …

Ces deux méthodes font tout ce qu’il y a de commun à toutes les zones. Mais pour chaque zone il y a des choses particulières à faire : aubiner les signaux, gérer le BAL, gérer les itinéraires. … .
Cela va être le rôle de deux méthodes, la méthode : actions() qui fait les actions à faire lors de l’occupation et : desactions() lors de la libération.

Comme pour aiguille il reste un problème d’initialisation de l’état. A l’initialisation du programme de gestion (dans le setup() ) la rétro-signalisation doit appeler, pour chaque zone, la méthode : init() pour indiquer l’état initial de la zone.

Deuxième étape : l’héritage

Bien que complète la classe zone n’est pas utilisable, d’autant que les méthodes : actions() et : desactions() ne font rien.
Lors de l’occupation et de la libération d’une zone, outre des actions communes à toutes les zones, en pratique il y a des actions spécifiques à chaque zone à faire : aubiner les signaux, gérer le BAL, gérer les itinéraires. …
C’est le rôle des deux méthodes : actions() et : desactions(). Pour cela on pourrait utiliser deux pointeurs sur des fonctions passés en paramètres du constructeur, et utilisés dans ces méthodes mais il y a une façon plus naturelle en programmation objet, c’est l’héritage.

Pour chaque zone du réseau on va écrire une classe spécifique qui héritera de la classe de base Zone et qui va redéfinir les méthodes : actions() et : desactions() pour leur faire faire les actions propres à chaque zone.

class Z8 : Zone { // héritage de Zone
        void actions() { … } // les actions spécifiques à faire en cas d'occupation
        void desactions() { … } // les actions spécifiques à faire en cas de libération
};

class Z9 : Zone { // héritage de Zone
        void actions() { … } // les actions spécifiques à faire en cas d'occupation
        void desactions() { … } // les actions spécifiques à faire en cas de libération
 };

Pour faire des choses plus intéressantes il faut enrichir la classe. Deux choses sont bien utiles, d’une part les deux signaux éventuels implantés sur la zone. D’autre part l’accès à la zone suivante et à la zone précédente.

Pour les signaux c’est facile, on ajoute deux variables de type pointeur de signal (on verra les signaux dans un prochain article), un constructeur à deux paramètres (pointeur de signal) et deux méthodes d’accès, les sens sont notés comme à la SNCF (pair et impair), en l’absence d’un signal on met NULL (le pointeur vide) :

class Zone { //  les ajouts a la classe zone
	…
	Signal* signalPair; 	// les signaux éventuels de la zone
	Signal* signalImpair; 

	Zone(Signal* sp,Signal* si) { // constructeur
		signalPair=sp; 
		signalImpair=si; 
	} 
 
	Signal* signalPair()     { return signalPair; } // méthode d'accès
	Signal* signalImpair() { return signalImpair; }  // méthode d'accès
	…
};

Pour les zones suivantes et précédentes on pourrait aussi envisager de les passer au constructeur mais dans notre cas deux problèmes empêchent d’avoir recours à cette technique.

  • Le premier problème est que les zones s’appellent mutuellement : Par exemple, si on a deux zones z1 et z2 la suivante paire de z1 peut être z2 et la suivante impaire de z2 être z1, c’est impossible à déclarer, car il faudrait déclarer z1 avant z2 et z2 avant z1 !
  • Le deuxième problème vient du fait que les zones suivantes dépendent éventuellement de la position d’une (ou plusieurs) aiguille. Cela reflète l’aspect dynamique du réseau et cet aspect est essentiel pour la modélisation.

On ne peut donc pas passer par le constructeur directement. On pourrait envisager de passer des pointeurs sur des fonctions, mais il est plus pratique d’utiliser l’héritage d’autant plus que l’on a déjà commencé.

On va donc pour chaque zone réelle compléter la classe spécifique déjà écrite en lui ajoutant deux méthodes : suivantePaire() et : suivanteImpaire(). Ces deux noms de méthode sont préférés aux noms suivante() et precedente() car il ne dépendent pas du sens de circulation.

Pour que l’héritage et les mécanismes objets se passent bien en C++ (on en parlera dans le prochain article), il faut ajouter aussi ces méthodes à la classe de base Zone. Il faut aussi, pour toutes les méthodes qui sont redéfinies ainsi, mettre le mot clé virtual devant les méthodes (actions(), desactions(), suivantePaire(), suivanteImpaire() ).

Il ne reste plus qu’a tout réunir, la classe de base zone :

class Zone { 
	boolean etat; // libre (false) ou occupe (true)
	Signal* signalPair; 	// les signaux éventuels de la zone
	Signal* signalmpair; 

	Zone(Signal* sp,Signal* si) { // constructeur
		signalPair=sp; 
		signalImpair=si; 
	}
   
	boolean occupee() { return etat; }  // méthode d’accès
	boolean libre()   { return !etat; } // méthode d’accès

	void occuper() { // appelée par la rétrosignalisation
		// fait tout ce qu'il y a à faire en cas d'occupation 
		// (actions communes à toutes les zones)
		actions(); // fait les actions spécifiques à une zone
	}

	void liberer() { // appelée par la rétrosignalisation
		// fait tout ce qu'il y a à faire en cas de libération 
		// (actions communes à toutes les zones)
		desactions(); // fait les actions spécifiques à une zone
	}

	virtual void actions() {} // les actions spécifiques à faire en cas d'occupation
	virtual void desactions() {} // les actions spécifiques à faire en cas de libération

 	Signal* signalPair()     { return signalPair; } // méthode d'acces
	Signal* signalImpair() { return signalImpair; } // méthode d'acces

	virtual Zone* suivantePaire()   { return NULL; } // la zone suivante paire (éventuellement vide)
	virtual Zone* suivanteImpaire() { return NULL; } // la zone suivante impaire (éventuellement vide)

	void init1(boolean e) { etat=e; } // initialisation

	Zone* selonAiguille(Aiguille* a,Zone* z1,Zone* z2) { // méthode utilitaire
		return a->directe()?z1:z2; 
		}
};

La méthode utilitaire selonAiguille() de la classe zone permet de prendre en compte l’aspect dynamique du réseau en fonction de la position réelle des aiguilles, cet aspect est essentiel au fonctionnement du gestionnaire de réseau.

Exemples de zones réelles (les exemples sont là pour montrer tous les cas caractéristiques d’utilisation, mais ils ne correspondent à aucun réseau concret) :

class Z8 : Zone { // héritage de Zone
	Z8(Signal* sp,Signal* si):Zone(sp,si) {} // constructeur

	virtual void actions() { c2.aubiner();  } 
		// les actions spécifiques à faire en cas d'occupation
	virtual  void desactions() { } 
		// les actions spécifiques à faire en cas de libération
	virtual Zone* suivantePaire()   { return NULL; } 
		//pas de zone suivante paire 
	virtual Zone* suivanteImpaire() { return z9; } 
		// la zone suivante impaire 
} ;

class Z9 : Zone { // héritage de Zone
	Z9(Signal* sp,Signal* si):Zone(sp,si) {}  // constructeur

	virtual  void actions() {s2.aubiner();  } 
		// les actions spécifiques à faire en cas d'occupation
	virtual void desactions() { … } 
		// les actions spécifiques à faire en cas de libération
	virtual Zone* suivantePaire()   { return selonAiguille( a1,z8,z9 } 
		// la zone suivante paire depends de a1
	virtual Zone* suivanteImpaire() { return selonAiguille( a1,selonAiguille( a2,z8,z9),z9; } 
		// la zone suivante impaire depend de a1 et a2
};

Exemples de déclarations d’objets signaux, zones, aiguilles :

	Signal* c2=new C2(); // signaux
	Signal* s2=new S2();

	Zone* z8=new Z8(s2,c2); // zones
	Zone* z9=new Z9(s2,NULL);

	Aiguille* a1=new Aiguille(1,z8); // aiguilles
	Aiguille* a2=new Aiguille(2,z9);

	Zone* tableZones[]  {z8,z9} // la table des zones pour la retrosignalisation

L’exemple précédent n’est pas directement compilable, il faut mettre des protections (mots clés : public, protected, …), il faut respecter un ordre très précis des déclarations de classes et de variables car les objets s’utilisent les uns les autres.

Les méthodes qui sont écrites complètement dans la classe sont réputées "inline", c’est à dire que le compilateur ne fait pas un vrai appel de méthode mais met les instructions de la méthode directement dans le programme. C’est très bien pour les méthodes courtes comme les méthodes d’accès ou certaines méthodes utilitaires. Pour les autres il faut juste les spécifier dans la classe et les écrire en dehors de la classe en rappelant le nom de la classe. Dans notre cas cela facilite la prise en compte des inextricables problèmes avec l’ordre des déclarations.

On trouvera ci dessous un exemple compilable plus complet, on a rajouté tout ce qu’il faut pour que cela soit compilable et ajusté l’ordre des déclarations qu’il faut scrupuleusement respecter pour que cela reste compilable.

On a aussi diversifié les exemples, ajouté la possibilité de donner des noms et des types aux objets avec les constructeurs nécessaires. Il y a aussi des choses (variables et méthodes) qui seront utilisées dans les prochains articles.

Noyau de base Gestionnaire C++

Dans les articles suivant on verra les classes Signal, Train, Itinéraire ainsi que des gadgets comme le suivi des trains, la poursuite en canton, le cabsignal, …

Si vous souhaitez tester cette première partie avec une modélisation réelle de votre propre réseau, vous pouvez suivre le fil « modélisation logicielle d’un réseau, ici : http://forum.locoduino.org/index.ph...

et aussi : ici dans le forum

17 Messages

Réagissez à « Un gestionnaire en C++ pour votre réseau (1) »

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 »

LaBox, Une Centrale DCC polyvalente et abordable (1)

LaBox, Une Centrale DCC polyvalente et abordable (2)

LaBox, Une Centrale DCC polyvalente et abordable (3)

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)

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)

La carte Satellite V1 (1)

La carte Satellite V1 (2)

La carte Satellite V1 (3)

La carte Satellite V1 (4)

La carte Satellite V1 (5)

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

Automatisation du pont FLEISCHMANN 6152 (HO) avec un ESP32 (1)

Identifier et localiser vos trains avec le RFID/NFC et un bus CAN.

Etude d’un passage à niveau multivoies

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

Ménage à trois (Ordinateur, Arduino, réseau)

Réalisation d’un va-et-vient automatique et réaliste

Souris et centrale sans fil

Communications entre JMRI et Arduino

Annonces en gare avec la RFID

Une croix de pharmacie animée avec Arduino UNO

Réalisation d’un wagon de mesure (distance et vitesse)

Passage à niveau géré par Arduino (1)

Passage à niveau géré par Arduino (2)

Passage à niveau géré par Arduino (3)

Passage à niveau géré par Arduino (4)

Passage à niveau géré par Arduino (5)

Une manette simple et autonome pour LaBox

Éclairer le réseau (1)

Éclairer le réseau (2)

Block Automatique Lumineux à 8 cantons analogiques

Un décodeur DCC pour les plaques tournantes Fleischmann et Roco

Éclairer le réseau (3)

Éclairer le réseau (4)

Éclairer le réseau (5)

JMRI pour Ma première centrale DCC

Rocrail pour Ma première centrale DCC

CDM-Rail pour Ma première centrale DCC (1)

CDM-Rail pour Ma première centrale DCC (2)

Banc de test pour les décodeurs DCC

Ma première manette pour les aiguillages DCC

Mon premier décodeur pour les aiguillages DCC

Boitier 3D pour la station DCC minimale

Va-et-vient pour deux trains

Un programme pour régler facilement les servos moteurs avec un ESP32

Affichage publicitaire avec Arduino (1)

Affichage publicitaire avec Arduino (2)

TCO Web interactif avec des ESP32 et des ESP8266 (1)

TCO Web interactif avec des ESP32 et des ESP8266 (2)

TCO Web interactif avec des ESP32 et des ESP8266 (3)

TCO Web interactif avec des ESP32 et des ESP8266 (4)

TCO Web interactif avec des ESP32 et des ESP8266 (5)

Les derniers articles

LaBox, Une Centrale DCC polyvalente et abordable (3)


Thierry

LaBox, Une Centrale DCC polyvalente et abordable (1)


Thierry

LaBox, Une Centrale DCC polyvalente et abordable (2)


Dominique, msport, Thierry

Un programme pour régler facilement les servos moteurs avec un ESP32


bobyAndCo

TCO Web interactif avec des ESP32 et des ESP8266 (5)


utpeca

TCO Web interactif avec des ESP32 et des ESP8266 (4)


utpeca

TCO Web interactif avec des ESP32 et des ESP8266 (3)


utpeca

TCO Web interactif avec des ESP32 et des ESP8266 (2)


utpeca

TCO Web interactif avec des ESP32 et des ESP8266 (1)


utpeca

Affichage publicitaire avec Arduino (2)


catplus, Christian

Les articles les plus lus

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

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

Mon premier décodeur pour les aiguillages DCC

La rétro-signalisation sur Arduino

Chenillard de DEL

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

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

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

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

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