A magic keksbox (cookie box) that conjures a smile into every child's face.
The Keksbox is a homegrown music box that allows streaming of audio files/audiobooks to an external speaker. The usability is child-friendly, as it utilizes "keks" tags instead of traditional CDs or otherwise. Streaming to a loudspeaker instead of a headset lets us share fun and experience the audiobooks together. The box differentiates between a standard and custom audio set. The standard set should be "burned" to the device during setup, whereby the custom set can be adjusted at any time via a web-based portal. This allows parents to assign each "keks" a default audio file, and the children can overwrite it with their audio files.
The project aims to demonstrate that everybody with a basic background can develop nice products. Studying a programming language or software architecture for years is unnecessary. Instead, managing to empower Linux is sufficient to build starving products.
- kekz audio playback: kekz NFC tags compatible
- Minimal code approach: Utilizes existing Linux tools instead of custom software.
- Web-based file management: Includes a web interface for uploading and managing custom NFC tag files.
- Web-based system management: Includes a web interface to control the device via ssh.
- Lightweight architecture: Built entirely with standard Linux utilities.
- Place an NFC tag on the reader.
- The system will:
- Detect the tag using
libnfc
. - Look up the associated audio file.
- Play the audio using
play
from SoX.
- Detect the tag using
- Use the Filebrowser web interface to:
- Upload new audio files.
- Associate files with specific NFC tags.
- Manage your audio library.
- Raspberry Pi Zero 2W: Main processing unit
- PN 532 NFC Reader: Reading NFC keks/tags
- USB Audio: USB audio adatper
- USB-C Port: Power delivery port (with resistors)
- 3D-Printed Case: Case printed via 3d printer
- Raspberry OS - Operating system for Raspberry Pi Zero 2 w
- Sound eXchange - Simple audio player framework for terminal use
- filebrowser - Web-Based file browser (github project)
- libnfc - Linux package to read NFC tag
- i2c-tools - Linux package for i2c communication
- bash - Scripting and automation
This project does not aim to reveal or exploit any internal system structure of the kekz product. However, a german security research has demystified the headset in his blog post: [https://nv1t.github.io/blog/kekz-headphones/][https://nv1t.github.io/blog/kekz-headphones/]. This project builds on top of his research. Following his research allows you to extract the audiobooks from a headset and decode them into mp3 files.
If you want to have your own keksbox, please follow the steps below:
Before we start with the firmware, let's get the wiring done. At first, we will connect the NFC reader to the correct GPIO pins. By checking the PI's pinout we can conclude the following connections:
# NFC module pin -> Pi GPIO physical pin #
GND -> 6
VCC -> 4
SDA -> 3
SCL -> 5
Next, we connect the USB Audio device to the PI through a micro USB to USB type A connector. The final wiring is shown in the image below.
# TODO: image
Please install the Raspberry OS as explained in the official tutorial. This project used the x64 bit lite version
of the os hidden behind the Raspberry Pi Os (other)
option. Using the official image tool directly lets you set the hostname, user, and, most importantly, the wifi connection. This project used the following configuration:
- hostname: rehkakeks
- username: keksbox
- password: ks
- wifi: home specific
After flashing the image to the SD card, please insert it into the Raspberry Pi and try establishing an SSH connection from your host computer.
# the following code assumes that the keksbox owns the ip 192.168.0.101
$ ssh [email protected]
Linux rehkakeks 6.12.20+rpt-rpi-v8 #1 SMP PREEMPT Debian 1:6.12.20-1+rpt1~bpo12+1 (2025-03-19) aarch64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Mon Apr 14 21:43:56 2025 from 192.168.0.122
keksbox@rehkakeks:~ $
Congratulations, you have managed the tricky part.
Next, we will update the system and install all required packages. This is easy and should work like a charm. If you face any unknown packages
, you properly use a different version of the Raspberry OS. This tutorial used bookworm
. Please search the internet for the new name of the failed packages.
# Raspberry Os version check
keksbox@rehkakeks:~ $ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 12 (bookworm)
Release: 12
Codename: bookworm
# upgarde system
keksbox@rehkakeks:~ $ sudo apt update && sudo apt upgrade
# install i2c and nfc tools
keksbox@rehkakeks:~ $ sudo apt install i2c-tools libnfc6 libnfc-bin libnfc-examples
# install tools needed of keksbox
keksbox@rehkakeks:~ $ sudo apt install sox libsox-fmt-all tmux vim git
# install filebrowser (https://filebrowser.org/installation)
keksbox@rehkakeks:~ $ curl -fsSL https://raw.githubusercontent.com/filebrowser/get/master/get.sh | bash
# activate i2c -> Interface Options -> I2C -> Enable
keksbox@rehkakeks:~ $ sudo raspi-config
# activate usb sound -> System Options -> Audio -> Select USB
keksbox@rehkakeks:~ $ sudo raspi-config
After installing all the necessary tools we can start to configure the system. There is little to configure here, except creating some directories and systemd files for autostartup.
# create home data directory with subfolders
keksbox@rehkakeks:~ $ cd ~ && mkdir data && mkdir data/{custom,filebrowser,tmp}
# create basic filebrowser configuration file
keksbox@rehkakeks:~ $ cat > data/filebrowser/.filebrowser.yaml <<EOF
# set listen address
address: "0.0.0.0"
# enable no authentication feature
noauth: true
EOF
# add i2c nfc reader configuration
keksbox@rehkakeks:~ $ echo -e "\ndevice.name = \"PN532 over I2C\"\ndevice.connstring = \"pn532_i2c:/dev/i2c-1\"" | sudo tee -a /etc/nfc/libnfc.conf
# create system data direcotry with subfolders
keksbox@rehkakeks:~ $ cd /opt && sudo mkdir keksbox && sudo mkdir keksbox/{standard,system} && sudo ln -s /home/keksbox/data/custom /opt/keksbox/ && sudo chown -R keksbox:keksbox /opt/keksbox
# clone keksbox repository and distribute files
keksbox@rehkakeks:~ $ cd ~ && git clone https://github.com/whati001/keksbox.git && cp keksbox/keksbox.bash ./ && sudo cp keksbox/system_sounds/* /opt/keksbox/system/
keksbox@rehkakeks:~ $ sudo cp keksbox/unit_files/{filebrowser,keksbox}.service /etc/systemd/system/
keksbox@rehkakeks:~ $ sudo chown root:root /etc/systemd/system/filebrowser.service
keksbox@rehkakeks:~ $ sudo chown root:root /etc/systemd/system/keksbox.service
# reload systemd and enable new service
keksbox@rehkakeks:~ $ sudo systemctl daemon-reload
keksbox@rehkakeks:~ $ sudo systemctl enable keksbox.service
keksbox@rehkakeks:~ $ sudo systemctl enable filebrowser.service
Now we can veriy if everything works as expected by checking if both service are up and running:
# filebrowser service
keksbox@rehkakeks:~ $ sudo systemctl status filebrowser.service
● filebrowser.service - Keksbox FileBrowser
Loaded: loaded (/etc/systemd/system/filebrowser.service; disabled; preset: enabled)
Active: active (running) since Tue 2025-04-15 20:26:01 CEST; 3min 6s ago
Docs: https://github.com/whati001/keksbox
Main PID: 837 (filebrowser)
Tasks: 9 (limit: 178)
CPU: 222ms
CGroup: /system.slice/filebrowser.service
└─837 /usr/local/bin/filebrowser -r /home/keksbox/data/custom/
Apr 15 20:26:01 rehkakeks systemd[1]: Started filebrowser.service - Keksbox FileBrowser.
Apr 15 20:26:02 rehkakeks filebrowser[837]: 2025/04/15 20:26:02 Using database: /home/keksbox/data/filebrowser/filebrowser.db
Apr 15 20:26:02 rehkakeks filebrowser[837]: 2025/04/15 20:26:02 Using config file: /home/keksbox/data/filebrowser/.filebrowser.yaml
Apr 15 20:26:02 rehkakeks filebrowser[837]: 2025/04/15 20:26:02 Listening on [::]:8080
# keksbox service
keksbox@rehkakeks:~ $ sudo systemctl status keksbox
● keksbox.service - Keksbox Application
Loaded: loaded (/etc/systemd/system/keksbox.service; disabled; preset: enabled)
Active: active (running) since Tue 2025-04-15 20:29:34 CEST; 1s ago
Docs: https://github.com/whati001/keksbox
Main PID: 869 (keksbox.bash)
Tasks: 5 (limit: 178)
CPU: 114ms
CGroup: /system.slice/keksbox.service
├─869 /bin/bash /home/keksbox/keksbox/keksbox.bash
├─876 tmux new -d -s effect "play /opt/keksbox/system/connect.mp3"
├─881 play /opt/keksbox/system/connect.mp3
└─883 nfc-list -t 1
Apr 15 20:29:35 rehkakeks keksbox.bash[873]: nfc-poll uses libnfc 1.8.0
Apr 15 20:29:35 rehkakeks keksbox.bash[873]: NFC reader: PN532 over I2C opened
Apr 15 20:29:35 rehkakeks keksbox.bash[873]: NFC device will poll during 36000 ms (20 pollings of 300 ms for 6 modulations)
Finally, we can upload unencrypted audio files into the standard
directory. If you aim to mimic the original headset, please look at the Kekz Heatset section, which helps you to understand how to gain the original audio files.
The finally folder structure should look as follows:
/home/keksbox/
├── keksbox.bash # Keksbox application/script
└── data/ # Keksbox data directory (custom songs)
├── filebrowser/ # Filebrowser data dir
│ └── .filebrowser.yaml # Filebrowser configuration file
│ └── filebrowser.db # Filebrowser database file (runtime data)
└── custom/ # Custom songs directory
├── 0001/ # Custom song with id 0001
└── XXXX/ # Custom song with id XXXX
/opt/keksbox/
├── custom # Symlink to custom song directory
├── standard # Standard songs directory
│ ├── 0001/ # Standard song with id 0001
│ └── XXXX/ # Standard song with id XXXX
└── system # System songs directory
│ ├── connect.mp3 # System sound at connect
│ └── disconnect.mp3 # Standard sound at disconnect
The current status of the keksbox should work fine and stream all audiobooks like a charm. However, it still faces one critical problem: it is not power loss resilient. As a result, the box may stop working unexpectedly after being disconnected from power. The problem is that the Raspberry Pi runs an entire operating system, which does not like abrupt power loss and may corrupt the file system. However, we can make the device more resilient against this problem by making the filesystem read-only. Luckily, Linux comes to our rescue with the OverlayFs. So let's start:
We will add another partition that remains read-write to support custom songs in combination with a read-only filesystem. If this partition dies, the device will still play the standard songs, and we can recreate it. However, the custom songs will be gone.
Please insert the SD card into your computer and create a new ext4 partition. Use your favorite tool, I have used GParted. As you can see below, my SD card is 64 GB in size, and I have added a 20 GB custom sound partition (/dev/mmcblk0p3), which should be enough.
keksbox@rehkakeks:~ $ sudo fdisk -l /dev/mmcblk0
Disk /dev/mmcblk0: 59.48 GiB, 63864569856 bytes, 124735488 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: 0x35eacb59
Device Boot Start End Sectors Size Id Type
/dev/mmcblk0p1 8192 1056767 1048576 512M c W95 FAT32 (LBA)
/dev/mmcblk0p2 1056768 80211967 79155200 37.7G 83 Linux
/dev/mmcblk0p3 80211968 124735487 44523520 21.2G 83 Linux
Add a new line /etc/fstab
file to mount the newly created partition automatically. Please consider that this will delete the ~/data
directory and all its content. The new partition is referenced via the PARTUUID and is mounted to `/home/keksbox/data' with the default flags where attach time is disabled.
# append to fstab file
keksbox@rehkakeks:~ $ echo "PARTUUID=35eacb59-03 /home/keksbox/data ext4 defaults,noatime 0 1" | sudo tee -a /etc/fstab
# final result should look like the one below
keksbox@rehkakeks:~ $ cat /etc/fstab
proc /proc proc defaults 0 0
PARTUUID=35eacb59-01 /boot/firmware vfat defaults 0 2
PARTUUID=35eacb59-02 / ext4 defaults,noatime 0 1
PARTUUID=35eacb59-03 /home/keksbox/data ext4 defaults,noatime 0 1
We turn off system logging to minimize writes to the file system as much as possible. Thank you very much for this tutorial, which guides us step-by-step on how to turn off system logging.
# disable systemd journal storage to none
keksbox@rehkakeks:~ $ sudo vim /etc/systemd/journald.conf
# set: Storage: none
# vacuum old logs to a minium
keksbox@rehkakeks:~ $ sudo journalctl --rotate && sudo journalctl --vacuum-time=1s
# used storage should be a minimum
keksbox@rehkakeks:~ $ journalctl --disk-usage
Archived and active journals take up 16.0M in the file system.
# restart service to activate new settings and clean old stuff
keksbox@rehkakeks:~ $ sudo systemctl restart systemd-journald
keksbox@rehkakeks:~ $ sudo rm -R /run/log/journal/*
keksbox@rehkakeks:~ $ sudo rm -R /var/log/journal/*
# remove old bash history and make file immutable
keksbox@rehkakeks:~ $ cat /dev/null > ~/.bash_history
keksbox@rehkakeks:~ $ sudo chattr +i ~/.bash_history
keksbox@rehkakeks:~ $ history -c && history -w``
# reboot and verify that no system logs are written
keksbox@rehkakeks:~ $ sudo reboot
Finally, we will activate the overlayFS.
This can be done either via the rasp-config
command or directly via the terminal as shown below.
Please consider that the rasp-config
command enables the overlayFS recursive, meaning that your data partition will be also read only.
Therefore, please follow the instruction below:
# install overlay fs
keksbox@rehkakeks:~ $ sudo apt install overlayroot
# mount /boot/firmware partition in read-write mode
# -> this should be already done
# update /boot/firmware/cmdline.txt file to include: overlayroot=tmpfs,recurse=0 ....
keksbox@rehkakeks:~ $ sudo vim /boot/firmware/cmdline.txt
keksbox@rehkakeks:~ $ sudo cat /boot/firmware/cmdline.txt
overlayroot=tmpfs:recurse=0 console=serial0,115200 console=tty1 root=PARTUUID=35eacb59-02 rootfstype=ext4 fsck.repair=yes rootwait cfg80211.ieee80211_regdom=AT
# make boot partition only readable
keksbox@rehkakeks:~ $ sudo systemctl daemon-reload
keksbox@rehkakeks:~ $ sudo mount -o remount,ro /boot/firmware/
# reboot
keksbox@rehkakeks:~ $ sudo reboot
# verify mount points
keksbox@rehkakeks:~ $ mount
...
/dev/mmcblk0p2 on /media/root-ro type ext4 (ro,relatime)
tmpfs-root on /media/root-rw type tmpfs (rw,relatime)
overlayroot on / type overlay (rw,relatime,lowerdir=/media/root-ro,upperdir=/media/root-rw/overlay,workdir=/media/root-rw/overlay-workdir/_,uuid=on)
...
/dev/mmcblk0p1 on /boot/firmware type vfat (ro,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,errors=remount-ro)
/dev/mmcblk0p3 on /home/keksbox/data type ext4 (rw,noatime)
...
Congratulation, you have finished the keksbox setup.
The current setup allows you to play "standard" and custom sound by simply approaching a tag to the device. But we are still limited to wired speakers. The advanced configuration illustrates boosting your Keksbox and using Bluetooth speakers for playback. Please see here the Advanced Setup
This project is provided as is and was just a funny side project of mine. For now, the current state if final. However, I have the following steps in mine in case this turns out as a real project:
- Shield NFC reader and keksbox storage via aluminum foil
- Use PogoPin connectors instead of a cable between lid and base
- Stream to Classic BLE speakers and broadcast to BLE audio headsets (allows to stream the audio to multiple headsets simultaneously)
- Replace the RPI with a real embedded system to save money and gain BLE
Contributions are welcome! Feel free to open an issue or submit a pull request for any improvements or suggestions.
- The developers of
libnfc
. - The SoX audio tools team.
- The Filebrowser project.
- The Linux community for providing robust tools and utilities.
- nv1t for his amazing research