Bibliothèque DcDccNanoController

Une petite centrale toute prête…

. Par : Thierry. URL : http://www.locoduino.org/spip.php?article224

A travers de nombreux articles sur le site, il est possible de se construire ou de se concevoir une centrale DCC   en s’inspirant, voire en copiant généreusement les articles existants proposés par mes confrères et néanmoins amis, et en modifiant tout aussi généreusement les sources qu’ils fournissent. L’idée de cette bibliothèque DcDccNanoController (DDc pour les intimes (le ’c’ est en minuscule pour ne pas faire d’ombre à DDC, l’autre centrale pas nano qui viendra plus tard) ) est un peu différente et propose une centrale quasiment clé en main. Vous fournissez le matériel, vous le gérez dans votre croquis, et DDc vous fourni tout le reste : une interface utilisateur et une centrale DC et DCC   !

Depuis longtemps, je fabrique des briques logicielles destinées à une construction plus ambitieuse. Sur ce site, je fournis des bibliothèques prêtes à l’emploi pour faciliter la vie d’éventuels utilisateurs qui n’ont plus qu’à coder un minimum pour s’en servir. C’est l’idée qui a vu naître UAD, remplacée depuis par Commanders (pour gérer les commandes : boutons, potars, encodeurs, bus CAN, SPI   ou I2C, etc…) et Accessories (pour piloter les accessoires en tout genre). C’est ensuite EEPROMextent qui permet de sauvegarder de manière structurée et fiable des données dans l’EEPROM en optimisant son usure. Et enfin LcdUi qui permet de construire une interface utilisateur sur un écran LCD sans réinventer la poudre à chaque fois.
La bibliothèque que je vous propose aujourd’hui est un peu la conclusion de cette démarche, la première maison (plutôt une maisonnette ici…) utilisant ces fameuses briques. DcDccNanoController est un nom un peu long, mais qui tente de tout dire en un seul mot : c’est une centrale de pilotage de trains (Controller), apte à l’analogique (DC) comme au numérique (DCC  ), et basée sur un Arduino Nano. Un Arduino Uno peut aussi faire l’affaire, leurs caractéristiques étant très proches à deux broches près. Le moteur DCC   utilisé est bien entendu notre chouchou du moment DCC++, remanié pour les besoins de la cause…
Sur de petits processeurs comme l’Atmel ATMEGA328 qui équipe les Nano et Uno, la mémoire vive SRAM est réduite à 2K, soit 2048 octets. C’est très peu, et cela ne m’a pas permis de faire tout ce que je voulais. Il y a deux conséquences : la centrale présentée aujourd’hui sera simple, et une nouvelle bibliothèque destinée à des modèles plus puissants d’Arduino ou équivalents, comme le Mega, le Due ou plus exotiques comme le STM32 ou les puces Wifi ESP8266 ou ESP32 sera présentée plus tard.

C’est quoi que c’est-y donc ?

DcDccNanoController est donc une centrale DC/analogique permettant de fixer la fréquence PWM   de l’alimentation, et bien sûr de piloter une machine dans les deux sens. Pendant le pilotage, une fonction vitesse lente permet de limiter la vitesse à la moitié, et donc de manœuvrer plus précisément.
Du côté DCC/numérique, elle ne peut piloter qu’une seule machine à la fois dont on peut fixer l’adresse, le nombre de pas (14/28/128) et l’adresse de deux fonctions. Elle permet aussi de lire ou d’écrire les Cvs du décodeur. C’est donc plutôt une centrale de locodrome, ou de réglage de décodeur type Sprog.

DCC++ ?

Cela a déjà été dit dans d’autres articles et sur le forum, mais DCC++ est une petite merveille, bien écrite, efficace et performante. Et c’est effectivement ce que j’ai utilisé dans cette centrale. Mais comme la mémoire d’un petit Arduino est réduite, et que d’autre part les possibilités de DCC++ sont naturellement limitées d’origine pour deux shields   moteur bien connus (l’Arduino Motor shield V3 et Pololu MC33926 Motor shield), je l’ai à la fois simplifiée et paramétrée afin que l’on puisse l’utiliser avec d’autres fournisseurs de puissance sans taper dans son code. Les deux shields   historiques restent bien sûr utilisables…

