LOCODUINO

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

mercredi 19 décembre 2018

58 visiteurs en ce moment

Souris et centrale sans fil

. Par : tony04

Cet article peut paraitre surprenant surtout à la suite du salon d’Orléans ou les solutions "tout automatique" ou "tout informatisé" présentées au magnifique stand Locoduino ont eu un énorme succès.
Il est vrai qu’il est très plaisant de voir circuler des trains en toute sécurité en les suivant à l’écran de son Smartphone, de sa tablette ou de son ordinateur, mais on peut aussi trouver beaucoup de plaisir à "jouer" manuellement avec une ou plusieurs machines, tout au moins sur une partie de son réseau.
L’originalité du projet tient entre autre dans le stockage maximum de 59 locos dans la souris, ce qui permet de travailler avec sa propre souris à la maison ou dans son club en ayant toutes ses machines sous la main (sous conditions d’avoir la même centrale au club).

Historique

En fréquentant des clubs de modélisme ferroviaire il est apparu que les souris utilisées ne convenaient pas toujours à tout le monde. L’idée d’en faire une sur mesure a germé et c’est en prenant en main le boitier HAMMOND que j’ai décidé de me lancer dans ce projet, tant ce boitier est agréable à manipuler. Le cahier des charges a été défini avec l’aide des membres de mon club. La demande était, entre autres, de pouvoir suivre en toute liberté son convoi tout au long du réseau, qui peut être très vaste dans certains clubs, ce qui m’a orienté vers le sans fil.
Après de longues semaines de tests avec les "pointures" du club, le projet est enfin abouti et prêt à être mis en ligne.
Depuis que j’utilise ce type de souris "liberté" il me serait difficile de revenir à une souris classique.
De plus aucun article sur ce sujet n’a jamais été présenté sur le site Locoduino (à ma connaissance en tous cas).
Cette souris/centrale sera facilement intégrable dans n’importe quelle configuration présentée sur Locoduino grâce à la présence d’un bus CAN dans la centrale.

Voici celle que je vous propose de réaliser :

Ma souris

En quoi consiste le projet ?
Il s’agit de réaliser une centrale 100% DCC   (pas d’analogique) commandée par une ou plusieurs souris avec liaison radio (30 et plus sont tout à fait envisageables).

Les caractéristiques sont les suivantes :

La souris :

  • Boitier ergonomique, robuste et esthétique équipé d’un clavier alphanumérique.
  • Afficheur OLED 1,54’’ très lisible.
  • Mise en mémoire (dans la souris) de 59 machines avec leur adresse et leur nom sur 12 caractères.
  • Une "liste du jour" de 10 locos sélectionnables directement par appui de la touche * suivi de 0 à 9 du clavier. Chaque loco garde sa vitesse et ses fonctions en mémoire.
  • 28 fonctions standards vers DCC   et 72 fonctions programmables envoyées sur le bus CAN de la centrale. La fonction lumière affiche une petite ampoule, les autres sont affichées dans la dernière ligne du bas.
  • Menu complet avec Liste triée sur nom, Liste triée sur adresse, Création nouvelle loco, Programmation de CVs.
  • Programmation automatique des adresses longues et courtes (ce qui modifie automatiquement les CV1, CV29, 17 et 18).
  • Programmation en mode POM possible.
  • Utilisation 24H en continu sans recharge selon la batterie installée.

La mémoire EEPROM de la souris comporte une "bibliothèque" de 59 locos avec un N° d’ordre, une adresse DCC et un nom. Il est possible de créer très rapidement une "liste du jour" de 10 locos maximum (parmi les 59), c’est avec cette liste qu’on va "jouer". Si le N° d’ordre de la loco est inférieur à 10 la loco est dans la liste du jour.
Je vous propose de feuilleter le mode d’emploi ci-dessous pour vous donner une idée plus précise de ses caractéristiques.

Mode d’emploi souris

La centrale :

La centrale est équipée de base :

  • d’un module Bluetooth (commande par Smartphone possible, programme Android disponible)
  • d’un module radio acceptant 6 souris (extensible par modules de 6 souris)
  • d’un bus CAN permettant toutes les extensions possibles
  • d’une sortie DCC voie MAIN 43A protégée selon les besoins du réseau
  • d’une sortie DCC voie de PROGRAMMATION de 3A.
    La centrale garde en mémoire les vitesses et fonctions même en cas de coupure de la tension DCC.
    Aucune commande ne se trouvant sur la centrale, celle-ci peut être cachée sous le réseau.

Est-il compliqué à réaliser ?

La réalisation de cet ensemble, et principalement de la souris, demande une grande rigueur mécanique, principalement au niveau de la découpe du boitier. Tous les gabarits de découpe, les plans, les schémas, les films CI, les sketchs et la liste des composants sont disponibles sur le site à la fin de cet article. Un circuit imprimé double face+trous métallisés+vernis épargne (pour la souris et pour la centrale) est également disponible sur simple demande.
Au niveau des sketchs, beaucoup "d’anciens" (ayant pratiqué l’assembleur) vont retrouver leur manière de programmer car je ne maîtrise absolument pas, à mon grand regret, le "monde des objets" si cher à Locoduino (à juste titre). Ce sera donc une programmation pas à pas avec un maximum de commentaires pour guider au mieux les utilisateurs.
Le coût de l’ensemble se situe aux alentours de 50€ pour la centrale et 40€ pour la souris.

Réalisation de la souris :

Commençons par le plus difficile, la souris.

Choix du matériel :

  • Ecran Oled pour sa qualité d’affichage.
  • Codeur incrémental avec bouton poussoir pour garder les vitesses de chaque loco en mémoire.
  • Clavier alphanumérique pour saisir facilement les noms des locos.
  • Arduino PRO/MINI 3,3V pour pouvoir utiliser une batterie de 3,7V très économique.
  • Module radio NRF24L01 pour ses 6 tampons mémoires émission/réception.

Voici le schéma complet de la souris ; ne figure pas les condensateurs de découplages conseillés sur l’alimentation du NRF24L01 ni les liaisons GND de certains composants :

Schéma souris

