Rebuilding an old Arduino Based PID for a Rancilio Silvia with a PI Zero and Rust

Similar projects worth following
About 10 years ago i tried my hand at an Arduino based PID for my Rancilio Silvia. It had 4 buttons, a serial display, a type K thermocouple, and an SSR to switch the boiler on and off. It did it’s job, but the UI wasn’t friendly, and I never got around to to putting an RTC in it for waking it up on a schedule. Now there are much more powerful boards out there, so I figured I would try my hand at rebuilding it with a PI Zero.

I have also been really wanting to learn Rust. So I picked up some supplies and started in on it.

Input Buttons

I’m using the buttons found on the Adafruit pi bonnet. These utilize 7 gpio pins.

On the original Arduino I utilized a single loop technique with a number of time stamps to debounce the 4 buttons. On the new system I decided to write a button pad struct that could be set into its own thread with its own loop and pass back via a rust channel all the button events: Press, Release, and Repeat. Repeat is triggered after a long wait without a release then fires more rapidly, as one might expect. This setup makes it much easier to work in more sophisticated abstraction layers and will assist me as I setup stronger paradigms for routing requests to their destinations.


The Adafruit display is the primary interface for graphical output. I’ve utilized the frame buffer kernel driver to provide generic access to it, and then I have used a basic rust framebuffer memory mapping library to get low level memory writes and flushes to the display. On top of this I am implementing some low level basics like lines and boxes.


The canvas abstraction builds on the display code to enable composition of elements and manipulation of layers and layer groups. It interfaces with the display code to handle final placement and compiling of the data before render. 

- Layers can be active or inactive.

- Canvas runs clipping for canvas items that are off screen.

- Canvas implements placement and item movement.

- Canvas can manipulate several layers as a group.

- Canvas handles memory buffer to frame buffer control.


Roughly equivalent to a screen of data, the view will hold a group of UI widgets. The abstractions start with a root view container, with an info bar on top 25-30 px and a display area for the view objects. The views themselves have multiple modes, navigate and manipulate, for selecting and moving between widgets with the key pad, and for routing keypad button actions to the widgets themselves.  


The widgets are groupings of canvas layers, which can be manipulated by changing which layers are active, or changing their coordinates on the canvas.  


The sensor input will follow a similar setup as the button handling class, and may in the end use the same channel to send input events. 


