My daily job is not related to embedded computing or electronics (even if I work in IT);
=> This talk is about a n00b sharing a Sunday project.
A bit of history...
HO-scale trains from the late '70s
... They slept for 25yrs in the attic...
... Before being powered back in 2005.
We made some improvements since 2011:
Tripled the size, with a new train station;
New structures, new buildings (with lights!);
And a big electrical update.
=> 3 generations played with them!
HO train 101
Toy from the 19th century (1:87 scale)
Became popular in the '50s (thanks to plastic manufacturing)
Still produced (but VERY expensive)
14V DC supplied through the rails;
One lamp and one motor per train;
That's it! Fully analog control.
Back to our story: 2011 - we're expanding the network!
And we started to see the limits of the analog system.
To control our trains, we modulate the supplied power (from -14V to +14V)
Which means all our trains go the same way and at (approximately) the same speed
Even on our big model table, we can't handle more than three trains simultaneously
"It just works": We use switches to cut power to parts of the network.
(But don't look under the table)
We're looking for a solution
And here comes DCC!
To summarize it real quick, we send information to our trains using the power coming through the rails
=> With this, we can handle up to 8 trains going at different speeds and in different directions!
That's awesome - we need this!
But...
When you start using DCC, you must change EVERYTHING!
You need a specific power delivery mechanism ;
You need to change the train motor ;
And add a control board inside.
Total cost? Nearly €2,900 (for our 20 trains)
Well... Forget it, analog is good enough anyway.
And of course, the market only offers proprietary solutions.
Time flies...
... Until 2024.
"TinyGo, petit mais costaud ! 💪" (Thierry Chantier @titimoby)
We disassemble one of our trains
There's just enough room for a Raspberry Pi Pico (and it runs TinyGo)!
21 * 51 mm
264kB RAM ; 2 core @133MHz ; 2MB flash memory 😎
We order five of them (W variant)
Our first victim (BB9201)
14V => 5V
Our train are powered by 14V DC, but our Raspberry can only handle 5V (without smoke, sparkles and fire).
=> We add a step-down converter
Motor control
To control the train's motor, we add a PWM module
=> Let's buy an Adafruit DRV8871
And we wire everything
14V DC
5V DC
PWM signal
And it works...
... Until we accidentally reverse the direction of the train.
The converter and the RPi start to smell funny...
Let's add a Schottky diode 😬
Stability
When we test it in real condition, we realize the electrical network is not stable.
Electricity has to go through a lot of parts: rails, wheels, etc.
=> The motor can handle these micro-cuts...
... But the RPi does not like it at all!
We add a capacitor to smooth out these micro-cuts.
And now, it works!
But it doesn't fit.
Let's minify it
We design a custom PCB shield for our needs
4 weeks later...
And it really works!
We push a very simple piece of code on the RPi (speed at 100% and lights on)
Now, it's time to code!
TinyGo
What does our Raspberry Pi have to handle?
Turn light on/off;
The motor (through PWM);
Communicate with the world;
And that's good enough for a first version.
Good ol' webserver? Nope.
My first idea was to expose a REST API on each train, and communicate over Wi-Fi
That was a dumb idea (but I tested it anyway)
There's no native Wi-Fi on RPi Pico with TinyGo;
Wi-Fi requires a lot of power, which makes my capacitor insufficient;
Wi-Fi is SLOW (> 200ms per command);
=> It was the idea of someone who had never done onboard computing.
So, I switched to Bluetooth BLE
A variant of the Bluetooth protocol, designed for IoT.
Each client exposes a GATT server ;
This server exposes services ;
Each service has characteristics ;
And we can read/write on each characteristic.
When a server connects to a client, the client stops advertising and keeps the connection open.
=> Latency is negligible! < 1ms 🎉
And a server can connect to multiple clients (depending on the Bluetooth antenna(s)).
Bluetooth BLE is used by your smart[watch|glass|ring|...]
Client (TinyGo)
(native tinygo.org/x/bluetooth)
[...]
adapter.AddService(&bluetooth.Service{
UUID: serviceUUID,
Characteristics: []bluetooth.CharacteristicConfig{
{
UUID: speedCharacteristicUUID,
Flags: basicFlags,
WriteEvent: func(client bluetooth.Connection, offset int, value []byte) {
// Code to update the motor here
// (In fact, we send information in a channel and handle it after to avoid thread-lock)
},
},
},
})
adv.Start();
[...]