Simplifiée comment ?

En gros, ne subsiste que le strict nécessaire pour une centrale pilotée par de vrais boutons physiques. Cela veut dire pas d’interface série pour envoyer des ordres, pas d’interface réseau, pas d’objets exotiques comme des aiguillages, des sorties, du stockage EEPROM des CVs… Juste la partie DCC ! Et encore, une seule loco pilotable, une seule voie pilotée en DCC qui fait office de voie principale et de voie de programmation.

Paramétrée comment ?

Le paramétrage de DCC++ dans sa version originale passe par la modification d’un fichier include pour déclarer les différents paramètres, type de shield  , type d’interface réseau, etc… Une fois réagencée, la nouvelle interface permet de décider dans le setup() du croquis avec le begin() de la bibliothèque quelles sont les broches utilisées par DCC++. Les modifications de l’include ne sont plus nécessaires.

Besoin de rien, envie de … quoi ?

Pour fonctionner, outre DCC++ intégrée aux sources de la bibliothèque DcDccNanoController, la bibliothèque LcdUi doit être installée. Et si comme dans mon exemple vous voulez utiliser la bibliothèque Commanders pour gérer les boutons, elle devra aussi être installée, ainsi que DIO2, livrée dans les extras de Commanders et nécessaire à son fonctionnement.

DcDccNanoController

La bibliothèque masque la tripaille : DCC++ bien sûr, mais aussi LcdUi utilisée pour l’interface utilisateur… Ne reste que l’objet principal DcDccController qui fait tourner tout ce petit monde.
PNG - 15.6 ko
Sur la figure 1, les ovales noirs représentent les objets physiques : le boitier avec ses boutons, l’écran et la voie à piloter. Les rectangles bleus sont du code. A l’extérieur de la bibliothèque se trouve le croquis qui reçoit les informations du matériel grâce à Commanders qui transforme un appui sur un bouton par un événement DDc compréhensible par la bibliothèque. A l’intérieur de celle ci, le noyau DcDccController reçoit les événements (bouge vers le haut ou le bas, select ou échap…) et les envoie à LcdUi. Dans LcdUi, la fenêtre en cours à ce moment là reçoit l’événement et fait ce qui doit être fait (affichage sur l’écran, sortie en disant Ok ou échap…). La sortie de LcdUi est traitée dans le noyau de la bibliothèque et éventuellement traduite en ordres pour DCC++ : va plus ou moins vite, allume ou éteint les feux… A son tour DCC++ traduit ces instructions en paquets DCC envoyés sur la voie.
Du côté utilisateur de la bibliothèque, il ’suffit’ de brancher les fils, et de coder le croquis. Facile !

L’exemple

L’exemple "demo" fourni avec la bibliothèque est simplement le code de ma propre centrale branchée à demeure sur un locodrome sans prétention. Le code est donc fonctionnel et utilisé ! Et il ne nécessite qu’une trentaine de lignes utiles destinées à décrire le matériel utilisé… L’exemple a bien sûr fait le choix d’utiliser Commanders pour ses boutons, et LiquidCrystal pour l’écran LCD piloté par LcdUi.

PNG - 1.2 Mo

