Elixir library that initializes the e-ink display on Kobo e-readers running Nerves, so that fbink_nif can drive the screen.
Kobo devices (tested on the Clara Colour, MT8113 SoC) ship with
proprietary userspace daemons that must be running before the kernel's HWTCON
framebuffer is fully operational. Because these binaries live on the stock Kobo
root partition and cannot be redistributed, they are extracted from the
device's own eMMC at build time by
kobo_firmware's one-time
mix kobo.setup step and baked into the firmware image. This library then
starts the required services under OTP supervision at boot.
-
Starts display services in order:
- Mounts the init_bin partition which contains the waveform LUT and CFA LUT files needed by the hwtcon kernel driver
- Creates REGAL waveform device nodes in
/dev - Starts
mdpd(MediaTek Display Processing Daemon, supervised viaMuonTrap.Daemon) - Starts
nvram_daemon(NVRAM calibration/config service, supervised viaMuonTrap.Daemon)
-
Broadcasts events so your application knows exactly when
/dev/fb0is ready for FBInk.
Add kobo_eink to your dependencies in mix.exs:
def deps do
[
{:kobo_eink, github: "Spin42/ex_kobo_eink"}
]
endkobo_eink pulls in kobo_firmware
(for the device manifest) and muontrap
automatically.
The application starts automatically. Subscribe to get notified when the display is ready:
defmodule MyApp.Display do
use GenServer
def start_link(_), do: GenServer.start_link(__MODULE__, nil, name: __MODULE__)
@impl true
def init(nil) do
KoboEink.subscribe()
{:ok, :waiting}
end
@impl true
def handle_info({:kobo_eink, :ready}, _state) do
{:ok, fd} = FBInk.open()
FBInk.init(fd, %FBInk.Config{})
FBInk.print(fd, "Hello from Nerves!", %FBInk.Config{})
{:noreply, {:ready, fd}}
end
def handle_info({:kobo_eink, {:error, reason}}, _state) do
require Logger
Logger.error("E-ink init failed: #{inspect(reason)}")
{:noreply, {:error, reason}}
end
def handle_info({:kobo_eink, phase}, state) do
require Logger
Logger.info("E-ink init phase: #{phase}")
{:noreply, state}
end
endSubscribers receive {:kobo_eink, event} messages:
| Event | Meaning |
|---|---|
:starting_services |
Starting mdpd, nvram_daemon |
:services_started |
All services running |
:ready |
/dev/fb0 is ready for fbink_nif |
{:error, reason} |
Initialization failed |
If you subscribe after initialization has already completed (or failed), you immediately receive the current terminal state.
KoboEink.status()
# :ready | :initializing | :starting_services | {:error, reason}KoboEink.stop_services()Device-specific paths (init_bin partition, mount point, daemon binary paths)
are provided by KoboFirmware.Manifest, keyed by device codename. Configure
the codename in your Nerves project:
config :kobo_firmware, codename: "spa_colour"See KoboFirmware.Manifest for the per-device values and how to override
them.
This library is the Elixir/Nerves equivalent of the S20kobo-eink init script
from buildroot-kobo. The Kobo Clara
Colour uses a MediaTek MT8113 SoC with a kernel-level HWTCON driver that
provides /dev/fb0. However, the framebuffer isn't usable until several
proprietary userspace daemons are running.
Because the device uses Secure Boot, the proprietary daemons can't be freely
redistributed. Instead, mix kobo.setup (from
kobo_firmware) extracts them from
the stock Kobo root filesystem on the device's eMMC and bakes them into the
firmware image's read-only squashfs overlay, so they are already on the
filesystem when this library starts.
Daemons are started as MuonTrap.Daemon processes under
KoboEink.DaemonSupervisor (a DynamicSupervisor), giving proper OTP
supervision, automatic restart on crash, and clean shutdown without manual PID
tracking.
The initialization sequence:
Kernel (HWTCON + PMIC drivers built-in)
|
+-- /dev/fb0 exists (waveform loaded lazily on first update)
|
KoboEink.Init GenServer starts
|
+-- Mount init_bin partition (waveform + CFA LUTs)
+-- REGAL waveform device node setup
+-- mdpd -f (MuonTrap.Daemon)
+-- nvram_daemon (MuonTrap.Daemon)
|
+-- :ready -- /dev/fb0 is now usable by FBInk/fbink_nif
MIT License. See LICENSE for details.