L’écran Oled et le NRF24L01 sont tous les deux contrôlés par le bus SPI   du PRO/MINI, avec les bits A1, A2 et A3 pour sélectionner l’écran et les bits 9 et 10 pour sélectionner le NRF.
On utilise l’interruption 0 pour lire les rotations du codeur.
L’entrée A6 a la double fonction de lire le niveau de tension de la batterie à travers le pont de résistances 10K/20K et de lire l’état du bouton d’arrêt d’urgence Stop.
Je me suis gardé les bits 1 et A5 pour pouvoir éventuellement ajouter une mémoire EEPROM (SPI  ) extérieure de grosse capacité pour les personnes qui possèdent plus de 59 machines à mettre en mémoire.

Le programme de la souris :

Sketch souris

J’ai laissé en tête de croquis plusieurs commentaires et liens qui m’ont permis de concevoir ce dernier. N’hésitez pas à consulter ces liens très enrichissants.

Voici les liens pour télécharger les différentes librairies :
Pour l’afficheur : https://github.com/olikraus/u8glib
Pour le NRF24L01 : https://github.com/nRF24/RF24
Pour l’anti-rebonds des touches : https://github.com/thomasfredericks/Bounce-Arduino-Wiring/archive/master.zip
Pour le clavier : http://www.arduino.cc/playground/uploads/Code/Keypad_1-0.zip

Je ne vais pas commenter le sketch complet, ce serait très vite rébarbatif, je vais juste présenter les blocs principaux avec leurs fonctions.

Les dix premières lignes permettent de modifier rapidement quelques comportements du programme (Attention ! Les N° de lignes ne correspondent pas forcément au programme).

  1. const char version[] = "1.10"; // changer ce numéro pour version
  2. #define tempo_alpha 700 // millisecondes pour touches alphanumériques (700)
  3. #define tempo_touche 500 // millisecondes pour touches fonctions (500)
  4. #define tempo_loco 1000 // millisecondes pour touches locos (500)
  5. #define palier_1 9 // 1er palier de changement d'incrément
  6. #define inc_palier_1 2 // incrément après 1er palier
  7. #define palier_2 20 // 2è palier de changement d'incrément
  8. #define inc_palier_2 5 // incrément après 2è palier
  9. #define palier_3 50 // 3è palier de changement d'incrément
  10. #define inc_palier_3 12 // incrément après 3è palier
  11. // si on veut autoriser la programmation en mode POM, dé-commenter la ligne suivante
  12. // #define aff_pom 1

tempo_alpha permet de modifier la tempo entre 2 appuis successifs des touches alphanumériques
tempo_touche est la tempo maximum pour la saisie de la seconde valeur des touches de fonction qui se fait sur 1 ou 2 chiffres.
tempo_loco idem mais pour la saisie d’une loco dans la liste du jour avec la touche *
Les différents paliers permettent d’incrémenter ou de décrémenter plus rapidement la vitesse de la loco selon la valeur des 3 paliers. Si on ne veut pas utiliser de palier il suffit de mettre palier_1, palier_2 et palier_3 à 128.
Si l’on désire autoriser la fonction "POM" (programmation sur la voie principale), il faut dé-commenter la ligne qui définit "aff_pom". Cela rajoute une option sur la page de programmation des CVs.

Les #include :
  1. #include <Arduino.h>
  2. #include "U8glib.h"
  3. #include <nRF24L01.h>
  4. #include <RF24.h>
  5. #include <Bounce2.h>
  6. #include <EEPROM.h>
  7. #include <Keypad.h>

Si vous avez déjà travaillé avec un écran Oled, vous serez certainement surpris du choix de la librairie U8glib.h qui est une ancienne librairie qui n’est plus maintenue et dont l’auteur propose de se rabattre sur la nouvelle version qui est U8g2lib.h.
Ce choix s’est imposé à moi car, d’après mes essais, c’est la librairie qui consomme le moins de mémoire vive et qui permet malgré tout de beaux graphismes. Par contre cela impose une gymnastique un peu particulière pour l’affichage d’une page car à chaque ajout de texte il faut entièrement rafraîchir cette dernière. Une petite explication (en anglais) ici : https://github.com/olikraus/u8glib/wiki/thelloworld

Les constantes :
  1. // menu
  2. #define MENU_ITEMS 5
  3. const char *menu_strings[MENU_ITEMS] = { "Liste par nom", "Liste par adresse", "Nouvelle Loco", "Gestion CVs", "Quitter" };

C’est le menu qui vous est proposé en appuyant la touche # .

Les canaux radio :

  1. // il y a 10 canaux possibles
  2. const uint64_t pipes[] = {0xB3B4B5B601LL, 0xB3B4B5B612LL, 0xB3B4B5B623LL, 0xB3B4B5B634LL, 0xB3B4B5B645LL, 0xB3B4B5B656LL, 0xB3B4B5B667LL, 0xB3B4B5B678LL, 0xB3B4B5B689LL, 0xB3B4B5B69ALL};

Pourquoi 10 canaux et non pas 6 comme l’exige le NRF24L01 ?
Au club la centrale est équipée d’une carte radio supplémentaire avec 4 canaux en plus ce qui permet d’utiliser 10 souris en même temps.
Le choix du canal se fait en fonction de la valeur num_souris lue dans l’EEPROM et modifiable par appui de la touche grise (mise en route DCC) au moment de l’allumage de la souris puis la saisie d’un chiffre de 0 à 9. Ce N° s’affiche pendant 2s à l’allumage ainsi que la version du programme.

Les touches alpha-numériques :

  1. const char lettre[10][4] = {
  2. {'Z','Q','-','0'},
  3. {' ',':','+','1'},
  4. {'A','B','C','2'},
  5. {'D','E','F','3'},
  6. {'G','H','I','4'},
  7. {'J','K','L','5'},
  8. {'M','N','O','6'},
  9. {'P','R','S','7'},
  10. {'T','U','V','8'},
  11. {'W','X','Y','9'},
  12. };

Ce tableau permet de modifier éventuellement les caractères utilisés pour les noms des locos ; chaque ligne correspond à son chiffre en fin de ligne.

Avant le setup se trouvent les différents sous-programmes d’affichage de pages et quelques fonctions utilisées plus loin dont la très courte routine d’interruption du codeur :

  1. // routine interruption du codeur
  2. void routineInterruption () {
  3. if (digitalRead(PinA)) {
  4. up = !digitalRead(PinB);
  5. }
  6. else {
  7. up = digitalRead(PinB);
  8. }
  9. mouvement = true;
  10. }

