Posted on ::

Recap

In the first post, I covered project setup and the code that retrieves and parses the arrival predictions data from the TfL API.

In the second post, I used the data from the API to create an image and save it to PNG.

In this short post, I will describe the hardware setup for the project.

The code can be found on on GitHub - london-bus-forecast.

Shopping List

This list is just reproduced from the first post.

  • Raspberry Pi Zero 2 This is a tiny quad-core ARM computer with a mere 512MB of ram, but it will be enough for my purposes. The W edition has Wi-Fi, which is essential. If you are a non-solderer like me, go for one with the GPIO header pre-installed.
  • Micro SD Card to use as storage
  • Bits and bobs that come bundled as a starter kit, such as:
    • Micro USB Power Supply
    • Mini HDMI Adapter
    • Heatsink/Case
  • 7.5in E-Ink Display I chose this based on the size and knowing I only need a black and white display

Maybe it's in a different category to "hardware", but you can also include a 7x5in picture frame to hold the display.

Setting up the Raspberry Pi

Basic Setup

The easiest way to setup the Raspberry Pi is using the Raspberry Pi Imager.

There is not much I can say that isn't covered better in the official documentation, so just follow that. I selected Raspberry Pi OS Lite (64-bit) because I don't need a desktop installed.

The software allows you to pre-load your WiFi credentials and also provide a public SSH key, so you can connect to it. I opted out of Raspberry Pi Connect.

Additional Setup

We need to make a few more changes to allow for the e-ink display.

All these commands are run in the command line after connecting to the device via SSH.

The first is to enable the SPI interface. The display documentation for the display explains how to do this.

Essentially, you run the following command, which gives you an interface you can use to enable SPI under the "Interface Options" submenu.

sudo raspi-config

The next step allows using the physical interfaces without being root. My username on the device is bus, but you can substitute whatever your username is.

sudo usermod -a -G gpio,spi,i2c bus

After this, it is straightforward to connect the screen to the GPIO header and try out some of the example code given by the manufacturer.

Now we are happy the display functions, we can try to display our bus arrivals image.

Setting up your Development Environment

Trying to compile the code on the Raspberry Pi Zero itself is so slow that it's a non-starter. The target architecture is aarch64-unknown-linux-gnu and I am running an x64 Windows computer and a x64 Linux computer.

The most straightforward choice is cross, which requires Docker or Podman.

Vendored OpenSSL

OpenSSL is a system library that provides SSL/TLS, i.e. allows secure HTTPS requests and is therefore a dependency of reqwest by default. If you try to compile your project with

cross build --release --target=aarch64-unknown-linux-gnu

you will get an error due to being unable to find an OpenSSL installation to link against.

One possible solution is to add "vendored" OpenSSL as an optional dependency. In your Cargo.toml, add the following to the dependencies section

openssl = { version = "0.10", optional = true }

Next add a features section.

[features]
default = []
vendoredssl = ["openssl/vendored"]

From here you can compile successfully by enabling the vendoredssl feature.

cross build --release --target=aarch64-unknown-linux-gnu --features vendoredssl

The Cross Wiki has a few alternate recipes.

rpi feature

In the previous posts, we created an app that creates a PNG image and has no specific Raspberry Pi or e-ink display functionality. We should create two variants of the app via another feature flag.

First add another optional dependency in your Cargo.toml file:

rpi-pal = { version = "0.22.2", features = ["embedded-hal"], optional = true }

The above allows communication with the e-ink display on a Raspberry Pi device.

Then add an item to the features section:

rpi = ["dep:rpi-pal"]

Now the cross-compile command becomes

cross build --release --target=aarch64-unknown-linux-gnu --features vendoredssl --features rpi

In the main.rs file, we will create a new main function, so the old function will only be activated when the rpi feature is turned off.

#[cfg(not(feature = "rpi"))]
fn main() -> anyhow::Result<()> {
  // Generate a PNG image
}

The e-ink function will be activated when rpi is turned on.

#[cfg(feature = "rpi")]
fn main() -> anyhow::Result<()> {
  // Write to the e-ink display
}

We will also create a new waveshare.rs file and optionally declare the module. This will contain code to control the e-ink display.

#[cfg(feature = "rpi")]
mod waveshare;

Configuring rust-analyzer in VS Code

I use rust-analyzer in VS Code for Rust development. This provides autocomplete, formatting (via rustfmt), diagnostics (via rustc and clippy) and so on.

In our setup, we have different versions of the main function depending on whether the rpi feature is enabled. The rust-analyzer will effectively ignore any code that is disabled, greying it out and not allowing any diagnostics. To switch the rpi feature on for diagnostics, I add the following to .vscode/settings.json.

{
    "rust-analyzer.cargo.features": ["rpi"]
}

To disable it, I just comment that line, so that it falls back to default = [] in the Cargo.toml file. The VS Code Settings file is ignored by git in the .gitignore file.

The next post will focus on the code itself.