The solid state relay will then function as the output switching on and off based on user input and the temp readings. 

  • Updates

    David P Smith05/07/2021 at 12:42 0 comments

    It's been a little while since I've put much of an update in here.

    I've spent some time refactoring Lovett to get the overall program flow in a better state. I've adopted a strong redux pattern for handling program state changes.  There are three major functional container structs. WindowViewer which contains the program views, the Store, which holds the program's state tree and handles reducers, and the Model Scheduler, which (theoretically) would handle starting up program threads as needed. The lines indicate message busses of the type indicated but the diamonds. Event messages indicate what has occurred in the program and they are routed to the dispatcher which determine what action or actions need to be send to the Store. The store runs the reducer code to produce the latest version of the program State. State messages are then relayed to Views in the Window Viewer and to the Model Scheduler and its model treads (should they need state).

    I would love to work on a better State element subscriber system. I currently have filters as a stand-in, so that State RXs will only see new State trees based on the conditions they supply. But this still ends up passing an entire state tree to various places that don't require it, and passes deeper state change detection back to the recipient structs, which seems a little inefficient.

    I have also been working on a implementing basic configuration as a static struct. This required tapping into the laxy_static crate to define how the static config could be built up at runtime. The config object is static so it can be read anywhere at anytime without having to pass it along as a value everywhere, which could become a bit onerous. Currently the config object holds two things, the font vector and the color palette. 

    Moving the font vector to a static config was the tactic I took to address another issue. I wanted to use the font in multiple places, but re-loading multiple copies of a font at a few mb each was eating up ram much faster than I had realized. Moving it to a single instance shared in the program got the ram usage back down to normal.

  • Font Rendering Options (With Caching)

    David P Smith10/24/2020 at 18:52 0 comments

    So after my analysis with perf, It looked like the biggest inefficiency was with the way I was using `rusttype` . The draw function was re-rasterizing each character in my onscreen clock for every clock tick. There are several ways to deal with that, but I didn't want to handle it entirely in more complicated app code. I wanted a way to push that kind of issue down into the framework.

    With my initial use of rusttype I was having to rasterize the entire string of chars for the clock every second (ie every time the clock ticked forward by one second). This means that every character before the seconds was being rasterized when for most of the time we only needed to rasterize the last couple of chars in the string. 

    At first I though I could just break down the string a bit into segments, and each segment would be updated only when that time char was updated.. ie render the hours number only when the hours changed. But as a general method this felt like it was putting too much logic into app side of things, when the framework itself should have a better way to do it.

    Enter ab_glyph. One of the authors of rusttype was already working on some code that would help this situation out. While ab_glyph was intended to offload some work to a GPU, it had enough gpu independent code to create a local font cache. This means that I only have to rasterize characters when they differ enough from what's in the cache to require it. 

    It took a bit of work to follow along with how the system works, but after awhile I got it all up and running. And in the process shaved about 1% of cpu usage off! (Hey 1% is important on a raspberry pi zero)

  • Perf on PI

    David P Smith10/08/2020 at 16:54 0 comments

    I was noticing that the CPU usage felt a little high on my Lovett framework for a fairly simple operation. So I took a new rabbit hole plunge. One of the best tools to get detailed performance details on a linux system, is the perf tool. It turns out, however, that the driver for by graphics fb requires a newer kernel than the base raspbian system provided. When installing the driver it automatically installs the latest kernel version. That would be ok, except that the kernel install doesn't come with a paired version of perf. So if you want perf, it looks like the only way is to compile it from scratch.

    So I go and download the kernel of the same version, hop over to tools/perf and see what it takes to get it up and running. The make utility is fairly helpful here, letting you know what packages you probably need to install to get it up and running. Unfortunately, I ran into a couple of gotchas.

    1) It looks like some code expects a different param type than would normally occur in an arm32 compile. A bit of searching turned up a helpful patch: ... with that in place all the sources were able to compile.

    2) Even though it compiled the util was crashing on me before it could do much. That led down another rabbit hole, But I eventually found an option that worked after taking a general glance at this article here:

    3) Equipped with 

    -e cpu-clock

    from that article I was able to generate the first round of data. Up next, though I could not generate a report. I was getting a number of crashes. The first one involved libssl. So I went through and did a bunch of raspbian updates, including installing a different libssl version. That triggered a whole lot more updates. At some point one of those updates appeared to have borked by boot options. So I reinstalled the driver which caused a new kernel version to get installed. That then required I go back and repeat steps 1 and 2 above. This time the report crashed on libunwind. So i rebuilt the perf util with

    make NO_LIBUNWIND=1

    This finally succeeded and the report was not readable.

    The result, I found a couple of places to tweak. Opting for some integer math over floating point. And then finally noticing the area of the code that was causing some object to get reinstantiated with a fairly large cpu penalty. Fingers crossed I'll be able to make a much more efficient font rendering and updating code over the next week.

  • Introducing Lovett

    David P Smith09/29/2020 at 03:24 0 comments

    So as I worked through my code, I thought it might be better to just abstract it out into its own framework for pi based fb devices with input buttons. 

    So I went through my code and pulled out the things that really could be abstracted out. The  result was something I decided to name Lovett.

    It needs a lot of work, but I figured why not?

  • States, Mutators, Views, oh my!

    David P Smith09/12/2020 at 05:26 0 comments

    So by this point, I’ve pretty well gone off the deep end, because, well, it’s so much fun. 

    So I started thinking about what would go on top of the canvas layer, which led to the widget layer, which needed a view, which needed a state store, which needed a mutation system. ...

  • Font Handling

    David P Smith08/23/2020 at 23:41 0 comments

    I just got Font handling up and running! It ended up being a bit of a habit hole, as proper font support required getting alpha blending up and running too, and once i had gone there, I dumped the bmp crate for the image crate, with a much larger capability and easy handling of alpha layers.

    I'm using the rusttype library from the redox-os project. It wasn't too hard to to get it working ok, and I can use google fonts to get choose some nice typefaces. 

    Now that I have the main canvas primitives that I want, I can get to adding some basic gui elements and then move on to a sort of gui - view setup, and get a little mvc style thing going.

  • Adding Code Links to Project

    David P Smith08/19/2020 at 13:52 0 comments


    Adding the GitHub repo for the code here too.

    You can check out my current code here:

    It's currently pretty rough, and the first project I've ever done with rust, so I'm 100% sure many Rustaceans will cringe at my code. Also unit tests need to happen :). 

    Some of the code for image rendering to fb is derived from example code provided by,  (under the very permissive license, I'm assuming that's ok), as well as some provided by example code for rusttype

  • Canvas and Button Control

    David P Smith08/17/2020 at 05:06 0 comments

    I started off playing around with the buttons and display module. First off I just wanted to make sure the display was working well, then I got to building some code to abstract button handling, finally I’m working on the Canvas and rendering pipeline now. 

    Code coming soon. 

View all 8 project logs

Enjoy this project?



Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates