Mes trains passent au numérique ;
avec RaspberryPi et TinyGo !
Un peu d'histoire
- Train(s) acheté au début des années 1980 ;
- ... Puis il a passé 25 ans au grenier...
- ... Avant d'être remis en route en 2005.
- On l'a fait (un peu) évoluer depuis 2011 :
- Taille x3 (gare de triage, extension de la table) ;
- Nouveaux bâtiments, nouvelles structures ;
- Mise à niveau électrique (l'ancien setup avait 40 ans, #FormationIncendie)
- => 3 générations y ont joué !
Mais, c'est quoi un train électrique ?
- C'est un jouet apparu il a 2 siècles environ ;
- Qui se démocratise dans les années 1950 (standardisation, coûts, etc) ;
- Nous, on est en format HO (réplique 1:87) ;
- C'est toujours produit ! (mais les prix font mal).
- Il en existe plein de modèles : TGV, TER, draisines, il y en a pour tout les
goûts !
- 14V continu dans un rail ;
- 1 lampe et un moteur branché en dérivation ;
- Et... Bah c'est tout.
En 2011, on agrandit le terrain de jeu
- Et on commence à voir les limites de ce système
- On pilote nos trains en analogique, avec un variateur -14 => +14 V
- Donc tout nos trains vont dans le même sens, à la même vitesse
- Même sur notre grande table, 3 trains en simultané, à gérer, c'est rude
- Du coup, on a 18 locomotives mais on peut en gérer 3 max :-(
- Solution "ça marche" : on coupe certaines zones du circuit pour éteindre les
locos
(Par contre, faut pas regarder dedans)
On regarde si il existe un système
- Et oui, il existe le DCC !
- En très court et en très bref, on passe des informations par le courant, comme du CPL ;
- On peut ainsi gérer plusieurs trains (entre 2 et 8 selon le matériel) à vitesses et directions différentes !
Et là, on se dit : c'est génial, il nous faut ça !
Oui, MAIS...
- C'est comme un produit Apple, faut tout changer !
- Il faut un transformateur particulier ;
- Il faut changer les moteurs des trains ;
- Et rajouter des cartes dedans.
- Coût de l'opération ? Environ 2900€
Au final, nos trains en analogique, c'est pas si mal hein !
En plus, bien entendu, c'est 100% propriétaire, donc impossible d'aller bidouiller derrière.
On abandonne l'idée, les années passent...
... Jusqu'en 2024.
"TinyGo, petit mais costaud ! 💪" (Thierry Chantier @titimoby)
On vole emprunte un train, et on démonte
- Il y a pile la place pour mettre un Raspberry Pi Pico !
- 21 * 51 mm
- 264kB RAM ; 2 coeurs @133MHz ; 2MB de mémoire flash
- On en commande 5 en version "W" (avec WiFi/Bluetooth inclus)
Notre victime (BB9201)
14V => 5V
- Le rail est alimenté en 14V, le Pi prend 5V max => étincelles, chaleur, fumée.
- => On achète des ponts abaisseurs de tension
Contrôler les moteurs
- Pour piloter les moteurs, on ajoute un contrôleur PWM
- => On achète des Adafruit DRV8871
On branche tout ça
- 14V continu
- 5V continu
- Data (contrôle PWM)
Et ça marche...
- ... Jusqu'à ce qu'on inverse le sens du train sans faire exprès.
- Le 14V entre dans le mauvais sens du convertisseur, crame la carte et le RPi
- On va rajouter une diode hein 😬
Stabilité
- On teste et on se rend compte que le réseau électrique n'est pas stable
- Beaucoup de pièces font passer l'électricité : rail, roues, etc
- => Plein de micro-coupures qu'on ne voit pas sur un moteur...
- ... Mais le Pi lui, il redémarre en boucle !
- On ajoute un condensateur pour lisser le courant.
Et là, ça fonctionne !
Par contre, ça rentre pas
On minifie un peu
- Impression d'un PCB "shield" pour le Pi
4 semaines plus tard...
Et ça démarre !
- On pousse un bout de code simple sur le Pi
- (il fait tourner le moteur à fond et allume la lampe...)
- Le moteur tourne, la lampe s'allume, GG.
Passons au code
TinyGo
TinyGo
- Adaptation de Go pour de l'embarqué
- On retrouve une grande majorité des features & concepts de Go
- MAIS TinyGo != Go => Certaines librairies ne fonctionneront pas
- Cycles de vie distinct entre Go et TinyGo
Bon, notre Pi, il gère quoi ?
- La lumière ;
- Le moteur ;
- La communication avec le reste du monde ;
- Et c'est déjà pas mal.
Good ol' webserver
- On part sur un classique client/serveur
- Simple, peu coûteux en ressources
- Le Pico expose une API
- Un serveur "central" fait le lien entre un frontend et les trains
- C'est pas parfait mais c'est une V1... 😉
Dans le train
- 5 endpoints :
- Allumer/éteindre la lumière ;
- Modifier la vitesse & le sens ;
- Récupérer le statut du train ;
- Ping !
Dans le train
//--- Constants
var DirectionPWM = machine.PWM1
const DirectionPin = machine.GPIO18
// ...
DirectionPWM.Set(DirectionChannel, 0);
var speed = 50;
SpeedPWM.Set(SpeedChannel, SpeedPWM.Top() / 100 * speed)
Dans le train - Watchdog!
- Quand le train perd l'alimentation > 5s, il devient "zombie"
- => Besoin de le full-reboot : On découvre les watchdogs !
- Un dispositif "anti-deadlock" qui permet de reboot si soucis
- Basé sur une horloge annexe dans le processeur
- Si on envoie pas de signal pendant 8s, reboot !
Dans le train - Watchdog!
func main() {
machine.Watchdog.Start()
go sanity()
}
func sanity() {
for {
time.Sleep(time.Second * time.Duration(4))
if err := checkAlive(); err == nil {
machine.Watchdog.Update()
Logger.Info("Sanity check passed")
}
}
}
Dans le train
- On contacte le serveur "central" pour lui dire qu'on est là
- On initialise tout les capteurs (moteur, lumière, etc)
- On démarre le watchdog
- Et on attend les ordres
Le serveur central
- Pas grand chose à dire, c'est une API REST basique
- Écrit en Go, et ça tourne sur un Raspberry Pi 3 qui trainait
L'interface graphique
Pas trop vite quand même
Car nous avons deux soucis :
- La communication avec la carte est LENTE (> 300ms) ;
- Et surtout, on découvre... La draisine modèle 8525.
Jouef 8525
- Voici notre adversaire : la draisine ! (Modèle 8525)
- La place à l'intérieur ? 21mm * 42mm * 12mm (l/L/H)
- Notre montage : 18mm * 21mm * 51mm (approx) 🫠 (l/L/H)
- Il va falloir (encore) miniaturiser !
Seeed Studio (XIAO)
- Fabriquant de cartes de très petites tailles
- Taille standardisée : 21 * 17.8mm
- Plusieurs variantes (ESP32, RP2040, nRF52480, etc)
Premier essai
- Le RP2040 n'a pas de connectivité, donc on oublie
- On tente avec un ESP32C6 (j'en commande 3)
- Et ça fonctionne plutôt bien ! Mais TinyGo n'est pas compatible 😭
- => On va mettre cette info dans notre inventaire et on en reparle après
Miniaturiser... Tout le reste.
14V => 5V
- On trouve un remplaçant : Le Pololu 5V@500mA fixe

22mm * 17mm
|
=>
|

7.6mm * 11.4mm
|
Miniaturiser... Tout le reste.
Contrôle du moteur
- On trouve un remplaçant : Le TA6586

24mm * 20mm
|
=>
|

9mm * 6mm
|
Et on retourne sur KiCad !
C'est l'heure du shield v2 (et on soude des 2 côtés désormais...)
15 jours plus tard...
Et ça passe !
Mais sans TinyGo 😭
- Alors je regarde s'il existe une Seeed supportée par TinyGo ET de la connectivité sans fil
- Un seul candidat potentiel : Seeed nRF52480.
- mono-coeur 64MHz, 256KB de RAM, 2Mo de Flash... Quasiment notre Pi du départ !
- Par contre, pas de WiFi : Bluetooth-only !
- Ça se tente, j'en achète 2 pour voir... Et je découvre le ✨monde merveilleux du Bluetooth (BLE)✨
- Mais on va peut-être résoudre le problème de latence ?
Bluetooth BLE
Variation du protocole Bluetooth adapté pour l'IoT. Pour le résumer rapidement :
- Chaque appareil "client" expose un serveur GATT ;
- Ce serveur expose des services ;
- Chaque service expose des caractéristiques ;
- Et on peut lire/écrire dans une caractéristique
- Le serveur va pouvoir se connecter à plusieurs appareils en simultané, et garder la connexion ouverte en permanence
- => La latence est donc très faible ! < 1ms en écriture 🎉
Client
adapter.AddService(&bluetooth.Service{
UUID: serviceUUID,
Characteristics: []bluetooth.CharacteristicConfig{
{
UUID: speedCharacteristicUUID,
Flags: basicFlags,
WriteEvent: func(client bluetooth.Connection, offset int, value []byte) {
// On met à jour le moteur ici
},
},
},
})
adv.Start();
Serveur
// Detect
devices, _ := ble.Find(ctx, false, func(a ble.Advertisement) bool {
return strings.HasPrefix(a.LocalName(), "Trainberry::")
})
// Connect
for _, k := range devices {
cln, err := ble.Connect(ctx, func(a ble.Advertisement) bool {
return a.LocalName() == k.LocalName()
})
//Send info
cln.WriteCharacteristic(&ble.Characteristic{ValueHandle: 16}, data, true)
}
Ça reste lent entre le front et le serveur
- On remplace l'API par une WebSocket
- On évite toute la latence liée à la stack TCP/IP (ou presque)
- Et on envoie/reçoit les notifications en temps réel !
- On garde juste un
GET /state pour récupérer l'état initial
Et en plus, ça simplifie !
- Plus besoin de réseau WiFi, donc :
- Plus besoin d'avoir un routeur dédié à cet usage ;
- Plus besoin de traîner une stack TCP/IP partout ;
- Plus besoin d'avoir des dizaines de paramètres dans la configuration du train
What's next ?
- Maintenant que les soucis sont résolus, on va pouvoir s'amuser !
- Annonces en gare ;
- Faire faire "tchou tchou" aux trains !
- Stop avec des tags NFC
- Carte temps-réel
- Webcam intégrée
- Passer les aiguillages au numérique
- Bref : J'ai PLEIN d'idées ! 😁
- Toujours en Open-Source, toujours en Open-Hardware. ❤️
Pour conclure
Et le making-off de la maquette sur forestier.re !