ou la routine d’affichage de la page "jeu" assez complexe :

  1. void draw_Jeux() { // affiche la page en position JEU
  2. u8g.setFont(u8g_font_6x13);
  3. u8g.setFontRefHeightText();
  4. u8g.setFontPosTop();
  5. // Afficher la ligne d'état
  6. sprintf(ligne1, " Ordre:%d Adr:%d", ordre, adresse_loco);
  7. u8g.drawStr(0, 0, ligne1);
  8. // Afficher le nom de la loco ou Loco ? si choix loco en cours
  9. u8g.setFont(u8g_font_helvB12); // hauteur 12 pixels GRAS 64%
  10. if (choix == 0) {
  11. for (i = 0; i < 16; i++) {
  12. ligne1[i] = nom_loco[i]; // initialiser avec la loco 0
  13. }
  14. d = ((128 - u8g.getStrWidth(ligne1)) / 2);
  15. u8g.drawStr(d, 28, ligne1);
  16. }
  17. else {
  18. u8g.drawStr(42, 28, "Loco ?"); // Afficher le nom de la loco ou Loco ? si choix loco en cours
  19. }
  20. // Afficher ampoule si fonction 0 activée
  21. if (fonction[ordre][0]) {
  22. u8g.drawCircle(centre_co, centre_li, 4);
  23. u8g.drawLine(15, centre_li - 8, 15, centre_li - 6);
  24. u8g.drawLine(15, centre_li + 6, 15, centre_li + 8);
  25. u8g.drawLine(centre_co - 8, centre_li, centre_co - 6, centre_li);
  26. u8g.drawLine(centre_co + 6, centre_li, centre_co + 8, centre_li);
  27. u8g.drawLine(centre_co - 7, centre_li - 6, centre_co - 5, centre_li - 4); // "\"
  28. u8g.drawLine(centre_co + 5, centre_li - 4, centre_co + 7, centre_li - 6); // "/"
  29. u8g.drawLine(centre_co - 7, centre_li + 6, centre_co - 5, centre_li + 4); // "/"
  30. u8g.drawLine(centre_co + 5, centre_li + 4, centre_co + 7, centre_li + 6); // "\"
  31. }
  32. // Afficher la vitesse
  33. if (tension == 1) {
  34. cpt = crans[ordre]; // crans[] = crans réels envoyés, cpt = crans affichés à l'écran
  35. sens[ordre] = 1;
  36. if (crans[ordre] > 0) {
  37. sprintf(buf, "%3d >", cpt + 2);
  38. d = ((128 - u8g.getStrWidth(buf)) / 2) + 10;
  39. }
  40. else if (crans[ordre] == 0) {
  41. sprintf(buf, "%3d", 0);
  42. d = ((128 - u8g.getStrWidth(buf)) / 2);
  43. }
  44. else if (crans[ordre] < 0) {
  45. cpt = - crans[ordre];
  46. sprintf(buf, "< %d", cpt + 2);
  47. sens[ordre] = 0;
  48. d = ((128 - u8g.getStrWidth(buf)) / 2) - 6;
  49. if (d < 0) {
  50. d = 0;
  51. }
  52. }
  53. }
  54. else if (tension == 0) {
  55. sprintf(buf, "%s", "STOP");
  56. d = 40;
  57. }
  58. else {
  59. sprintf(buf, "%s", "DISJONCTE");
  60. d = 9;
  61. }
  62. u8g.drawStr(d, 46, buf);
  63. // afficher niveau batterie
  64. u8g.setFont(u8g_font_6x13);
  65. u8g.setFontRefHeightText();
  66. u8g.setFontPosTop();
  67. u8g.drawFrame(106, 38, 22, 9);
  68. if (moyenne > 0) {
  69. byte volt = 0; //(0 = 1 barre, 5 = 6 barres)
  70. if (moyenne > 850) { // 6 barres
  71. volt = 5;
  72. }
  73. else if (moyenne > 829) { // 5 barres
  74. volt = 4;
  75. }
  76. else if (moyenne > 808) { // 4 barres
  77. volt = 3;
  78. }
  79. else if (moyenne > 787) { // 3 barres
  80. volt = 2;
  81. }
  82. else if (moyenne > 766) { // 2 barres
  83. volt = 1;
  84. }
  85. else if (moyenne > 745) { // 1 barre
  86. volt = 0;
  87. }
  88. for (byte i = 0; i <= volt; i++) {
  89. u8g.drawLine((i * 3) + 109, 40, (i * 3) + 109, 44);
  90. }
  91. }
  92. // Afficher les fonctions
  93. d = 0; // au départ afficher à partir de gauche
  94. if (w > 128) {
  95. d = -(w - 128); // si ligne trop longue, décaler à gauche
  96. }
  97. u8g.drawStr(d, 54, ligne2); // n'afficher que la fin de la ligne des fonctions
  98. }
Le setup :
  1. //Serial.begin (9600); // Initialisation du port série
  2.  
  3. ordre = EEPROM.read(1020); // lire la dernière loco utilisée en EEPROM
  4. contraste = EEPROM.read(1021); // récupérer dernier contraste en EEPROM
  5. num_souris = EEPROM.read(1022); // lire le numéro de souris en EEPROM
  6. PTXpipe = pipes[num_souris]; // récupérer le canal correspondant
  7.  
  8. rech_loco_ordre(ordre); // chercher les infos de la dernière loco utilisée
  9.  
  10. attachInterrupt (0,routineInterruption,CHANGE);
  11.  
  12. pinMode(btn_STOP,INPUT_PULLUP);
  13. pinMode(btn_START,INPUT_PULLUP);
  14. pinMode(PinA,INPUT_PULLUP);
  15. pinMode(PinB,INPUT_PULLUP);
  16. pinMode(btn_codeur,INPUT_PULLUP);
  17.  
  18. boun_btn_codeur.attach(btn_codeur);
  19. boun_btn_codeur.interval(10);
  20.  
  21. radio.begin();
  22. radio.setPALevel(RF24_PA_MAX); //RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH and RF24_PA_MAX
  23. radio.setChannel(108); //canal 108 au dessus des canaux WiFi (de 0 à 125)
  24. radio.setDataRate( RF24_250KBPS ) ; // 250Kbps pour max distance
  25. radio.openReadingPipe(0, PTXpipe); // ouvrir le canal PTXpipe en réception
  26. radio.openWritingPipe(PTXpipe); // ouvrir un canal en émission avec même adresse
  27. radio.stopListening(); // se mettre en émission
  28.  
  29. crans[ordre] = 0;
  30. mouvement = false;
  31. // Lecture niveau batterie, RAZ du tableau d'échantillons
  32. for (int i = 0; i < nEchantillons; i++) {
  33. echantillon[i] = 0;
  34. }
  35. u8g.begin();
  36. u8g.setContrast(contraste); // mettre le contraste qui a été lu en EEPROM
  37. dateDernierAffichage = - 52000; // forcer affichage niveau batterie après 8s
  38.  
  39. // créer la liste des locos non triée
  40. for (int i = 0; i < nb_max_enr; i++) {
  41. listeTriee[i] = i;
  42. }
  43. // Test si bouton gris appuyé au démarrage pour réglage numéro souris
  44. if (analogRead(btn_START) < 20) {
  45. type_affichage = 8;
  46. menu = 8;
  47. }

