I’m going through Knurling Sessions 2020 Q4, an excellent course/project from the Knurling project. It was originally available only to sponsors of the project but is now open to everyone for free.
I decided to use the latest version of all crates instead of following the instructions to the letter. Here are some notes on things that have changed as of May 2021.
18.104.22.168. Hello World: while editing config.toml to set up the first project, the instructions show a flag that is no longer in the app template; probe-run no longer supports the --defmt flag, just skip it.
If you work on embedded Linux systems, those Docker container thingies, play around with Raspberry Pi or anything in between, you may occasionally find yourself on your main computer connected to the target via SSH when it suddenly hangs or gets rebooted, rendering your SSH session unresponsive. Sometimes when this happens, the SSH session gets disconnected automatically and you can start a new one. In other cases, the terminal with SSH open freezes and your usual hotkeys like ctrl + D do nothing. This is because ctrl + D key presses are usually transferred to the target and the disconnecting is so to say engaged from there.
If only there was a hotkey that was detected by the SSH client before it tries to send it to the target 🤔. You guessed it, there is! The sequence ⏎ + ~ + . lets you disconnect from an unresponsive target without having to close the terminal or anything like that. To be clear, you press the keys one by one, no need to hold them all at once.
It does not work!
I had some troubles getting this to work reliably. Two things that helped me:
Realizing the Enter key is part of the sequence. The first time I read about this, the sequence was said to be just Tilde + Period. Often when working in a terminal, Enter is the last key that was pressed so this works fine most of the time. Remember Enter as part of the sequence!
Remembering how to type Tilde on my keyboard layout. Because I use a Swedish keyboard, Tilde is actually alt + ¨ , space, where ¨ is the key with ^ and ¨ on it. Since there is no visual feedback when typing in the escape sequence (which is now five key presses), I would sometimes get it wrong.
Recently, I got to learn about Strict Aliasing in C. It is yet another thing that can cause your C code work perfectly fine today and then blow up because of Undefined Behavior down the line. One example of what not to do is casting an array of uint8_t (like a payload from a communications protocol) into a struct (like the message you are receiving):
I recently started learning Embedded Rust. As I mentioned at the top of the last post, there are a couple of toolchain options:
OpenOCD + GDB
probe-rs + cargo-embed
Turns out there is also a third one that I just discovered:
probe-rs + probe-run
This was a little confusing at first, I was unsure what was the better option and how these projects all connect to each other. Embedded Rust seems to be moving fast, so this might get outdated but here is a basic summary if you are just getting into Embedded Rust as well.
The Embedded Rust way
Rust-embedded is a working group in the official Rust organization. Among other things, they maintain The Embedded Rust Book, which you may have come across. In the book, they describe what I would call the “official” toolchain, using OpenOCD and ARM-compatible GDB (gdb-multiarch). This is probably the way to go if you need to do serious development today. OpenOCD and GDB are stable and mature projects and for a Rust programmer, it is the most fully-featured and reliable option right now.
As an alternative to those external (non-Rust) dependencies, a team has formed an ambitious project around replacing it all with software written in Rust – probe-rs. Here is an illustration (borrowed from the video below):
Here is a very informative talk by one of the people behind probe-rs:
My major learning from this talk was that probe-rs is really a library. Other projects, like cargo-embed and probe-run are built on top.
The probe-rs team built cargo-embed to show off the capabilities of probe-rs. As such, it was the first tool I came across when I found the official probe-rs website. One might imagine that being built by the same team, cargo-embed will stay closer to the latest features of probe-rs and have a shorter path to get new features in. But this is just speculation.
To build and upload programs, you simply run cargo embed --release (see my last post about why --release is important for timing). It possible to do logging with rtt, a debugger-based thing that uses an internal buffer that gets read out by the debugger instead of, for example, printing over UART. Debugging is also supported (but not at the same time as rtt currently) from the command line, or visually in something like Visual Studio Code, by hooking into the GDB stubs that probe-rs provides. This is an interface that (to the best of my understanding) mimics GDB’s, but actually goes directly to probe-rs.
Configuration is done in a new file called Embed.toml. There you configure what chip you are using and whether to use rtt or GDB debugging, or set up separate profiles for each.
The vision of probe-rs is to offer a full development environment for embedded Rust, so they are also working on a VSCode plugin. It is still in alpha, and I have not tried it yet.
Ferrous Systems is a company that pops up everywhere in embedded Rust. They are a consultancy specializing in Rust for embedded applications and are also very active in open source development for embedded Rust. They started a project called Knurling dedicated to improving the experience working with embedded Rust.
Knurling has many sub-projects and probe-run is one of them. Built on top of probe-rs, it gives you the same features as cargo-embed, but in a slightly different packaging. The philosophy is that embedded development should work the same way as native development, so instead of introducing a new cargo command, probe-run is a so called Cargo runner. This means you configure the “usual” cargo run command to use probe-run under the hood. And there is no new configuration file to keep track of, just the regular Cargo.toml and .cargo/config.toml. Does it matter? Up to you.
Knurling also has an interesting logging framework; defmt. Instead of doing string formatting on the embedded device, it relies on a tool on the host side and simply generates a list of strings at compile time that is kept on the host. The embedded device then simply sends the index into that list (using rtt), causing much less overhead.
I do like the idea of keeping the main Rust interface unchanged, which speaks in favor of probe-run, but I’m not sure about plans for integrating probe-run with VSCode, or debugging with breakpoints. As I learn more, I hope to find a favorite and maybe also start contributing myself.
TL;DR: When doing timing critical stuff, use the --release flag to get a faster binary! For example: cargo embed --release.
I’m learning Embedded Rust on a STM32 Bluepill board (with a STM32F103 microcontroller). At the time of writing there seems to be two toolchain options:
The “official” Embedded Rust way, using OpenOCD and ARM-compatible GDB.
Up-and-coming probe-rs that is working on having everything in Rust and installable via cargo. Their tool cargo-embed basically replaces OpenOCD and GDB.
OpenOCD + GDB is true and tested, but a lot more work to set up. Probe-rs is litteraly just cargo install cargo-embed, but it is a work in progress and far from feature-complete. I tried both, but this particular thing caught me while using cargo-embed, so that’s the command I will be showing.
The Timer Problem
I wanted to talk to a ws2812 adressable RGB LED (also known as NeoPixel). I found the crate smart-leds that seemed perfect. It comes with several “companion crates” with device drivers that support different LEDs and several options for different ways of driving the ws2812, like the ws2812-spi and ws2812-timer-delay.
The SPI crate unfortunately did not work in my attempts so far. It manages to write to my LED once, then panics with the error “Overrun”. Probably I’m using a newer version of the embedded-hal and/or stm32f1xx-hal than it was written for. Maybe a topic for another day.
The Timer Delay crate also did not work at first. I broke out my Analog Discovery 2 to look at the data signal:
The time between bits was around 200 us. To get a comparison, I fired up a Platformio project for the same STM32 Bluepill board and imported Adafruit’s Neopixel library. Now, the LED of course worked perfectly and the problem was obvious:
The time between the bits was now only around 1,4 us. I will spare you the details of all the things I tried while wrongly thinking either the entire MCU or the timer was running at the wrong frequency.
The solution turns out to be almost silly: Rust binaries can be really slow if you do not compile them in release mode. Just add the --release flag and all is well! 💩
cargo embed --release
There is apparently a way to override this per-dependency in Cargo.toml, that might be worth a try if you need it.
I tried adding the following to Cargo.toml to make all dependencies build with the highest optimization level, but this still was not enough to make the LED work in my case.
In 2018, I had the great honor to speak at the NDC conference in Oslo. At the time, I was working with cellular connectivity for IoT at nordic mobile operator Telia, and I titled the talk accordingly. NDC is mainly a developer conference, so the talk was intended as an introduction to cellular IoT for the “Rasppberry Pi and Arduino crowd” that I anticipated would show up. I went into the difference between NB-IoT and LTE-M as well as between chips, modules and boards. Probably the best part however, if I may say so myself, was the last one, where I showed a live demo of working with a couple of development kits.
I have been working on my own custom wakeup-light on and off for several years (part 1, part 2, part 3). After getting Philips Hue lights, however, I have not gotten around to setting it up in my new apartment. So recently, when the need came along, I figured the quarter round rod I used for that project might also make a pretty nice looking bed light.
I cut one of the old rods to length and taped it to the headboard to see how it would look:
This could work! I borrowed the old driver board from the wakeup light and hooked up a couple of potentiometers to an Arduino to try the whole thing out:
After convincing myself (and subsequently the significant other) that this was a good idea, I went at designing and 3D-printing some mounting brackets to replace the silver tape that held the v1 in place.
The last version was long enough to hold the rod and had holes to let the cable escape invisibly out the back of the headboard:
Looking way better already!
The next thing to work on was the electronics. Using potentiometers and analog inputs was not really ideal as the light would flicker and change even by just touching the knobs or the cables and required frequent polling of the inputs in the code. Also, the potentiometers I had were only single-turn.
To make a more reliable control, I ordered some EC11 and EC12 “coded switches” or rotary encoders from Aliexpress. These turn just like potentiometers, but are really acting like switches, opening and closing in a sequence that lets you decide which way they are rotating. The main advantage of this is that there is no need to sacrifice an entire analog input channel just for the sake of having a rotary knob and furthermore, the pins can be hooked up to pin interrupts, removing the need for polling altogether. The ones I got could also be pushed like a button, which I could use to switch between brightness and color mode instead of having two separate knobs like before.
After getting the encoders in the mail, I designed yet another version of the holder that would also house the control knob:
The knob looks a bit out of place and is bigger than is really needed. I will probably try making a smaller one that fits the style of the whole thing a bit better for version 4. And then it would also be neat if the light could be controlled over wifi of course… But first I will figure out a way to hide the electronics so that they are not on top of the headboard!
The 3D-printed parts I made fit the bed IKEA Malm really well but are fairly simple in design. If you still would like me to share them for your own project, drop a comment below!
No power switch – the only way to turn the printer off is by unplugging the cord.
No lights inside the enclosure – we got the additional enclosure, but it came without any lights inside.
No network interface – you have to connect to the printer via USB directly or go get the SD-card. Also no way to control or monitor it remotely.
So to fix this, I wanted to put a switch on the back, put in some lights and add a Raspberry Pi with a camera and Octoprint inside the printer. I started by printing a faceplate for the hole in the back of the enclosure:
This was then fitted with a switch and a DC jack for the power supply. (Make sure their rating is higher or equal to that of the power supply!) The jack lets me split off power to the Raspberry Pi and also fixes the slight annoyance of having to reach in to the very back of the enclosure to connect the cable to the printer.
I then soldered and crimped some cables to wire the switch in series with the DC jack and connected two Wago cage clamps for distributing power to the multiple things on the inside of the enclosure. I also mace a short DC cable to connect to the printer. The face plate has a flange to keep it from moving around in the hole, and is less than half the thickness of the enclosure wall, so I printed a second copy of it, put one on from either side and simply secured the whole thing with the locking nut of the DC jack.
Next, I added some LED-strips on the sides. Note that the enclosure is laying upside-down, so they are actually in the ceiling.
The power supply gives out 12 volts, which is fine for connecting to the printer and the particular LED-strips that I had. The Raspberry Pi however, requires a 5 V power supply, so I wired in a step-down converter and a micro-USB cable.
Finally, I put the enclosure back on and connected the DC-jack and the Raspberry Pi. Here is what it looks like in the back of the printer now.
For now, the Raspberry Pi is just laying inside the enclosure and the camera is simply stuck to the back wall with double-sided tape. I might come up with something smarter in the future, but it works for now.
The LED-lighting made a huge improvement! It was also completely necessary for the Raspberry Pi camera to be useful.
To sum up, these improvements took less than a day to do in total and were fairly inexpensive, but provide a huge step up in usability. We can now upload prints and monitor the progress from our desks instead of going to the printer all the time. Octoprint also has a Cura plugin so that you can simply upload STL-files directly without the need for everyone to have a slicer installed locally. This also means we can have optimized settings on the printer and not have to distribute settings to each individual using the printer.
One caveat is that the Trinus LCD display does not work with Octoprint, meaning that you cannot stop the print or use any of the other features on the front panel but have to run back to the computer to stop a failed print. I might replace the LCD with a small touch screen connected to the Raspberry Pi instead and/or wire in an emergency stop button to the GPIO pins. Also, the LED lights flicker quite a bit as the printer draws more or less power, probably due to the poor-quality power supply. I might try to fix it with some decoupling capacitors and/or a new power supply.
Let me know if you did similar upgrades, have some good ideas for the Raspberry Pi and camera or if you just want some pointers on doing this to your own printer!