Le code

  1. /*************************************************************
  2. project: <Dc/Dcc Controller>
  3. author: <Thierry PARIS>
  4. description: <Dc/Dcc Nano controller sample>
  5. *************************************************************/
  6.  
  7. #include "French16.h"
  8. #include "Commanders.h"
  9.  
  10. #include "DcDccNanoController.h"
  11.  
  12. #include <LiquidCrystal.h>
  13. #include "ScreenLiquid.hpp" // Fichier include de DcDccNanoController pour les ecrans pilotes par LiquidCrystal...
  14.  
  15. LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
  16. ScreenLiquid screen;
  17.  
  18. ButtonsCommanderPush buttonSelect;
  19. ButtonsCommanderEncoder buttonEncoder;
  20. ButtonsCommanderPush buttonCancel;
  21. ButtonsCommanderPush buttonEmergency;
  22. ButtonsCommanderSwitchOnePin buttonF0;
  23. ButtonsCommanderSwitchOnePin buttonF1;
  24.  
  25. void setup()
  26. {
  27. Serial.begin(115200);
  28.  
  29. buttonSelect.begin(EVENT_SELECT, A0);
  30. buttonF0.begin(EVENT_FUNCTION0, A1);
  31. buttonF1.begin(EVENT_FUNCTION1, A2);
  32. buttonEmergency.begin(EVENT_EMERGENCY, A3);
  33. buttonCancel.begin(EVENT_CANCEL, A4);
  34. buttonEncoder.begin(EVENT_ENCODER, 12, 8, 2);
  35.  
  36. screen.begin(16, 2, DcDccStringTable, &lcd);
  37.  
  38. // Si le premier argument est à 255, seul le DCC est disponible.
  39. // S'il est à 0, alors seul le Dc est disponible.
  40. // Sinon l'etat de la broche fixera le mode.
  41. DcDccController::begin(A5, &screen);
  42. DcDccController::beginMain(255, DCC_SIGNAL_PIN_MAIN, 11, A6); // Dir, Pwm, broche capteur courant
  43. }
  44.  
  45. void loop()
  46. {
  47. unsigned long eventId = Commanders::loop();
  48.  
  49. // Pour LcdUi, UNDEFINED_ID de Commanders ne veut rien dire. Alors il faut le convertir en EVENT_NONE.
  50. if (eventId == UNDEFINED_ID)
  51. eventId = EVENT_NONE;
  52.  
  53. DcDccController::loop(eventId, Commanders::GetLastEventData());
  54. }

Voilà c’est tout ! Trente deux lignes utiles pour une centrale DCC ! Détaillons un peu :

  1. #include "French16.h"
  2. #include "Commanders.h"
  3.  
  4. #include "DcDccNanoController.h"
  5.  
  6. #include <LiquidCrystal.h>
  7. #include "ScreenLiquid.hpp" // DcDccNaNoControler include file associated with LiquidCrystal...

Première ligne utile, première question… C’est quoi ce French16.h ?
Comme je l’avais écrit dans l’article sur LcdUi, une interface utilisateur c’est d’abord beaucoup de texte. C’est ce que donne French16.h qui comme son nom l’indique concerne une ergonomie en français sur un écran 16 caractères. Dans la bibliothèque DcDccNanoController se trouve aussi English16.h pour ceux que cela intéresse… Il y a bien sûr moyen pour celui qui crée le croquis de remplacer ce fichier par le sien, si et seulement si il respecte les règles en ne changeant que le texte entre guillemets, et en respectant la longueur de ces textes limitée selon l’écran utilisé…
Viennent ensuite des includes plus connus : Commanders pour les boutons, DcDccController lui même, puis LiquidCrystal (livrée avec l’IDE) pour l’écran Lcd. ScreenLiquid, c’est la version LcdUi d’un écran déclaré par LiquidCrystal. Si vous utilisez NewLiquidCrystal (livrée avec LcdUi dans le répertoire extras), vous devrez inclure NewLiquidCrystal.hpp, avec ScreenLiquidNew.hpp pour son équivalent LcdUi .

  1. LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
  2. ScreenLiquid screen;
  3.  
  4. ButtonsCommanderPush buttonSelect;
  5. ButtonsCommanderEncoder buttonEncoder;
  6. ButtonsCommanderPush buttonCancel;
  7. ButtonsCommanderPush buttonEmergency;
  8. ButtonsCommanderSwitchOnePin buttonF0;
  9. ButtonsCommanderSwitchOnePin buttonF1;

Puis arrivent les déclarations : l’écran LiquidCrystal lcd avec les broches utilisées, son pendant LcdUi screen, puis les différents boutons présents sur le boitier. J’ai préféré utiliser des interrupteurs plutôt que des poussoirs pour les fonctions parce qu’en mode analogique ils ont un autre rôle. C’est aussi un moyen de laisser une fonction activée entre deux allumages de la centrale, sans devoir stocker l’état dans l’EEPROM…

  1. void setup()
  2. {
  3. Serial.begin(115200);
  4.  
  5. buttonSelect.begin(EVENT_SELECT, A0);
  6. buttonEncoder.begin(EVENT_ENCODER, 12, 8, 2);
  7. buttonCancel.begin(EVENT_CANCEL, A3);
  8. buttonEmergency.begin(EVENT_EMERGENCY, A4);
  9. buttonF0.begin(EVENT_FUNCTION0, A1);
  10. buttonF1.begin(EVENT_FUNCTION1, A2);
  11.  
  12. screen.begin(16, 2, DcDccStringTable, &lcd);
  13.  
  14. // Si le premier argument est à 255, seul le DCC est disponible.
  15. // S'il est à 0, alors seul le DC est disponible.
  16. // Sinon l'etat de la broche fixera le mode.
  17. DcDccController::begin(A5, &screen);
  18. DcDccController::beginMain(255, DCC_SIGNAL_PIN_MAIN, 11, A6); // Dir, Pwm, broche capteur courant
  19. }

