diff --git a/README.md b/README.md
index 0b4836e..7d6d186 100644
--- a/README.md
+++ b/README.md
@@ -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): https://amzn.to/2SjPFPt
+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 |
-| --- | --- | --- | --- | --- | --- | --- | --- | --- |
-|  |  |  |  |  |  |  |  |  |
-
-My finished Box on Youtube: Die drei ??? Kids Phoniebox
-
-Youtube Information Video: German Youtube Video
-
-Forum: German Forum
-
-My Phoniebox Project (german only): My Selfmade Phoniebox
-
-## 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: https://github.com/splitti/oled_phoniebox/tree/master/scripts/gpio-buttons
-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
-paypal.me
-
-## 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 |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- |
+|  |  |  |  |  |  |  |  | 
+
+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:
- uelly
- carver
-Github:
+Github projects referenced:
+- oled_phoniebox
- Luma.Oled
- Luma.Examples
-- RPi-Jukebox-RFID also known as Phoniebox
-
-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
+- RPi-Jukebox-RFID (Phoniebox)
+
+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
@@ -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: https://github.com/splitti/oled_phoniebox/issues/2#issuecomment-456209931)
+ - The information on the radio stream was not displayed correctly: fixed (Issue: https://github.com/splitti/oled_phoniebox/issues/2)
- Fixing some small errors
- Features:
- Mute Image
@@ -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
diff --git a/scripts/gpio-buttons/gpio-buttons.py b/scripts/gpio-buttons/gpio-buttons.py
index 62f620e..0977bec 100644
--- a/scripts/gpio-buttons/gpio-buttons.py
+++ b/scripts/gpio-buttons/gpio-buttons.py
@@ -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()
\ No newline at end of file
+pause()
diff --git a/scripts/install/o4p_installer.sh b/scripts/install/o4p_installer.sh
index 5a01c15..d4be3b0 100644
--- a/scripts/install/o4p_installer.sh
+++ b/scripts/install/o4p_installer.sh
@@ -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