Mix tasks to automate the one-time setup and installation of
Nerves firmware on Kobo e-readers.
After the initial install, the standard mix firmware + mix upload
workflow handles ongoing development.
Stock Kobo e-readers run Linux from an internal eMMC. This package automates three things:
-
Blob extraction — proprietary binaries for e-ink and WiFi are copied from the running Kobo over SSH and baked into the Nerves rootfs overlay at build time, so the firmware image is self-contained.
-
Partition backup — the stock OS partition is streamed to the host machine via
ddover SSH, giving you a recovery image in case anything goes wrong. -
Init replacement — a small shell script (
init-nerves) replaces/bin/initon the Kobo. At boot it waits 10 seconds for a touchscreen tap; if detected, it loop-mounts the Nerves image from the onboard FAT32 storage, loadssquashfs.koandf2fs.ko, andpivot_roots into Nerves. If no tap is detected, the stock Kobo OS boots normally.
Each Kobo model is identified by a codename. Built-in devices:
| Codename | Model | SoC | Kernel |
|---|---|---|---|
spa_colour |
Kobo Clara Colour | MT8113 | 4.9.77 |
Contributions of new device manifests are welcome.
- A Kobo e-reader with SSH access (e.g. via a
KoboRoot.tgzhack that installs dropbear) - The Kobo connected to the same network as the build machine
- A Nerves project with a matching system for the device
sshpassinstalled if using password authenticationfwupinstalled for firmware image generation
Add kobo_firmware to your list of dependencies in mix.exs:
def deps do
[
{:kobo_firmware, github: "Spin42/kobo_firmware"}
]
endExtracts proprietary blobs from the stock Kobo into rootfs_overlay/ and
builds the initial firmware image:
MIX_TARGET=nerves_system_kobo_clara_colour \
mix kobo.setup --host 192.168.1.42 --device spa_colourOptions:
| Flag | Description | Default |
|---|---|---|
--host |
Kobo hostname or IP (required) | |
--device |
Device codename (required) | |
--password |
Root password for SSH | key-based auth |
--skip-blobs |
Skip blob extraction (reuse existing) | false |
--image-name |
Output image filename | nerves-kobo.img |
--overlay-dir |
Path to rootfs overlay directory | rootfs_overlay |
This produces a nerves-kobo.img file ready for deployment.
Backs up the Kobo's OS partition, uploads the firmware image and kernel
modules, and installs init-nerves as /bin/init:
MIX_TARGET=nerves_system_kobo_clara_colour \
mix kobo.install --host 192.168.1.42 --device spa_colourOptions:
| Flag | Description | Default |
|---|---|---|
--host |
Kobo hostname or IP (required) | |
--device |
Device codename (required) | |
--password |
Root password for SSH | key-based auth |
--image-name |
Firmware image filename | nerves-kobo.img |
--backup-dir |
Local directory for partition backups | backups |
--skip-backup |
Skip the partition backup step | false |
--skip-modules |
Skip uploading kernel modules | false |
After installation, reboot the Kobo:
ssh root@192.168.1.42 rebootTap the touchscreen within 10 seconds of boot when the led is blinking to start Nerves. Without a tap, the stock Kobo OS boots normally.
Once Nerves is running on the device, use the standard workflow:
mix firmware # build a new .fw file
mix upload # push to the running device via nerves_sshNo need to run mix kobo.setup or mix kobo.install again.
If the built-in manifest for your device is incomplete or wrong, you can override it from your project's config. Any keys you provide are merged on top of the built-in defaults for that codename:
# config/config.exs
config :kobo_firmware, :device, %{
codename: "spa_colour",
entries: [
%{remote: "/usr/bin/mdpd", overlay: "usr/bin/mdpd", type: :file},
%{remote: "/lib/my_custom_blob.so", overlay: "lib/my_custom_blob.so", type: :file}
# ...
]
}To add a completely new device that isn't built-in, provide the full device map:
config :kobo_firmware, :device, %{
codename: "my_kobo",
name: "Kobo Something",
kernel_version: "5.4.0",
root_partition: "/dev/mmcblk0p10",
onboard_partition: "/dev/mmcblk0p12",
touch_device: "/dev/input/event1",
kernel_modules: [
{"squashfs", "kernel/fs/squashfs/squashfs.ko"},
{"f2fs", "kernel/fs/f2fs/f2fs.ko"}
],
nerves_system: "nerves_system_my_kobo",
entries: [
%{remote: "/usr/bin/mdpd", overlay: "usr/bin/mdpd", type: :file}
# ...
]
}The built-in spa_colour manifest extracts these files:
E-ink display:
/usr/bin/mdpd/sbin/nvram_daemon/lib/libnvram.so/lib/libnvram_custom.so
WiFi:
/lib/firmware/*(wireless firmware blobs)/usr/bin/wmt_loader/usr/bin/wmt_launcher/etc/Wireless//etc/*wmt*,/etc/*WMT*(WMT configuration)
Everything deployed to the Kobo lives on the onboard FAT32 storage
(mounted at /mnt/onboard) under a .nerves/ directory:
/mnt/onboard/.nerves/
├── nerves-kobo.img # firmware image (loop-mounted at boot)
└── modules/<kernel-version>/kernel/fs/
├── squashfs/squashfs.ko
└── f2fs/f2fs.ko
The partition backup created by mix kobo.install is saved to backups/
on the host machine (timestamped, e.g. mmcblk0p10_20260309_143012.img).
This can be flashed back via fastboot if the device is bricked.
The stock /bin/init is also preserved as /bin/init.stock on the Kobo
during installation.
MIT