Voilà la plus grosse partie : le setup. Comme d’habitude, il ne faut pas hésiter à lire et relire cette partie pour être sûr de ce qui s’y trouve. C’est là que la plupart des problèmes se jouent en général.
Passons sur le Serial.begin() classique pour arriver aux begin() des différents boutons.
Chaque bouton définit ce qui sera son identifiant lorsqu’il sera utilisé. J’ai directement utilisé les différents événements DDc attendus comme identifiants ! Ces événements sont pour la plupart aussi ceux de LcdUi : EVENT_SELECT, EVENT_CANCEL, plus ceux particuliers de DDc comme EVENT_FUNCTION0. Le but est de ne pas avoir de transcription à faire entre l’identifiant du bouton et le message à transmettre à la bibliothèque. Les arguments supplémentaires des begin() concernent les broches utilisées par chaque bouton.
Ensuite, le begin de l’écran LcdUi définit sa taille, la liste des textes utilisés fournie par l’include French16.h, et l’écran LiquidCrystal associé. En réalité, French16.h ne fournit que les chaînes individuelles, c’est DcDccNanoController.h qui les rassemble dans une liste nommée DcDccStringTable utilisée ici en argument.
Une fois l’écran initialisé, ne reste plus que DcDccController lui même.
Le begin donne l’adresse d’une broche en premier argument. Selon l’état de cette broche, la centrale sera en mode DC/analogique ou DCC/numérique. Le mieux est de raccorder un interrupteur sur cette broche ! Si l’on veut piloter uniquement en analogique, il suffit de mettre 0 dans cette valeur. Avec 255, c’est le mode numérique qui est forcé. Toute autre valeur sera utilisée comme numéro de broche. Le second argument donne l’écran LcdUi initialisé à DDc.
Le beginMain définit les broches utilisées en analogique et/ou numérique. Lorsque DCC++ est utilisé avec un shield   moteur, la broche PWM   de l’Arduino est physiquement renvoyée sur la broche de pilotage du shield. C’est le premier argument, inutile dans mon cas puisque j’utilise un circuit spécialisé, le LMD18200, qui n’a pas besoin de renvoi… C’est pourquoi j’ai 255 pour cette valeur. Le deuxième argument est le numéro de broche associé à l’interruption utilisée par DCC++ et elle est dépendante du modèle d’Arduino utilisé. C’est donc une constante DCC_SIGNAL_PIN_MAIN définie par DCC++ lui même que j’utilise pour la broche de direction. Le PWM est donné par la broche 11, et enfin le niveau de courant consommé est accessible par la broche A6, c’est là qu’est branché le circuit MAX471 qui va mesurer le courant consommé et ainsi lire les valeurs de CV renvoyées par le décodeur.
Si la fonction s’appelle beginMain, c’est que DCC++ permet de piloter une voie principale et une voie de programmation simultanément. Mais dans le cas du Nano Controler, seule la voie principale est utilisée.

Bon, on a fait le plus dur, c’est à dire fixer les rôles et les branchements de tous les intervenants. Reste la fonction loop qui est extrêmement simple puisque l’on va récupérer les événements générés par les boutons grâce à Commanders, et en envoyer l’identifiant directement à DcDccController. Au passage on aura pris soin de transformer l’absence d’événement de Commanders (UNDEFINED_ID), en absence d’événement pour DDc (EVENT_NONE)… Parce que même s’il n’y a pas d’événements en cours et qu’aucun bouton n’a été utilisé il faut quand même donner la main à DcDccController pour qu’il finisse éventuellement d’afficher des choses avec LcdUi…