Le Serial.begin est supprimé pour gagner de la place en mémoire flash et RAM.
Il peut être remis en service à des fins de débogage en supprimant momentanément la police "u8g_font_helvB12" en mettant toutes les lignes qui l’utilisent en commentaire ce qui libère pas mal de mémoire.
Le NRF24L01 est utilisé de façon un peu particulière en lui attribuant le même canal pour l’émission et la réception. C’est ce qui permet d’utiliser 6 souris (avec une centrale de base sans l’extension radio).
Le setup configure l’interruption pour le codeur, les bits E/S, le module radio NRF24L01 et récupère toutes les infos sauvegardées en EEPROM : Le N° de canal de la souris, la dernière loco utilisée et le dernier contraste affiché.

La loop :

Désolé pour les explications un peu (voire très) succinctes de la partie la plus conséquente du programme mais vue la longueur (encore le monde des objets qui manque) je ne me vois pas commenter ces 748 lignes de code, et n’en faire qu’une petite partie ne vous apporterait pas grand chose. Je suis à votre disposition pour répondre à toutes vos demandes à la fin de cet article ou via le Forum.
En voici en tous cas les blocs principaux :
Ligne 636 à 673 : Boucle d’affichage selon la valeur de la variable "type_affichage".
Ligne 676 à 699 : On teste si une trame radio est détectée, suivi de l’envoi d’une éventuelle réponse.
Ligne 701 à 836 : Test et actions en fonction de la rotation du codeur incrémental. Les commentaires vous orienteront si besoin.
Ligne 837 à 863 : On n’envoie les commandes DCC que toutes les 500 ms.
Ligne 869 à 929 : Test des boutons STOP (rouge).
Ligne 931 à 974 : Test des boutons START (gris).
Ligne 977 à 1200 : Très long test du bouton du codeur avec actions correspondantes selon la page dans laquelle on se trouve.
Ligne 1204 à 1343 : Test du clavier alphanumérique et actions selon page.
Ligne 1345 à 1355 : Test de la tempo pour la saisie des différents caractères des touches du clavier.
Ligne 1357 à 1360 : Test de la tempo pour les touches de fonction.
Ligne 1362 à 1370 : Test de la tempo pour les touches de choix de loco.
Ligne 1373 à 1384 : Lecture du niveau batterie avec moyenne glissante, sujet très intéressant à voir ici: : http://arduino.blaisepascal.fr/index.php/2016/02/06/lisser-un-signal-analogique/

Les ligne 977 à 1343 représentent Le gros du programme avec une tripotée de "if" que j’ai essayé de remplacer par des "case" mais sans que cela ne fasse gagner de la mémoire, donc abandonné. C’est dans cette partie que tout se passe pour gérer l’appui des touches, afficher un "pseudo" curseur ( il n’existe pas de curseur dans la bibliothèque U8glib.h), limiter le nombre de caractères saisis, annuler ou valider une action, etc. L’orientation se fait principalement en fonction de la variable "menu" qui est définie par la validation d’une des lignes du menu.
La aussi les commentaires sont nombreux et peuvent éclairer votre route.

La fabrication de la souris

Voici les dimensions du boitier :

Dimensions boitier

Un des objectifs pour la fabrication de la souris était de mettre tous les composants sur un seul circuit imprimé avec le clavier, l’écran et le microcontrôleur sur picots enfichables pour en faciliter le remplacement en cas de panne. Cela impose 2 types de picots pour une question d’encombrement vertical et que vous retrouverez sur la liste des pièces.
La partie la plus difficile car la plus précise, sera la préparation du boitier avec toutes les découpes nécessaires pour accueillir cet unique circuit imprimé qui sera bloqué entre les 2 demis-coques du boitier sans aucune fixation. Il faudra suivre scrupuleusement le gabarit de découpe fourni.

Gabarit de découpe

Gabarit de perçage

Pour la fabrication proprement dite j’avais commencé par mettre toutes les photos et tous les commentaires directement dans cet article mais pour une plus grande lisibilité j’ai préféré tout intégrer dans le document PDF ci-dessous :

Montage mécanique souris

Le câblage du circuit imprimé

Il faut bien respecter les hauteurs des différents composants pour que le boitier se ferme sans forcer. Pour cela le microprocesseur PRO/MINI 3,3V ne peut pas être monté avec les picots mâles prévus pour le support bas profil.

Câblage PRO/MINI

Dans le document PDF ci-après vous trouverez l’explication pour son montage un peu particulier. Je voulais surtout éviter de le souder directement sur le CI pour faciliter son remplacement en cas de panne.

Mise en place des composants

Liste des pièces

J’ai mis la liste des pièces dans un fichier Excel qui facilite la récupération des liens correspondants aux pièces principales.

Liste des pièces

Le circuit imprimé est disponible dans ce zip au format Gerber et Excellon mais aussi au format Sprint-Layout6.0 pour lequel il existe un lecteur gratuit ici : https://www.electronic-software-shop.com/lng/en/support/free-viewer-software/
Pour ma part je travaille depuis pas mal de temps avec https://jlcpcb.com/ pour la qualité des circuits et leur prix, sauf pour les frais de port où il ne faut surtout pas choisir DHL sinon gare aux frais de douane. J’ai toujours utilisé l’option ePacket avec 0€ de frais de douane.

Circuit imprimé souris

Je peux également vous faire parvenir par courriel les fichiers au format bmp.
Vous pouvez aussi obtenir le circuit imprimé en qualité professionnelle en me contactant en MP (messagerie privée). Si le montage vous semble hors de portée vous pouvez me contacter de la même manière.

La Centrale

Nous abordons ici la partie la plus facile et la plus libre de l’article.
Comme je vous l’ai déjà précisé plus haut, cette centrale tel qu’elle est conçue, ne fonctionne qu’en DCC avec le logiciel DCCpp de Thierry dont vous trouverez un article ici : https://www.locoduino.org/spip.php?article228. Libre à vous de la modifier pour l’utiliser en analogique.
Vous trouverez une documentation complète de cette bibliothèque dans le dossier \libraries\DCCpp\extras\Doc\ et en cliquant sur index.html  .

Voici son schéma :
Schéma centrale

Elle est basée sur un Arduino MÉGA car il est impossible d’utiliser le bus SPI d’un UNO puisque le bit 12 (Miso) est utilisé pour le DIR du booster de la voie principale, voir ici : http://www.locoduino.org/spip.php?article187
Ce MÉGA gère une carte NRF24L01 pour l’émission/réception radio, une carte CAN pour l’ouvrir à toutes sortes d’extensions, une carte Bluetooth et commande les 2 boosters DCC.

Pour la voie principale j’ai fait le choix d’un module BTS7960B qui est capable de supporter 43A mais que le programme va bien sûr limiter à une valeur bien plus réaliste et que vous pourrez adapter à votre réseau, ceci grâce à ses sorties R_IS et L_IS envoyées sur l’entrée analogique A0 et qui donne une valeur proportionnelle au courant consommé.
Cette entrée n’est plus géré par le logiciel DCCpp (qui est le cœur du programme) mais par une routine personnelle qui me permet de traiter les courts-circuits d’une façon un peu particulière. A la détection d’un court-circuit, la centrale va se rallumer automatiquement 3 fois de suite avec un intervalle de 200ms (ajustable avec une constante en début de programme) afin d’absorber les petits courts-circuits qui peuvent se produire au passage de certaines aiguilles selon les locos ou wagons. Cette spécificité a trouvé un grand succès auprès des membres du club qui étaient obligés de remettre en marche très souvent leur ancienne centrale du fait de l’usage de machines parfois assez exotiques.

Pour la voie de programmation le booster est un produit beaucoup plus traditionnel puisqu’il s’agit du LMD18200 suivi d’un MAX471 pour la lecture du courant. Je précise, pour les débutants, que cette mesure de courant a énormément d’importance car c’est grâce à elle que la centrale peut lire les informations des CVs.
L’Arrêt d’urgence peut vous paraitre assez originale puisque je force le MÉGA à faire un reset. J’ai fait ce choix car il permet dans tous les cas, même après un "plantage" du programme de repartir avec les sorties DCC à 0.

Le programme

Sketch centrale

Attention ! Le cœur du programme est DCCpp comme déjà annoncé, mais j’ai du faire 2 modifications au niveau de la librairie que vous trouverez ici : https://github.com/Locoduino/DCCpp/blob/master/DCCpp.zip
La première dans config.h ligne 23 : MAX_MAIN_REGISTERS passe de 12 à 24
La seconde dans PacketRegister.cpp suppression affichage moniteur lignes 144 à 151, 199 à 206, 382 à 392, 490 à 500. Cette modification est uniquement une question de confort, elle supprime certains affichages qui ne me servaient pas.

En tête du programme vous trouverez quelques paramètres pour adapter le programme à votre usage.

  1. // Version = 1.00;
  2. #define Courant_Max 500 // courant maximum avant disjonction
  3. #define Boucle_Lec_Courant 2 // boucle de lecture courant
  4. #define tempo_CC 200 // nb de millisecondes avant rétablissement tension DCC
  5. #define YESICAN // à commenter si pas de module CAN installé

Courant_Max comme son nom l’indique est la valeur maximum autorisée avant de couper le DCC.
Boucle_Lec_Courant est le nombre de boucle que l’on autorise avec une valeur de courant dépassée avant de couper le DCC.
tempo_CC est le temps avant rétablissement automatique de la tension DCC (3 fois)
Il faudra jongler avec ces 3 paramètres pour adapter la gestion des courts-circuits à votre réseau.

Les #include :
  1. #include "DCCpp.h"
  2.  
  3. #ifndef USE_TEXTCOMMAND
  4. #error To be able to compile this sample,the line #define USE_TEXTCOMMAND must be uncommented in DCCpp.h
  5. #endif
  6.  
  7. #include <SPI.h>
  8. #include <nRF24L01.h>
  9. #include <RF24.h>
  10.  
  11. #ifdef YESICAN
  12. #include <mcp_can.h>
  13. #endif

Où trouver les librairies :
Pour la carte CAN : https://github.com/Locoduino/CAN_BUS_Shield
Pour le NRF24L01 : https://github.com/nRF24/RF24

Les constantes :
  1. const unsigned int ad_reception_carte1= 0x100;
  2. const unsigned int ad_reception_carte2= 0x101;
  3. const unsigned int ad_emission_carte1 = 0x200;
  4. const unsigned int ad_emission_carte2 = 0x300;
  5. const unsigned int nb_char_recu = 1; // nombre de caractères à reçevoir
  6. const unsigned int nb_char_emis = 1; // nombre de caractères à emettre
  7. boolean _dumpCan = true; // mode débogage

Ce sont les constantes utilisées pour les échanges avec le bus CAN. Il faudra bien sûr les adapter à votre "philosophie" personnelle.

  1. const uint64_t pipes[] = {0xB3B4B5B601LL, 0xB3B4B5B612LL, 0xB3B4B5B623LL, 0xB3B4B5B634LL, 0xB3B4B5B645LL, 0xB3B4B5B656LL};

Contrairement à la souris, la centrale n’autorise que 6 canaux radio différents. Si l’on désire augmenter le nombre de canaux il faudra ajouter une carte optionnelle qui va gérer les 4 canaux supplémentaires. Description et CI sur simple demande.

  1. // locos
  2. #define nb_max_locos 12 // Nb de locos max mises en mémoire

Cette valeur ne doit pas être modifiée car elle est liée à la valeur MAX_MAIN_REGISTERS de DCCpp. Nous utilisons 12 registres pour la mise en mémoire des vitesses de 12 locos et 12 registres pour la mise en mémoire des fonctions de ces 12 locos. Alors pourquoi MAX_MAIN_REGISTERS = 24 ce qui ferait 25 registres ? Tout simplement parce que le registre 0 ne peut pas être utilisé de la même façon. Je vous reparlerai de ce sujet (sensible) plus loin.

