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 | -| --- | --- | --- | --- | --- | --- | --- | --- | --- | -| ![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: 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 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| ![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: - 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