Le matériel

Comme on a pu le voir plus tôt sur la photo, le boitier est constitué d’un écran LCD 2 lignes de 16 caractères piloté en parallèle 4 fils, d’un encodeur doté d’un bouton poussoir sur son axe, d’un gros interrupteur rouge DC/DCC en haut à gauche, de deux interrupteurs pour les fonctions en dessous, d’un bouton ’échap’ à droite de l’encodeur caché sur la photo et d’un bouton d’urgence générale en haut à droite.
Du côté électronique, un Nano R3, un LMD18200 pour alimenter la voie, un petit circuit de conversion 12V/5V pour alimenter l’Arduino, et un MAX471 pour mesurer la consommation de courant. Sur le dessus, une DEL   à droite pour l’alimentation, et deux DELs à gauche pour visualiser l’alimentation de la voie : une DEL   allumée, on est en analogique et selon celle qui est allumée on visualise le sens de circulation, deux DELs allumées on est en DCC.
PNG - 227.5 ko
Voici le schéma de ce décodeur. Tous les choix peuvent être remis en cause, mais avec ces éléments là, ça marche !
Au centre bien sûr, le Nano R3 qui peut être remplacé par un Uno, mais dans ce cas il faudra trouver une solution pour brancher le MAX471 ailleurs que sur la broche A6 qui comme l’A7 est inexistante sur un Uno. On voit que presque toutes les broches d’entrée-sortie sont utilisées. Restent seulement les 9, 13 et A7 de disponibles.
Sous le R3, j’ai représenté le connecteur qui relie l’Arduino au reste du matériel. De l’autre côté de ce connecteur on va trouver les deux interrupteurs pour les fonctions F0 et F1, l’encodeur pour la vitesse avec son bouton poussoir ’Ok’ associé mais représenté séparément ici, un poussoir de plus pour ’échap’, puis un gros poussoir rouge pour l’arrêt d’urgence en réalité petit et noir sur mon prototype, et un gros interrupteur rouge également pour sélectionner le mode analogique ou numérique. Enfin l’écran Lcd avec son petit potentiomètre pour régler sa luminosité.
Au dessus du Nano, et sans passer par le connecteur central sur le schéma, alors que dans la réalité tout passe par lui (je n’ai pas voulu surcharger le croquis…), on va trouver la prise d’alimentation principale qui va fournir à la fois le LMD18200 pour la voie, et le Nano via sa prise Vin. Toujours dans la réalité, pour éviter de trop demander au 7805 du Nano, j’ai préféré intercaler un petit convertisseur de tension réglable Chinois… Avant le pont en H est placé le circuit MAX471 qui peut ainsi mesurer le courant demandé par le LMD, et donc la voie. De ce circuit de mesure sort un fil vers la broche analogique A6. C’est elle qui permettra de savoir si une loco répond à une interrogation sur une de ses CVs ! La sortie ’Rs -’ du MAX471 rejoint ensuite le LMD18200.
Lui a son entrée PWM connectée à l’Arduino via la broche 11, et son entrée Dir sur la broche 10, le DCC_SIGNAL_PIN_MAIN qu’on a vu dans le code. Pour éviter tout démarrage intempestif de la loco à la mise sous tension avant même que l’Arduino ne soit alimenté, une résistance de 10k Ohms relie la broche PWM et la masse.
Enfin, comme expliqué plus haut, deux diodes permettent de visualiser la mise sous tension de la voie, placées tête bêches entre les fils de sortie du LMD18200. La diode témoin de mise sous tension de la centrale n’est pas représentée sur le schéma, mais ce n’est pas le plus compliqué à ajouter…

De quoi est capable cette centrale ?

Comme dit dès le départ, il s’agit d’une petite centrale de programmation, capable de ne piloter qu’une seule loco à la fois… A la mise sous tension l’état de l’interrupteur de choix DC/DCC fixe le mode d’alimentation de la voie. Il n’est pas possible de changer de mode sans redémarrer la centrale, c’est une petite protection… Une seconde protection est une demande de confirmation au lancement :
PNG - 1.7 ko
La voie ne sera pas alimentée tant que ’oui’ n’aura pas été répondu. Mon souci est de m’assurer que je ne suis pas en train de mettre une voie en DCC alors qu’une loco analogique se trouve dessus. Les moteurs analogiques n’aiment pas du tout les signaux hachés du DCC… La demande de confirmation est aussi faite en DCC, même si dans la plupart des cas (mais pas toujours, j’ai grillé un décodeur -économique il est vrai- à cause de ça…) les décodeurs acceptent de fonctionner en mode analogique.

