This project drives a Pimoroni Pico Inky Pack from this host computer over the Pico's USB serial port.
The host watches MPD for player events, reads the current track metadata with mpc, and sends artist/album/title text to the Pico. The Pico listens for simple serial commands and redraws the Inky display.
The system is split into two host-side processes plus one Pico-side script.
File:
/opt/inky/pico_serial_display.py
Deployed to the Pico as:
main.py
Responsibilities:
- listens on USB serial
- accepts simple text commands in
COMMAND:DATAformat - redraws the Inky display
- returns acknowledgments like
OK,PONG, orERR:...
Supported commands:
PINGTEXT:<text>TIME:<text>LINES:<title>|<line1>|<line2>STATUSCLEARSHOW
For the MPD display flow, the host mainly uses:
LINES:<artist>|<album>|<title>
File:
/opt/inky/mpd_watcher.py
Responsibilities:
- waits for MPD player events using:
mpc idleloop player
- fetches current metadata using:
mpc --format '%artist%\n%album%\n%title%' current
- filters metadata to ASCII only
- writes the latest seen track to:
/opt/inky/state/current-track.json
This process does not do throttling. It always writes the newest track state.
File:
/opt/inky/inky_updater.py
Responsibilities:
- reads the latest desired track from:
/opt/inky/state/current-track.json
- compares it against the last displayed track
- enforces a minimum delay between eInk updates
- sends updates to the Pico over USB serial
- coalesces rapid changes so only the newest pending update is eventually shown
This process owns the display timing logic.
The updater implements this logic:
- read the current desired track
- compare it to the last displayed track
- if unchanged:
- sleep briefly and check again
- if changed and enough time has passed since the last display update:
- update the Inky immediately
- if changed but not enough time has passed:
- store it as pending
- if more events arrive before the delay expires:
- replace the pending track with the newest one
- when the delay expires:
- display the pending track
This avoids excessive eInk refreshes while still ensuring the display catches up to the latest track.
File:
/opt/inky/config.json
Current fields:
{
"serial_port": "/dev/ttyACM0",
"min_update_interval_seconds": 120,
"poll_interval_seconds": 0.5,
"max_field_length": 40,
"state_dir": "/opt/inky/state"
}Meaning:
serial_port: Pico serial devicemin_update_interval_seconds: minimum time between actual Inky refreshespoll_interval_seconds: how often the updater checks state filesmax_field_length: maximum displayed length for artist/album/titlestate_dir: location of runtime JSON state files
Directory:
/opt/inky/state
Files:
current-track.json- latest track seen from MPD
pending-track.json- latest queued update waiting for throttle window to expire
displayed-track.json- most recently confirmed track sent to the Pico
last-update.json- timestamp of the last successful display update
These files make the system easy to inspect and recover after restarts.
Track metadata is filtered to ASCII characters before being written and displayed. This avoids issues with unsupported characters on the display path.
python3 /opt/inky/mpd_watcher.pypython3 /opt/inky/inky_updater.pyRun them in separate terminals.
Installed system services:
inky-mpd-watcher.serviceinky-updater.service
These are enabled at boot and start automatically.
Check status:
systemctl status inky-mpd-watcher.service
systemctl status inky-updater.serviceFollow logs:
journalctl -u inky-mpd-watcher.service -f
journalctl -u inky-updater.service -fForce an immediate display refresh:
rm -f /opt/inky/state/displayed-track.json /opt/inky/state/last-update.jsonThis clears the remembered display state and throttle timestamp, so the updater will redraw the current track on its next loop.
Restart services after code changes:
sudo systemctl restart inky-mpd-watcher.service inky-updater.serviceStop services:
sudo systemctl stop inky-mpd-watcher.service inky-updater.serviceDisable services:
sudo systemctl disable inky-mpd-watcher.service inky-updater.serviceUnit files:
/etc/systemd/system/inky-mpd-watcher.service/etc/systemd/system/inky-updater.service
File:
/opt/inky/send_command.py
Examples:
python3 /opt/inky/send_command.py "PING"
python3 /opt/inky/send_command.py "TEXT:Hello from host"
python3 /opt/inky/send_command.py "LINES:Now Playing|Artist Name|Track Title"File:
/opt/inky/send_time.py
Example:
python3 /opt/inky/send_time.pyFile:
/opt/inky/test_ack.py
Example:
python3 /opt/inky/test_ack.pyThe Pico Inky Pack uses these safe display settings in the current Pico script:
- white pen =
15 - black pen =
0 display.set_update_speed(2)
Using incorrect pen values can produce a mostly black or unreadable display.
- MPD changes track
mpd_watcher.pyreceives aplayerevent- watcher reads artist/album/title and writes
current-track.json inky_updater.pynotices the desired track changed- if allowed by the throttle window, it sends:
LINES:<artist>|<album>|<title>
- Pico receives the command and redraws the Inky display
- Pico replies
OK - updater records the new displayed state and timestamp
If updates happen too quickly, the newest desired track is stored in pending-track.json until the minimum interval expires.