Le setup :
  1. void setup()
  2. {
  3. Serial.begin(9600);
  4. Serial1.begin(9600);
  5. Serial.println("OK pour connecter CAN!");
  6.  
  7. START_INIT:
  8. if (CAN_OK == CAN.begin(CAN_250KBPS)) { // init can bus : baudrate = 250k
  9. if (_dumpCan) {
  10. Serial.println("CAN BUS Shield init ok!");
  11. }
  12. }
  13. else {
  14. if (_dumpCan) {
  15. Serial.println("CAN BUS Shield init fail");
  16. }
  17. delay(100);
  18. goto START_INIT;
  19. }
  20.  
  21. /*
  22.   configurer mask & filter du bus CAN
  23.   */
  24. CAN.init_Mask(0, 0, 0b11111111111); // Il y a 2 masques à initialiser dans le mcp2111
  25. CAN.init_Mask(1, 0, 0b11111111111); // on teste tous les bits
  26.  
  27. CAN.init_Filt(0, 0, ad_reception_carte1); // Réception possible : que 2 adresses
  28. CAN.init_Filt(1, 0, ad_reception_carte1); // idem
  29. CAN.init_Filt(2, 0, ad_reception_carte1); // idem
  30. CAN.init_Filt(3, 0, ad_reception_carte2); // idem
  31. CAN.init_Filt(4, 0, ad_reception_carte2); // idem
  32. CAN.init_Filt(5, 0, ad_reception_carte2); // Idem
  33.  
  34. // configurer l'interruption
  35. attachInterrupt(2, MCP2515_ISR, FALLING); // interrupt 2 (pin 21)

Si vous n’installez pas la carte CAN, il faudra commenter la ligne (5 dans le programme) pour éviter de boucler sur la configuration du module CAN.

  1. // lancement radio
  2. radio.begin();
  3. radio.setPALevel(RF24_PA_MAX); //RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH and RF24_PA_MAX
  4. radio.setChannel(108); //canal 108 au dessus des canaux WiFi (de 0 à 125)
  5. radio.setDataRate( RF24_250KBPS ) ;
  6.  
  7. // ouvrir 6 canaux de réception radio
  8. radio.openReadingPipe(0, pipes[0]);
  9. radio.openReadingPipe(1, pipes[1]);
  10. radio.openReadingPipe(2, pipes[2]);
  11. radio.openReadingPipe(3, pipes[3]);
  12. radio.openReadingPipe(4, pipes[4]);
  13. radio.openReadingPipe(5, pipes[5]);
  14.  
  15. radio.startListening(); // passer en réception

On configure les 6 canaux radio

  1. // Lancement DCC
  2. DCCpp::begin();
  3. DCCpp::beginMain(UNDEFINED_PIN, DCC_SIGNAL_PIN_MAIN, 3, UNDEFINED_PIN ); //en mettant UNDEFINED_PIN à la place de A0,plus de test de courant par DCC, il y a une routine spécifique dans le loop
  4. DCCpp::beginProg(UNDEFINED_PIN, DCC_SIGNAL_PIN_PROG, 11, A1);
  5. Serial.println("SerialDcc OK");

On lance DCCpp en supprimant le test de courant par A0 avec le UNDEFINED_PIN puisque nous voulons utiliser notre propre routine de lecture de courant.

  1. // mise à 0 des tableaux de fonctions et adresses locos
  2. for (int nb_loc = 0; nb_loc < nb_max_locos; nb_loc++) {
  3. adr_loc[nb_loc] = 0;
  4. tab_fonc_B0_4[nb_loc] = 128;
  5. tab_fonc_B5_8[nb_loc] = 176;
  6. tab_fonc_B9_12[nb_loc] = 160;
  7. tab_fonc_B0_43_2[nb_loc] = 0;
  8. tab_fonc_B0_28_2[nb_loc] = 0;
  9. }

Nous initialisons 1 tableau pour les adresses des 12 locos et 5 tableaux pour la sauvegarde des fonctions de ces 12 machines.
Peut-être l’avez-vous remarqué si vous utilisez déjà DCCpp, mais en cas de mauvais contacts sur les rails ou de coupure de la tension DCC, les fonctions de toutes les locos sont remises à 0 ce qui est très gênant surtout si l’on fait circuler plusieurs convois en même temps.

  1. // envoyer l'info tension coupée sur les souris
  2. //start_DCC(); // si demande de tension DCC à l'allumage, dé-commenter cette ligne
  3. envoi_etat();
  4. }

Dans l’état actuel du programme, à l’allumage de la centrale, la tension DCC est à 0. Si vous préférez avoir du DCC à l’allumage il suffit de dé-commenter la ligne "start_DCC() ;".

La loop :