Ensuite, on arrive dans le menu DDc principal qui n’a que deux choix : pilotage ou configuration.

Analogique / DC

En analogique, l’interface est très simple. La seule option de configuration permet de fixer la fréquence du PWM utilisée sur les rails. Selon les moteurs, des bruits peuvent se faire entendre sur certaines fréquences, en particulier les plus audibles, entre 200 et 20000 Hz.
PNG - 2.3 ko
Les fréquences disponibles sont de 30, 122, 244, 488, 976, 3906 et 31250 Hertz. Ces fréquences sont imposées par construction par le micro-contrôleur Atmega328 qui équipe les Nano et Uno.
Le pilotage se fait sur l’écran de pilotage avec le bouton encodeur principal pour régler la vitesse. Un appui sur son axe change de sens, et dans ce cas l’affichage passe de
PNG - 1.4 ko
à
PNG - 1.4 ko
. Les ’+’, ’-’ et le sens des ’<’ fixent la direction, le nombre de ’<’ ou ’>’ donne la vitesse. Au dessus de cette barre de vitesse sont rappelés le mode ’Dc’ à gauche, et la fréquence utilisée à droite.
En analogique, les fonctions DCC ne sont bien sûr pas activées, alors le bouton F0 est utilisé pour activer le ’mode lent’ qui change les bornes de la barre de progression à 0 / 50% au lieu de 0 / 100% en temps normal. Cela permet d’avoir plus de précision sur les basses vitesses en manœuvre. En haut à gauche, le texte ’Dc’ se transforme en ’Dc lent’.
PNG - 1.8 ko

Numérique / DCC

Le menu de configuration permet de fixer l’adresse de la locomotive présente sur le réseau, sachant que le programme va interroger le décodeur en entrant dans cette option.
PNG - 2.6 ko
La valeur proposée est donc la valeur actuelle. Une modification de cette valeur ne changera pas la configuration de la loco, elle permettra seulement de piloter une autre loco dont la CV ne serait pas lisible par la centrale…
On peut également fixer le nombre de pas (14 / 28 / 128), puis le numéro de l’adresse de fonction piloté par les interrupteurs F0 et F1.
PNG - 2 ko
Enfin, la dernière option permet de modifier les CVs de la loco présente sur les rails.
PNG - 2.9 ko
On choisi d’abord à gauche le numéro de la variable à configurer :
PNG - 2.7 ko
Lorsque ce numéro sera validé, DDc va interroger la loco présente sur les rails pour en récupérer la valeur actuelle, et l’afficher à droite. Les ’>’ et ’<’ passeront alors autour de cette valeur qui pourra être modifiée.
PNG - 3 ko
Enfin, lorsqu’elle sera validée, elle sera envoyée à la loco. Seul le bouton ’Echap’ permettra de terminer la saisie des CVs et de retourner au menu de configuration de DDc.
L’écran de pilotage reprend les informations importantes :
PNG - 1.6 ko
en haut à gauche le mode ’DCC’ suivi de l’adresse de la loco. A droite deux caractères ’.’ sont présents. Ils représentent les fonctions, et se changent en ’*’ lorsque la fonction correspondante est activée.
En bas, l’écran de pilotage reprend le fonctionnement de l’analogique avec ses ’-’/’+’ et ses ’>’/’<’ !

Bien sûr toutes les configurations DC et DCC sont stockées dans l’EEPROM du Nano.

La bibliothèque est disponible sur la Forge Locoduino.

Réaliser une centrale simple avec un Arduino devient de plus en plus facile. DcDccNanoController est une solution express forcément limitée, mais une autre solution beaucoup plus complète apparaîtra bientôt, basée sur le même matériel externe et les mêmes bases logicielles, mais sur un Arduino plus puissant. A suivre.