Skip to content

A project to add a radio-controlled sticky note to my office door using a Raspberry Pi and a Waveshare e-Paper Display.

Notifications You must be signed in to change notification settings

pkgw/rc-stickynote

Repository files navigation

Raspberry Pi Radio-Controlled Sticky Note

This repository contains the software component of a project to show custom messages on a Raspberry Pi (RPi) hooked up to an e-Print display (EPD), intended to act as a radio-controlled (RC) sticky note on an office door.

An important aspect of this project is that it is a testbed for using the blobman framework to construct the necessary software in as reproducible a fashion as can be managed.

Hardware

This project is currently executed on a Raspberry Pi 4 Model B, which turns out to be way overpowered for this application.

The EPD is a 680×374 pixel, 7.5", black-and-white display from Waveshare (SKU 13187). This particular model, the "V1" seems not to be available anymore — instead there are options with the same resolution but three colors, or still monochrome but at higher resolution (the "V2").

Software

There are two software components: a “hub” that runs on a persistent server, and a “display client” that runs on the RPi. Both are written in Rust. The Pi needs to be able to SSH into the hub server.

Step 1: Set up builder VM

In order for this project to work, we need to create a custom Raspberry Pi OS image that will be flashed onto an SD card. Creating this image requires some low-level custom Linux-y work, so we do the operations inside a Vagrant virtual machine (“box”) for reproducibility.

The basis for the builder box is the 20200116.1 version of the ubuntu/bionic64 Vagrant box. This can be initialized reproducibly with:

blobman provide bionic-server-cloudimg-amd64-vagrant.box
vagrant box add --name ubuntu_bionic64_20200116.1 bionic-server-cloudimg-amd64-vagrant.box

Running vagrant up will initialize and provision the builder box. TODO: the provisioning uses Apt commands and therefore touches the network! The goal is to get blobman infrastructure such that we can provision the box even when running fully offline, but we're not there yet.

Step 2: Configuration

Various configuration files need to be set up in the directory local/.

  1. Create an SSH keypair that has no protective passphrase. This will be used by the Pi to connect to the Hub. Run:
    ssh-keygen -t ed25519 -f local/stickynote_ed25519_key
    
  2. Configure the client to talk to the hub. Copy local/client-config.example.toml to local/client-config.toml and customize as appropriate for your hub setup, as described in comments in that file.
  3. Configure the Raspberry Pi OS image build. Copy local/pi-gen-config.example to local/pi-gen-config and customize as desired, as described in comments in that file.

Step 3: Building the Software

Building the software requires a Rust toolchain and the Rust cross tool.

To build the hub, run:

cargo build --bin rc_stickynote_hub --release

If your server runs an OS that's not fully compatible with your build machine (e.g., it runs an older version of glibc), you can cross-compile it if you'd like:

cross build --target x86_64-unknown-linux-musl --release

To cross-compile the display client for the RPi, run:

cross build --target armv7-unknown-linux-gnueabihf --release

Step 4: Build the RPi OS image

We build the Raspberry Pi OS image using scripts derived from the pi-gen tool used for the official RPi images. The forked scripts live in the semi-pi-gen/ directory.

To build the image, run:

vagrant ssh -c "cd /vagrant/semi-pi-gen && sudo STAGE_LIST='stage0 stage1 stage2' ./build.sh"

This will output the image file in semi-pi-gen/deploy/YYYY-MM-DD-rc-stickynote.img, where the YYYY-MM-DD corresponds to today’s date. This command does not automatically rebuild the custom software, so make sure to recompile beforehand if needed. The from-scratch build process takes about 20 minutes on my machine.

To rebuild a new image with updated configuration or stickynote executables, you can modify the STAGE_LIST in the above command to contain just stage2. Note, however, that this will reuse the existing filesystem tree, so operations should be idempotent. To avoid this, add CLEAN=1 PREV_ROOTFS_DIR=/pigenwork/stage1/rootfs to the build.sh environment, where PREV_ROOTFS_DIR specifies the rootfs directory of the previous stage (e.g., stage1 if you're just rerunning stage2).

To write the image to an SD card, first insert the card into your machine. If it already contained an OS image, your computer might mount the partitions, which we definitely don't want! Ummount anything like /run/$USER/media/boot or /run/$USER/media/rootfs that appears.

Once that's done, the obvious GUI method didn't work for me and I don't feel like futzing around with this too much, so here's the command-line method to flash the disk. Make sure you specify the right device otherwise you might make your machine unbootable, etc., because we're writing data straight to a block device node!

sudo dd bs=4M if=semi-pi-gen/deploy/*-rc-stickynote.img of=/dev/mmcblk0 conv=fsync

If the dd seems to exit instantly in a suspicious fashion, you may have a stale /dev/mmcblk0 device. Try removing the SD card, deleting that device node, and re-inserting.

Testing: Simulator Client

To run a “simulator” version of the client that uses SDL to draw graphics to a window rather than the EPD, run:

cd displayer && cargo run --no-default-features --features=simulator -- client

This program will require a client configuration file, which should be placed in ~/.config/rc-stickynote-client/rc-stickynote-client.toml. This is the same file format as used in local/client-config.toml.

Testing: Checking the RPi OS image

To mount the RPi OS image on your (Linux) machine and poke around its filesystem, you can mount it with a loopback device. The only tricky part is that the image file is a partitioned disk image, not just a single filesystem partition, so you need to be able to tell the mount program the right offset into the disk image to find the filesystem.

The pi-gen build process outputs this information while creating the image. It will emit some output that looks like this:

[19:26:46] Begin /vagrant/semi-pi-gen/export-image/prerun.sh
/boot: offset 4194304, length 268435456
/:     offset 272629760, length 1635778560

In the above, the number 272629760 is the offset we want to know.

Alternatively, you can use fdisk. If you run fdisk -lu /path/to/stickynote.img, the output will look something like:

Disk stickynote.img: 1.8 GiB, 1908408320 bytes, 3727360 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x8319fb68

Device          Boot  Start     End Sectors  Size Id Type
stickynote.img1        8192  532479  524288  256M  c W95 FAT32 (LBA)
stickynote.img2      532480 3727359 3194880  1.5G 83 Linux

In this example, the blocksize is the 512 in the Units: line, and the offset in blocks is the "start" value, 532480, in the final output line. The offset in bytes is the product of these two numbers, 512 * 532480 = 272629760.

Once you have the offset, mounting the image is as simple as:

sudo mount -o loop,offset=$OFFSET /path/to/stickynote.img /tmp/img

About

A project to add a radio-controlled sticky note to my office door using a Raspberry Pi and a Waveshare e-Paper Display.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published