La ligne 283 lance le rafraîchissement des routines DCCpp
De 288 à 389 on teste la réception radio qui appelle quelques commentaires.

  1. if (radio.available(&pipeNum)) { // tester si une trame est arrivée,
  2. char c_cv[2];
  3. radio.read(&dcc_receive, sizeof(dcc_receive)); // sizeof(dcc_receive) est toujours à 8
  4. if(dcc_receive[0] == '<') { // ne traiter que si trame commence par <
  5. if(dcc_receive[1] == 1) { // "1" = trame allumer DCC
  6. start_DCC(); // utiliser de ce programme pour allumer DCC
  7. cpt_cc = 0; // permettre à nouveau 3 CC de 0,5s
  8. old_etat = 10; // pour forcer l'envoi de l'état
  9. delay(100); // laisser le temps à la souris de passer en réception
  10. }
  11. else if(dcc_receive[1] == 0) { // "0" = trame couper DCC
  12. stop_DCC(); // utiliser de ce programme pour couper DCC
  13. old_etat = 10; // pour forcer l'envoi de l'état
  14. delay(100); // laisser le temps à la souris de passer en réception
  15. }
  16. else {
  17. if(dcc_receive[1] == 't') { // 't' = trame vitesse
  18. adr_loco_H = dcc_receive[2];
  19. adr_loco_L = dcc_receive[3];
  20. adresse_loco = restaure_adr(); // restaure l'adresse complète à partir de HI et LOW
  21. crans = dcc_receive[4]; // non utilisé pour l'instant
  22. sens = bitRead(dcc_receive[5], 7); // le sens est déterminé par le bit 7 de vitesse
  23. vitesse = dcc_receive[5] & 0b01111111 ; // mettre bit 7 à 0
  24. enregistre_loc(); // enregistre la nouvelle loco ou récupère son ID si existe
  25. // ici on change de registre de vitesse selon loco, ne pas utiliser registre 0 donc +1
  26. DCCpp::setSpeedMain(ordre_loc + 1, adresse_loco, 128, vitesse, sens);
  27. }
  28. else if(dcc_receive[1] == 'W') { // "W" = trame écriture CV sur voie de programmation
  29. envoi[pipeNum] = true; // pour forcer un retour du résultat
  30. adr_cv[pipeNum] = dcc_receive[2];
  31. // assembler les 2 bytes pour refaire valeur INT
  32. adr_loco_H = dcc_receive[3];
  33. adr_loco_L = dcc_receive[4];
  34. don_cv = restaure_adr(); // restaure valeur complète à partir de HI et LOW
  35. if(adr_cv[pipeNum] == 1 and don_cv < 128) {
  36. // c'est une adresse courte pour cv1
  37. DCCpp::writeCvProg(adr_cv[pipeNum], don_cv, 100, 200); // écriture de CV1
  38. // mettre bit 5 de CV29 à 0 pour adresse courte
  39. byte cv_29 = DCCpp::readCvProg(29, 100, 200);
  40. bitWrite(cv_29, 5, 0);
  41. DCCpp::writeCvProg(29, cv_29, 100, 200);
  42. }
  43. else if(adr_cv[pipeNum] == 1) {
  44. // c'est une adresse longue pour cv1, donc on calcul les valeurs de CV17 et CV18
  45. int deb=0;
  46. int fin=0;
  47. int res=0;
  48. int cv17=192; // laisser cv17 à 192 pour calcul
  49. int cvres=0;
  50. for (int i = 0;i <= 9984;i = i + 256) {
  51. deb = i;
  52. fin= i + 255;
  53. if ((don_cv >= deb)&&(don_cv <= fin)){
  54. res = don_cv - i;
  55. cvres = cv17;
  56. }
  57. cv17 = cv17 + 1;
  58. }
  59. // mettre bit 5 de CV29 à 1 pour adresse longue
  60. byte cv_29 = DCCpp::readCvProg(29, 100, 200);
  61. bitWrite(cv_29, 5, 1);
  62. DCCpp::writeCvProg(29, cv_29, 100, 200);
  63. DCCpp::writeCvProg(17, cvres, 100, 200); // écriture de CV17
  64. DCCpp::writeCvProg(18, res, 100, 200); // écriture de CV18
  65. }
  66. else { // pour tous les autres cv, envoyer trame
  67. DCCpp::writeCvProg(adr_cv[pipeNum], don_cv, 100, 200);
  68. }
  69. }
  70. else if(dcc_receive[1] == 'R') { // "R" = trame lecture CV
  71. adr_cv[pipeNum] = dcc_receive[2];
  72. envoi[pipeNum] = true; // la lecture se fait dans test envoi[pipeNum]
  73. }
  74. else if(dcc_receive[1] == 'f') { // "f" = trame fonctions locos
  75. adr_loco_H = dcc_receive[2];
  76. adr_loco_L = dcc_receive[3];
  77. adresse_loco = restaure_adr(); // restaure l'adresse complète à partir de HI et LOW
  78. etat_fct = bitRead(dcc_receive[4], 7); // le sens est déterminé par le bit 7 de vitesse
  79. num_fct = dcc_receive[4] & 0b01111111 ; // mettre bit 7 à 0
  80. if(num_fct < 29) { // n'envoyer sur loco que les 28 premières fonctions
  81. enregistre_loc(); // enregistre la nouvelle loco ou récupère son ID si existe
  82. // envoyer la fonction
  83. if (etat_fct) {
  84. gLocoFunctions[ordre_loc].activate(num_fct);
  85. } else {
  86. gLocoFunctions[ordre_loc].inactivate(num_fct);
  87. }
  88. // MAJ registre loco
  89. DCCpp::setFunctionsMain(ordre_loc + 13, adresse_loco, gLocoFunctions[ordre_loc]);
  90. }
  91. else { // les fonctions > 28 sont envoyées sur bus CAN avec bit 7 en l'état
  92. ordres_carte1 = dcc_receive[4];
  93. }
  94. }
  95. else if(dcc_receive[1] == 119) { // "w" = trame programmation POM
  96. adr_cv[pipeNum] = dcc_receive[2];
  97. don_cv = dcc_receive[3];
  98. DCCpp::writeCvMain(adr_cv[pipeNum], don_cv, 100, 200);
  99. }
  100. }
  101. }
  102. }

Tout d’abord on n’accepte que des trames délimitées par < et >

Les trames <0> et <1> coupent ou mettent en route la tension DCC.

Les trames commençants par "t" sont les trames de réglage de vitesse que l’on va sauvegarder dans les registres 1 à 12. Pour ce faire on va enregistrer l’adresse de chaque nouvelle loco mise sur les rails (dont on reçoit une commande) dans un tableau dont le pointeur va s’incrémenter de 0 à 11, c’est le tableau adr_loc[]. Si on dépasse les 12 locos sans avoir initialisé la centrale les 6 tableaux (adresses et fonctions) sont remis à 0.
Puis on utilise ce pointeur (ordre_loc) pour enregistrer l’adresse, les crans, la vitesse et le sens dans les registres 1 à 12 :

  1. DCCpp::setSpeedMain(ordre_loc + 1, adresse_loco, 128, vitesse, sens);

Les trames commençants par "W" sont les trames d’écriture de CV sur la voie de programmation. On oriente le programme différemment s’il s’agit d’une adresse longue (supérieure à 127) ou courte. Pour le CV1 on réécrit le CV29 puis les CV17 et 18 selon les normes NMRA et on positionne un drapeau (envoi[]) pour signaler qu’un retour d’information est à envoyer à la souris :

  1. envoi[pipeNum] = true; // pour forcer un retour du résultat
  2. DCCpp::writeCvProg(29, cv_29, 100, 200);
  3. DCCpp::writeCvProg(17, cvres, 100, 200); // écriture de CV17
  4. DCCpp::writeCvProg(18, res, 100, 200); // écriture de CV18

