😮 You looked at the source!

Dimitris Zorbas

Code spells, smells and sourcery

An Unusual Pomodoro Timer on Elixir and Nerves

In my previous post about “Organising Book Highlights and Notes”, I wrote:

“Some day I may build a gadget for my desk to display a daily quote”.

A few days later, it’s on my desk and it couldn’t have been a better pretext to give Livebook on Nerves a try.

Finished Product


actual image of Brain
It's alive! 🧟

The case is not made of flesh, but working with clay is not my forte. I should buy a 3D printer at some point..

Goal

The scope of this weekend-project was to build some sort of smart desktop ornament. It should periodically display a random quote from my Kindle highlights and notes on an E Ink screen. The quote is fetched via bookworm 🪱📚. The display should refresh every 25 minutes and flash, marking the end of a time block.

As the title hints, this is an unusual pomodoro timer. It deviates from the standard pomodoro technique, but it suits me.

Name

A project needs a name. The obvious choice for something built on Nerves is no other than Brain 🧠!

And here’s a sample quote Brain would display:

The function of the brain and nervous system is to protect us from being overwhelmed and confused by this mass of largely useless and irrelevant knowledge, by shutting out most of what we should otherwise perceive or remember at any moment, and leaving only that very small and special selection which is likely to be practically useful.

Aldous Huxley, The Doors of Perception

The combination this name and Elixir’s fault tolerance reminds me this excellent cult horror film:

brain poster

Optional Features

Display the week number

I tend to make daily plans and thinking in terms of numbered weeks, it drives me to make them memorable.

Display the temperature outside

I tend to hit the gym after work, so this is mostly to inform me whether to wear shorts. (plot twist: I’ll wear shorts anyway).

Why E Ink?

The slow refresh rate of E Ink devices is ideal for something I’ll keep right in from of me. It can be useful even when powered off. It’s not even backlit and it doesn’t distract me at all. I was sceptical of having yet another “screen” to look at. I’ve even ditched my multi-monitor setup for a single monitor with the laptop’s monitor solely for messaging apps like Slack. Oh and the energy ⚡️ consumption is minimal, Pisugar could power it for a couple of days (untested assumption).

What you’ll need

  • Raspberry Pi 2 model B (that’s what I had lying around)
  • SD card
  • WiFi USB dongle (Raspberry Pi 2 doesn’t have on-board WiFi)
  • Pimoroni Inky wHat E Ink Display
  • A JSON file with notes to display (see bookwork)

You might notice there’s a camera on my device, it’s not required for the features discussed in this post. A future next one will be about it though, stay tuned!

Instructions

Follow the instructions below to build one yourself.

1. Burn the Nerves Livebook Firmware

Install prerequisites for packaging firmware images

MacOS

brew update
brew install fwup squashfs coreutils xz pkg-config

Linux (Debian)

sudo apt install build-essential automake autoconf git squashfs-tools ssh-askpass pkg-config curl

Then install nerves_bootstrap with:

mix archive.install hex nerves_bootstrap

You can find more detailed information about getting started with Nerves here.

Burn the Image

Follow the instructions on the nerves_livebook repo to burn the firmware using fwup or run the code below in your shell:

wget https://github.com/livebook-dev/nerves_livebook/releases/download/v0.2.26/nerves_livebook_rpi2.fw

# Mind to replace with your SSID and passphrase
sudo NERVES_WIFI_SSID='access_point' NERVES_WIFI_PASSPHRASE='passphrase' fwup nerves_livebook_rpi2.fw

2. Mount the E Ink Screen Hat on the Pi

mounted screen

At this point you can already power on your Pi.

Point your browser to http://nerves.local ..and voila!

livebook auth
The password is "nerves".

🙌 You have an easily accessible instance of Livebook running on a tiny computer. It runs in embedded mode by default which means that code evaluated in your notebooks runs in the context of the Livebook node.

This is ideal, as it allows us to redefine Scene modules from within a notebook and trigger a screen refresh without uploading firmware changes and rebooting the Pi. More about that further below.

3. Clone the Brain Repo

Hang tight, we’re halfway there, run:

git clone git@github.com:zorbash/brain.git

4. Add Some Notes

You can use bookwork to download your Kindle highlights in a format Brain understands or simply copy mine from https://github.com/zorbash/notes/blob/main/books.json and place them in priv/notes/.

5. Deploy! 🚀

Then run:

export MIX_TARGET=rpi2

mix deps.get
mix firmware
mix firmare.gen.script
./upload.sh livebook@nerves.local

When it asks for a password, type in nerves.

That’s it, a few moments later the screen should flash and your Brain will have come to life.

Making Changes

Brain is open-source and it’s easy to tweak it according to your needs. The UI is built using Scenic which makes it trivial to preview changes locally without re-uploading firmware.

Start Scenic to preview your changes:

MIX_ENV=dev MIX_TARGET=host iex -S mix scenic.run

You should see a window like this:

scenic preview

The Scene to modify is NervesLivebook.Scenes.Main and you can trigger an update with:

send :main_scene, :update

To trigger a screen refresh remotely run:

ssh livebook@nerves.local 'send :main_scene, :update'

💡 Since Brain runs Livebook (http://nerves.local), you can trigger an update by evaluating an Elixir cell with send :main_scene, :update in a notebook.

Debugging

You can connect to your Brain with:

ssh livebook@nerves.local

Then you can inspect the logs with:

RingLogger.next

For further debugging, check out toolshed which bundles a variety of helpful utility helpers.

use Toolshed
import IEx.Helpers

h(Toolshed)

Caveats

The Pimoroni E Ink screen afaik does not support partial refresh, it’d be cool to partially refresh the header (week, temperature, time) every minute.

Scenic’s text wrapping could support break-word, see scenic#118.

Future Enhancements

  • Get it to show notes per bookshelf, for example it could be configured to show only notes from the computer science bookshelf between 10 and 11 AM and from philosophy between 6 and 7 PM.

Livebook

Livebook is an incredible piece of technology, improving at a mind-blowing rate.

A couple of months ago I started work on an enhancement to be able to show the documentation for any module / function in a notebook. My intention was to expose a Livedoc.render(:plug) to fetch and render the documentation of the given hex package.

Thankfully, I was surprised to find out that this has already been implemented in livebook#453 🙌.

If you haven’t tried Livebook yet, please check out livebook.dev. You can even configure the default Livebook location where notebooks will open. I’ve set mine to http://nerves.local where Brain lives.

With your notes loaded in Brain, you can quickly browse and search them with the following notebook:

# Notes

<!-- livebook:{"livebook_object":"cell_input","name":"search","reactive":true,"type":"text","value":""} -->

```elixir
query = IO.gets("search: ") |> String.trim()
notes = NervesLivebook.Notes.all() |> Enum.filter(&String.contains?(&1.text, query))

Kino.DataTable.new(notes)

Reactive inputs make this possible.

Acknowledgements

This project wouldn’t be as enjoyable and painless without nerves, nerves_livebook, scenic, inky.

Thank you @mobileoverlord, @fhunleth, @boydm, and @lawik and all of the contributors to this amazing part of the Elixir ecosystem.