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
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:
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
At this point you can already power on your Pi.
Point your browser to http://nerves.local ..and voila!
🙌 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:
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 fromphilosophy
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.