Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 76 additions & 67 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,81 +1,90 @@
Date: 06.02.2022
Version: 2.0.3 - 20220206
Date: 11-2025

# oled_phoniebox
oled_phoniebox
Based on https://github.com/rm-hull/luma.oled
# oled_phoniebox on Bookworm

Tested with this display (affiliate Link): <a href="https://amzn.to/2SjPFPt" target="_blank">https://amzn.to/2SjPFPt</a>
This repository is a fork of splitti/oled_phoniebox (original project: https://github.com/splitti/oled_phoniebox) which provides OLED display support for the Phoniebox / RPi-Jukebox-RFID project using luma.oled.

| Start Screen | Play View Full | Play View Mixed | Play View Lite | Info Screen | Pause Screen | Play Screen | Vol Down Screen | Vol Up Screen |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| ![Caption](preview/Start_Screen.jpg "Caption") | ![Caption](preview/Play_View-full.jpg "Caption") | ![Caption](preview/Play_View-mixed.jpg "Caption") | ![Caption](preview/Play_View-lite.jpg "Caption") | ![Caption](preview/Info_Screen.jpg "Caption") | ![Caption](preview/Pause_Screen.jpg "Caption") | ![Caption](preview/Play_Screen.jpg "Caption") | ![Caption](preview/VolumeDown_Screen.jpg "Caption") | ![Caption](preview/VolumeUp_Screen.jpg "Caption") |

My finished Box on Youtube: <a href="https://youtu.be/GpHAbM0JIt4" target="_blank">Die drei ??? Kids Phoniebox</a>

Youtube Information Video: <a href="https://youtu.be/bEJN0BWdAXY" target="_blank">German Youtube Video</a>

Forum: <a href="https://forum-raspberrypi.de/forum/thread/41465-oled-display-fuer-die-phoniebox/" target="_blank">German Forum</a>

My Phoniebox Project (german only): <a href="https://splittscheid.de/phoniebox-bauanleitung-toniebox-alternative/" target="_blank">My Selfmade Phoniebox</a>

## Installation Steps

### Deactivate Buttons
It is possible, that the Display uses the same GPIO like the Shut-Command of the jukebox4kids. In this case, you should edit the file gpio-buttons.py like this
> sed -i -e "s:shut = Button(3, hold_time=2):#shut = Button(3, hold_time=2):g" ~/RPi-Jukebox-RFID/scripts/gpio-buttons.py

### Service Installation
Please notice:
This installer will create the directory oled_phoniebox in the home-Directory of the user. If you try to rename or move this folder, the service stops working.
Run the following code with the user pi for the installer:
**Why this fork?**

> cd; rm o4p_installer.sh; wget https://raw.githubusercontent.com/splitti/oled_phoniebox/master/scripts/install/o4p_installer.sh; chmod +x o4p_installer.sh; ./o4p_installer.sh
Root cause: Bookworm’s default gpio backend produces very-rapid spurious edges for your hardware that were interpreted as many presses; the previous version (Bullseye) used a different backend and did not show this behavior.
Fix applied to gpio-buttons.py (on-device): removed forced backend changes that caused the service to fail, and added a backend-agnostic software debounce wrapper for when_pressed handlers plus a small bounce_time — this prevents the sub-millisecond PRESSED/RELEASED bursts from firing multiple actions while leaving when_held behavior intact. After that change the service starts normally and the duplicate-presses are gone on your Bookworm device.
Display/I2C note: the display was disabled because I2C was off; the command sudo raspi-config nonint do_i2c 0 re-enabled I2C and restored the display. Therefor a a one-line call to the installer was added so the installer will enable I2C via raspi-config on systems where it was disabled.

### Contrast Control
You can add Contrast Control while editing the ~/RPi-Jukebox-RFID/scripts/gpio-buttons.py. You can find an example here: <a href="https://github.com/splitti/oled_phoniebox/tree/master/scripts/gpio-buttons" target="_blank">https://github.com/splitti/oled_phoniebox/tree/master/scripts/gpio-buttons</a>
Add a Button press and start these commands:
> python3 ../oled_phoniebox/scripts/contrast/contrast_up.py
> python3 ../oled_phoniebox/scripts/contrast/contrast_down.py

### Just another Feature
Press Prev- and Next-Button together for two or more seconds, and you get some Information about your WiFi and the installed oled_phoniebox-Version. If you press the buttons again, you can change the Display-Mode.

## Spend me a coffee
<a href="http://paypal.me/splittscheid" target="_blank">paypal.me</a>

## FAQ
How can i switch between lite or full version?
**Preview**
| Start Screen | Play View Full | Play View Mixed | Play View Lite | Info Screen | Pause Screen | Play Screen | Vol Down Screen | Vol Up Screen |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| ![Caption](preview/Start_Screen.jpg "Caption") | ![Caption](preview/Play_View-full.jpg "Caption") | ![Caption](preview/Play_View-mixed.jpg "Caption") | ![Caption](preview/Play_View-lite.jpg "Caption") | ![Caption](preview/Info_Screen.jpg "Caption") | ![Caption](preview/Pause_Screen.jpg "Caption") | ![Caption](preview/Play_Screen.jpg "Caption") | ![Caption](preview/Vol_Down_screen.jpg "Caption") | ![Caption](preview/Vol_Up_screen.jpg "Caption")

Installation (quick start)
--------------------------
Note: The installer will create the directory `oled_phoniebox` in the home directory of the user executing it. Renaming or moving this folder will break the service.

1. Deactivate conflicting buttons (if needed)
- Some displays may use the same GPIO pin as the jukebox4kids shutdown command. If so, edit gpio-buttons.py to comment out the shutdown button entry. For example:
> sed -i -e "s:shut = Button(3, hold_time=2):#shut = Button(3, hold_time=2):g" ~/RPi-Jukebox-RFID/scripts/gpio-buttons.py

2. Service installation (run as the user that runs Phoniebox, e.g. `pi`)
- Download and **run the installer script**. The installer will place the oled_phoniebox folder into your home directory:
> cd; rm o4p_installer.sh; wget https://raw.githubusercontent.com/jonaskor/oled_phoniebox/master/scripts/install/o4p_installer.sh; chmod +x o4p_installer.sh; ./o4p_installer.sh

- Important: Depending on the Phoniebox version and filesystem changes, running the installer again is currently the recommended way to update this addon (git pull may not work reliably due to install-time filesystem changes).

3. I2C and dependencies
- Ensure I2C is enabled on your Raspberry Pi (raspi-config -> Interface Options -> I2C).
- The installer will attempt to install required python packages and systemd service files. If the display service fails to start, check system logs and ensure i2c-dev is available (sometimes kernel modules load late; see FAQ below).

Contrast Control (optional)
- You can add hardware button control for contrast by editing `~/RPi-Jukebox-RFID/scripts/gpio-buttons.py` to call the contrast scripts when a button is pressed. Example commands:
> python3 ../oled_phoniebox/scripts/contrast/contrast_up.py
> python3 ../oled_phoniebox/scripts/contrast/contrast_down.py

Controls & Features
-------------------
- Lite and Full display modes (parent / kids mode) — toggle via the special button action (Prev+Next hold).
- Scrolling text support for long titles (letter scrolling).
- Play progress bar (0 - 100%).
- Wifi signal display and status screens (radio metadata when available).
- Shutdown image and improved shutdown handling.
- Contrast control scripts for hardware-driven contrast changes.
- Compatible with RPi-Jukebox-RFID (Phoniebox). Uses luma.oled for display rendering.

FAQ
---
How can I switch between lite or full version?
- Press and hold Next and Prev together for 2 seconds or more. The Information Screens will be displayed. Press and hold Next and Prev again for 2 seconds and the mode will be changed.

How should i update the oled_phoniebox?
- This time, the only way is to run the installer again, because there are different changes in the file-system. git pull does not work here!
How should I update the oled_phoniebox?
- At the moment, re-run the installer to update because some changes affect the filesystem layout. Running `git pull` in the installed folder may not apply all required installation steps.

Troubleshooting
- Service won't start: check `journalctl -u oled_phoniebox.service` (or the service name used by the installer) and make sure `i2c-dev` and required Python packages are available.
- Display shows nothing: confirm wiring, I2C enabled, and that the correct driver and address are configured.

## Thank you
Thank you / Credits
-------------------
Contributors and Testers:
- <a href="https://forum-raspberrypi.de/user/53531-uelly/" target="_blank">uelly</a>
- <a href="https://forum-raspberrypi.de/user/53205-carver/" target="_blank">carver</a>

Github:
Github projects referenced:
- <a href="https://github.com/splitti/oled_phoniebox" target="_blank">oled_phoniebox</a>
- <a href="https://github.com/rm-hull/luma.oled" target="_blank">Luma.Oled</a>
- <a href="https://github.com/rm-hull/luma.examples" target="_blank">Luma.Examples</a>
- <a href="https://github.com/MiczFlor/RPi-Jukebox-RFID" target="_blank">RPi-Jukebox-RFID also known as Phoniebox</a>

Image (music.png):
- https://pixabay.com/de/noten-musik-melodie-musiknote-2570451

Images (cardhand and musiccard) based on:
- https://pixabay.com/de/turm-mikrowelle-wireless-signal-34981/
- https://pixabay.com/de/klavier-noten-musik-klavier-musical-2460937/
- https://pixabay.com/de/hand-karte-halten-angabe-ticket-307636/
- https://pixabay.com/de/herunterfahren-macht-off-auf-icon-2935465/

Font:
- https://www.dafont.com/bitstream-vera-mono.font
- https://www.dafont.com/wifi.font

## Changelog
- <a href="https://github.com/MiczFlor/RPi-Jukebox-RFID" target="_blank">RPi-Jukebox-RFID (Phoniebox)</a>

Images and Fonts
- Image (music.png): https://pixabay.com/de/noten-musik-melodie-musiknote-2570451
- Images (cardhand and musiccard) based on:
- https://pixabay.com/de/turm-mikrowelle-wireless-signal-34981/
- https://pixabay.com/de/klavier-noten-musik-klavier-musical-2460937/
- https://pixabay.com/de/hand-karte-halten-angabe-ticket-307636/
- https://pixabay.com/de/herunterfahren-macht-off-auf-icon-2935465/
- Fonts:
- https://www.dafont.com/bitstream-vera-mono.font
- https://www.dafont.com/wifi.font

Changelog before fork
---------
- 06.02.2022 - 2.0.3 - 20220206
- Bugfix:
- https://github.com/splitti/oled_phoniebox/issues/9#issue-1125147086 fixed
Expand Down Expand Up @@ -109,11 +118,11 @@ Font:
- 26.06.2019 - Version 1.8.3 - 20190626
- Features:
- New Mixed Mode
- Special Button Hint optimized
- Shorter Duration Time of Status like Play, Vol Up/Down
- Special Button Hint optimized
- Shorter Duration Time of Status like Play, Vol Up/Down
- 31.01.2019 - Version 1.7.1 - 20190131
- Bugfix:
- The information on the radio stream was not displayed correctly: fixed (Issue: <a href="https://github.com/splitti/oled_phoniebox/issues/2#issuecomment-456209931">https://github.com/splitti/oled_phoniebox/issues/2#issuecomment-456209931</a>)
- The information on the radio stream was not displayed correctly: fixed (Issue: <a href="https://github.com/splitti/oled_phoniebox/issues/2#issuecomment-456209931">https://github.com/splitti/oled_phoniebox/issues/2</a>)
- Fixing some small errors
- Features:
- Mute Image
Expand All @@ -128,7 +137,7 @@ Font:
- Button Replacement
- Features:
- New updated Scrolling (Letter Scrolling)
- Wifi gets a new view
- Wifi gets a new view
- 11.01.2019 - Version 1.3.6
- Bugfixing
- Code optimized
Expand Down
123 changes: 71 additions & 52 deletions scripts/gpio-buttons/gpio-buttons.py
Original file line number Diff line number Diff line change
@@ -1,99 +1,118 @@
#!/usr/bin/python3
from gpiozero import Button
from signal import pause
from subprocess import check_call
#!/usr/bin/env python3
"""
GPIO buttons for OLED Phoniebox — Bookworm-safe version.

This file does NOT force a gpiozero pin factory (no RPiGPIOFactory forcing).
It adds a software debounce wrapper for when_pressed handlers so the rapid
spurious edges produced by some backends are filtered out while keeping
when_held behavior intact.
"""

import time
from time import sleep
from subprocess import check_call
from signal import pause

# This script will block any I2S DAC e.g. from Hifiberry, Justboom, ES9023, PCM5102A
# due to the assignment of GPIO 19 and 21 to a buttons

# 2018-10-31
# Added the function on holding volume + - buttons to change the volume in 0.3s interval
#
# 2018-10-15
# this script has the `pull_up=True` for all pins. See the following link for additional info:
# https://github.com/MiczFlor/RPi-Jukebox-RFID/issues/259#issuecomment-430007446
#
# 2017-12-12
# This script was copied from the following RPi forum post:
# https://forum-raspberrypi.de/forum/thread/13144-projekt-jukebox4kids-jukebox-fuer-kinder/?postID=312257#post312257
# I have not yet had the time to test is, so I placed it in the misc folder.
# If anybody has ideas or tests or experience regarding this solution, please create pull requests or contact me.
from gpiozero import Button

# Path to phoniebox/jukebox scripts (adjust if different in your install)
jukebox4kidsPath = "/home/pi/RPi-Jukebox-RFID"

def def_shutdown():
check_call(jukebox4kidsPath+"/scripts/playout_controls.sh -c=shutdown", shell=True)

# --- action functions ------------------------------------------------------
def def_volU():
check_call(jukebox4kidsPath+"/scripts/playout_controls.sh -c=volumeup", shell=True)
check_call(jukebox4kidsPath + "/scripts/playout_controls.sh -c=volumeup", shell=True)

def def_volD():
check_call(jukebox4kidsPath+"/scripts/playout_controls.sh -c=volumedown", shell=True)
check_call(jukebox4kidsPath + "/scripts/playout_controls.sh -c=volumedown", shell=True)

def def_vol0():
check_call(jukebox4kidsPath+"/scripts/playout_controls.sh -c=mute", shell=True)
check_call(jukebox4kidsPath + "/scripts/playout_controls.sh -c=mute", shell=True)

def def_next():
for x in range(0, 19):
if btn_next.is_pressed == True :
if btn_next.is_pressed:
sleep(0.1)
else:
check_call(jukebox4kidsPath+"/scripts/playout_controls.sh -c=playernext", shell=True)
check_call(jukebox4kidsPath + "/scripts/playout_controls.sh -c=playernext", shell=True)
break

def def_contrastup():
if btn_prev.is_pressed == True :
if btn_prev.is_pressed:
check_call("/usr/bin/touch /tmp/o4p_overview.temp", shell=True)
else:
check_call("/usr/bin/python3 /home/pi/oled_phoniebox/scripts/contrast/contrast_up.py", shell=True)

def def_contrastdown():
if btn_next.is_pressed == True :
if btn_next.is_pressed:
check_call("/usr/bin/touch /tmp/o4p_overview.temp", shell=True)
else:
check_call("/usr/bin/python3 /home/pi/oled_phoniebox/scripts/contrast/contrast_down.py", shell=True)

def def_prev():
for x in range(0, 19):
if btn_prev.is_pressed == True :
if btn_prev.is_pressed:
sleep(0.1)
else:
check_call(jukebox4kidsPath+"/scripts/playout_controls.sh -c=playerprev", shell=True)
check_call(jukebox4kidsPath + "/scripts/playout_controls.sh -c=playerprev", shell=True)
break

def def_halt():
for x in range(0, 19):
if btn_halt.is_pressed == True :
if btn_halt.is_pressed:
sleep(0.1)
else:
check_call(jukebox4kidsPath+"/scripts/playout_controls.sh -c=playerpause", shell=True)
check_call(jukebox4kidsPath + "/scripts/playout_controls.sh -c=playerpause", shell=True)
break

def toggle_display():
check_call("/home/pi/oled_phoniebox/scripts/toggle_display/toggle_display.sh", shell=True)

#btn_shut = Button(3, hold_time=2)
#btn_vol0 = Button(21,pull_up=True)
btn_volup = Button(7,pull_up=True,hold_time=0.3,hold_repeat=True)
btn_voldown = Button(13,pull_up=True,hold_time=0.3,hold_repeat=True)
btn_next = Button(8,pull_up=True,hold_time=2.0,hold_repeat=False)
btn_prev = Button(27,pull_up=True,hold_time=2.0,hold_repeat=False)
btn_halt = Button(12,pull_up=True,hold_time=2.0,hold_repeat=False)

#btn_shut.when_held = def_shutdown
#btn_vol0.when_pressed = def_vol0
btn_volup.when_pressed = def_volU
#When the Volume Up button was held for more than 0.3 seconds every 0.3 seconds he will call a ra$
# --- software debounce wrapper --------------------------------------------
# We use a timestamp filter keyed by pin number so the first event runs
# and further events within DEBOUNCE_MS are ignored. This is backend-agnostic.
_last_event = {}
DEBOUNCE_MS = 120 # milliseconds: tune between 60..200 depending on your tests

def debounce_by_pin(pin_number, fn, debounce_ms=DEBOUNCE_MS):
def wrapped(*a, **k):
now = int(time.time() * 1000)
last = _last_event.get(pin_number, 0)
if now - last < debounce_ms:
# ignore as bounce/duplicate
return
_last_event[pin_number] = now
try:
return fn(*a, **k)
except Exception:
# prevent exception from killing the service; let it be visible in journal
import traceback
traceback.print_exc()
return wrapped

# --- Buttons configuration -----------------------------------------------
# Keep pull_up and hold_time/hold_repeat behaviour from original script.
# We do NOT force a pin factory here (so it will use the distro/default one).
# You can increase bounce_time if desired, but the debounce wrapper will be the main defense.
btn_volup = Button(7, pull_up=True, hold_time=0.3, hold_repeat=True, bounce_time=0.01)
btn_voldown = Button(13, pull_up=True, hold_time=0.3, hold_repeat=True, bounce_time=0.01)
btn_next = Button(8, pull_up=True, hold_time=2.0, hold_repeat=False, bounce_time=0.01)
btn_prev = Button(27, pull_up=True, hold_time=2.0, hold_repeat=False, bounce_time=0.01)
btn_halt = Button(12, pull_up=True, hold_time=2.0, hold_repeat=False, bounce_time=0.01)

# Attach handlers: wrap only the when_pressed handlers (keep when_held direct)
btn_volup.when_pressed = debounce_by_pin(7, def_volU)
btn_volup.when_held = def_volU
btn_voldown.when_pressed = def_volD
#When the Volume Down button was held for more than 0.3 seconds every 0.3 seconds he will lower t$

btn_voldown.when_pressed = debounce_by_pin(13, def_volD)
btn_voldown.when_held = def_volD
btn_next.when_pressed = def_next

btn_next.when_pressed = debounce_by_pin(8, def_next)
btn_next.when_held = def_contrastup
btn_prev.when_pressed = def_prev

btn_prev.when_pressed = debounce_by_pin(27, def_prev)
btn_prev.when_held = def_contrastdown
btn_halt.when_pressed = def_halt

btn_halt.when_pressed = debounce_by_pin(12, def_halt)
btn_halt.when_held = toggle_display

pause()
pause()
2 changes: 2 additions & 0 deletions scripts/install/o4p_installer.sh
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,8 @@ else
echo -e " --> i2c arm_baudrate: ${green}set${nocolor}"
fi

# Ensure the I2C interface is enabled via raspi-config (fixes disabled display on some Bookworm installs)
sudo raspi-config nonint do_i2c 0 > /dev/null 2>&1

if [ -f /etc/modprobe.d/raspi-blacklist.conf ]; then
sudo sed -i 's/^blacklist spi-bcm2708/#blacklist spi-bcm2708/' /etc/modprobe.d/raspi-blacklist.conf
Expand Down