Nos 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) ;
- 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
DCC au secours
- 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.
Time flies... 2011 -> 2024
"TinyGo, petit mais costaud ! 💪" (Thierry Chantier @titimoby)
On démonte un train...
- 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)
Et on va builder une v1...
- Pont abaisseur de tension (car notre Pi supporte 5V, et notre réseau est en
14 18-20V
- Contrôleur PWM pour le moteur...
- 14V continu
- 5V continu
- Data (contrôle PWM)
... Et très vite une v2
- Car le réseau électrique n'est pas stable : roues, rails, ... => Beaucoup de pièces mouvantes
- On rajoute un condensateur
Et là, ça fonctionne !
Par contre, ça rentre pas
On minifie un peu
- Impression d'un PCB "shield" pour le Pi
Sauf que...
... Nous avons un problème de taille
Alors on retourne miniaturiser
- Raspberry Pi (51*21mm) => Seeed Studio nRF52840 (21*17mm)
- Adafruit DRV8871 (24*20mm) => TA6525 (9*6mm)
- Pont abaisseur de tension générique (22*17mm) => Polulu 5V (7*11mm)
Et ça rentre (au chausse-pied)
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 ?
- Notre première idée : un client-serveur REST classique. MAIS :
- Pas de Wi-Fi natif sur TinyGo => On a recodé un truc (sale) nous-même
- La latence est abominable (Plus de 300ms) ;
- La carte consomme énormément (le Wi-Fi, c'est gourmand en élec) ;
- Et notre carte pour la Draisine (le Seeed Studio nRF52840) n'a pas de puce Wi-Fi.
Une solution ? Bluetooth BLE
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 🎉
- Un serveur peut se connecter à plusieurs clients (ce PC = 8 clients)
- Et en plus, c'est un protocole très connu et utilisé !
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();
Client - Mono-cœur
- On doit mettre à jour les données du moteur, mais le processeur est déjà occupé avec la communication Bluetooth...
- Résultat => Une deadlock où plus rien ne se passe
Client - Mono-cœur
- La solution ? On va ajouter un
chan qui stocke les informations pour les traiter après
- L'impact sur les performances est négligeable
Client - Mono-cœur
// constants.go
var EventChannel = make(chan structures.Event, 5)
// bluetooth.go
// ...
Characteristics: []bluetooth.CharacteristicConfig{
{
UUID: speedCharacteristicUUID,
WriteEvent: func(client bluetooth.Connection, offset int, value []byte) {
constants.EventChannel <- structures.Event{
Data: value,
Operation: constants.SpeedOperation,
}
constants.SpeedState = value
}
},
// ...
Client - Mono-cœur
// main.go
func main() {
// ...
for {
event := <-constants.EventChannel
switch event.Operation {
case constants.LightOperation:
controls.SetLight(event.Data)
break
case constants.SpeedOperation:
controls.SetSpeed(event.Data, reverseBool)
break
}
}
}
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)
}
Architecture finale
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 : On a PLEIN d'idées ! 😁
- Toujours en Open-Source, toujours en Open-Hardware. ❤️
Pour conclure...
- Ce talk montre 1% des difficultés qu'on a pu avoir !
- On a pas parlé des watchdogs, des cartes sacrifiées sur l'autel des tests, etc
- Un projet qui nous a fait sortir de nos habitudes et de notre zone de confort
- Et qui nous a permis d'onboarder des gens très loin de la tech !