Pour les autres CVs on positionne le même drapeau et on écrit la valeur demandée :

  1. envoi[pipeNum] = true; // pour forcer un retour du résultat
  2. DCCpp::writeCvProg(adr_cv[pipeNum], don_cv, 100, 200);

Les trames commençants par "w" sont les trames d’écriture de CV sur la voie principale en mode "POM". Cette programmation ne permet pas de retour d’information :

  1. DCCpp::writeCvMain(adr_cv[pipeNum], don_cv, 100, 200);

Les trames commençants par "R" sont les trames de lecture de CV sur la voie de programmation. Cela se fait ici en positionnant aussi le drapeau de retour :

  1. adr_cv[pipeNum] = dcc_receive[2];
  2. envoi[pipeNum] = true; // la lecture se fait dans test envoi[pipeNum]

Les trames commençants par "f" sont les trames de fonctions que l’on va sauvegarder dans l’un des 5 registres.
Si ce sont des fonctions standards de 0 à 28, on les envoie à DCCpp par :

  1. DCCpp::setFunctionsMain(ordre_loc + 13, adresse_loco, gLocoFunctions[ordre_loc]);

Si la fonction est supérieure à 28 on va l’envoyer sur le bus CAN, information qui sera récupérée par l’une des cartes connectée sur le bus et qui fera l’action que vous aurez programmé ; cela peut être une animation quelconque, un son (parmi plusieurs) fourni par une carte que je suis entrain de développer, une commande d’aiguille etc…

Retour d’information

  1. if (envoi[pipeNum] and etat) { // récupérer la donnée à lire à l'adresse adr_cv et l'envoyer par radio mais que si loco sous tension (etat = true)
  2. char text[7];
  3. if (adr_cv[pipeNum] == 1) { // si on lit le CV1, tester si adresse longue ou non
  4. int CV_29 = DCCpp::readCvProg(29, 101, 101);
  5. int CV_17 = DCCpp::readCvProg(17, 101, 101);
  6. int CV_18 = DCCpp::readCvProg(18, 101, 101);
  7. if (bitRead(CV_29, 5) == 1) { // on est en adresse longue
  8. sav_don_cv[pipeNum] = ((CV_17 - 192) * 256 ) + CV_18; // reconstruire adresse longue
  9. sprintf(text, "R%d", sav_don_cv[pipeNum]); // on renvoi l'adresse longue lue
  10. }
  11. else {
  12. int monINT = DCCpp::readCvProg(adr_cv[pipeNum], 101, 101); // lecture CV1 (adr courte)
  13. sprintf(text, "R%d", monINT); // réponse bonne ou fausse
  14. }
  15. }
  16. else { // si on lit les autres CV, retourner simplement sa lecture
  17. int monINT = DCCpp::readCvProg(adr_cv[pipeNum], 101, 101); // lecture du CV demandé
  18. sprintf(text, "R%d", monINT); // réponse bonne ou fausse
  19. }
  20. radio.stopListening(); // passer en émission
  21. radio.openWritingPipe(pipes[pipeNum]); // utiliser le canal de celui qui a envoyé la demande
  22. radio.write(&text, sizeof(text));
  23. delay(5);
  24. radio.startListening(); // passer en réception
  25. envoi[pipeNum] = false;
  26. }

Si le drapeau relatif à la loco N° [pipeNum] est à true on renvoie la lecture du ou des CVs vers la souris.

Gestion du Bluetooth
  1. while (Serial1.available()>0) { // Tant que données série arrivent
  2. char c = Serial1.read();
  3. if (c == '<') { // début de la trame
  4. cmdBlue[0] = 0;
  5. }
  6. else if (c == '>') { // fin de la trame
  7. TextCommand::parse(cmdBlue); // on envoie traiter la trame
  8. }
  9. else if (strlen(cmdBlue) < MAX_COMMAND_LENGTH) { // s'il reste de la place dans le tableau
  10. sprintf(cmdBlue, "%s%c", cmdBlue, c); // continuer jusqu'au >
  11. }

Le module Bluetooth est relié sur l’entrée Serial1 du MÉGA. Si une information arrive par ce biais on l’envoie directement vers DCCpp.
Pour l’instant je ne traite pas la mise en mémoire des informations venues par le Bluetooth mais il est tout à fait possible de les gérer de la même manière que les informations venants de la souris. Avis aux amateurs.

Le reste du programme me semble suffisamment commenté pour ne pas rallonger inutilement cet article. N’hésitez pas à me contacter à la fin de cet article ou sur le forum si vous avez des questions.

La fabrication de la centrale

Pour la fabrication de la centrale toutes les possibilités existent selon le boitier, le budget, la présentation que vous souhaitez etc…

Pour câbler les différents composants vous pouvez utiliser un Protoshield MÉGA sur lequel vous implantez le module NRF24L01, le module Bluetooth, le transistor inverseur, éventuellement la carte CAN ainsi que tous les connecteurs nécessaires pour relier le 0 et 5V, les boosters, les entrées lecture courant, le bouton STOP et la sortie CAN.
Les boosters et le MAX471 sont montés séparément.

Pour ma part j’ai fait faire un circuit imprimé qui regroupe tous ces éléments ce qui facilite énormément le montage. Ce circuit est disponible de la même manière que celui de la souris.
JPEG - 256.2 ko

J’ai facilement intégré tous les éléments dans ce boitier dont le lien est dans la liste des pièces et qui fait 180x135x55mm :
Boitier centrale

Option
Comme je vous l’ai signalé plus haut, il est possible d’étendre le nombre de souris avec un module additionnel.
Ce module comporte un Arduino NANO, une carte NRF24L01 et un module CAN et voici son circuit imprimé (également disponible en fichiers ou physiquement) :
Module 4 canaux radio

Je vous laisse approfondir cet article et me poser toutes les questions que vous jugerez utiles ; je suis également ouvert à toutes les améliorations ou suggestions qui feraient évoluer ce produit.

10 Messages

Réagissez à « Souris et centrale sans fil  »

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

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

Realisation d’un va-et-vient automatique et réaliste

Souris et centrale sans fil

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

Souris et centrale sans fil


tony04

Realisation d’un va-et-vient automatique et réaliste


Dominique

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


Christian

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


Jean-Luc

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


Christian

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

Les articles les plus lus

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

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

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

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

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

Feux tricolores

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

Réalisation d’un affichage de gare ARRIVEE DEPART

La rétro-signalisation sur Arduino

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