Adam is a project that blends hardware and software to recreate a CD player-style interface on a Raspberry Pi running moOde audio player—where you can literally watch the seconds tick away as the music plays, see the status lights come alive to indicate different functions, and even check how many tracks are loaded or the total runtime of the current album/playlist. It’s all glowingly presented in the same room where your music is playing, without the need to scroll or unlock screens (like on a tablet or a phone). The setup features a spartan 4-digit 7-segment LED display, five indicator LEDs, and a single multi-function push button—just enough for that straightforward, track-and-time display we all know from traditional CD players.
I started—and still maintain—this project as a hobby while I was learning Python, driven by my interest in digital music and the desire for a device that shows time and track info the way I had in mind.
Adam has grown significantly along with my coding skills, surpassing the initial goal of a single script driving the TM1637 display. The various toggle scripts now manage key playback modes (random, repeat, single, consume) by lighting up the appropriate LEDs, while the display can easily switch between showing elapsed or remaining time (on the fly), total runtime, or even the number of tracks (stop mode). USB copy operations were also introduced via a dedicated script, and the multi-function button was expanded to handle short and long presses for different controls.
Delivering these features required fine-tuning real-time GPIO interactions and code structure—a process that offered invaluable learning experiences. In essence, Adam now provides the streamlined, no-distractions user experience I originally envisioned.
Working with GPIO on the Raspberry Pi does bring some limitations. Of its 40 pins, only four support hardware PWM, and two of those are typically used for I2S audio. This can complicate LED dimming if you also want to run a DAC over GPIO. Early on, I decided to use USB Audio output, which guided many of my design choices—like dedicating GPIO18 and GPIO19 to hardware PWM instead of I2S.
While exploring other options, I came across Python-compatible microcontrollers—especially the Raspberry Pi Pico (RP2040)—which are both affordable and flexible for handling PWM. This path lets you avoid compromising GPIO I2S audio on the Pi.
I also looked into using a TM1638 chip, which can control up to 16 LEDs, drive multiple 7-segment displays with adjustable brightness, and handle up to 24 buttons—all on one SOP28 chip. This greatly simplifies designs and frees up GPIOs, though it relies on specialized hardware rather than direct Pi I/O. Meanwhile, Python-capable microcontrollers like the Pico offer a balance between raw GPIO control and higher-level scripting via CircuitPython or MicroPython.
Adam’s code can be adapted for different uses, though it’s currently closely tied to the Pi’s GPIO. This reflects the hardware side of the project, which relies on a simple set of elements. One of my main goals was to gain practical experience working directly with GPIO pins. Even so, because of a detailed and user-friendly configuration file, it’s entirely possible to adapt the code to different pin layouts—or even port it to another hardware platform.
Recently, I’ve been leaning toward UART connectivity for more universal integration. Nearly all SBC music streamers support USB for both control and data, offering a simpler foundation to build upon while freeing up the Pi’s GPIO for more custom hardware expansions. There’s also a lot of potential in turning Adam’s interface into a dedicated USB peripheral—tools like CircuitPython make that surprisingly straightforward.
For now, development will likely focus on bug fixes or new ideas I pick up when I have time to refactor, while firmly keeping the original Adam hardware design for legacy purposes. Adam is a champion and will keep its rightful place, hopefully inspiring you to embark on your own projects. Building your own devices may be challenging, but it’s incredibly rewarding once everything comes together.
-
4-digit LED Display (TM1637)
- Track numbers (
-XX-
) - Total tracks (
XX--
) - Playback time (
MM:SS
) - Volume levels (
--XX
) - Multiple brightness levels
- Track numbers (
-
Status LEDs
- Hardware PWM-enabled indicators
- Synchronized brightness control
- Playback mode indicators
- USB operation status
- Multi-function Button
- Short press: Random playback
- Long press: System shutdown
- Quick Album Copy
- USB drive detection
- Directory structure preservation
- Progress display
- Error handling
- Real-time Configuration
- No restart required
- Dynamic display modes
- Adjustable brightness
- Customizable timings
- Hardware Setup
- GPIO Pinout
- Hardware Components
- Project Structure
- Configuration
- Features & Usage
- Service Installation
- Troubleshooting
- Raspberry Pi (tested on Model 4B)
- TM1637 4-digit 7-segment display
- 5x LEDs (4 PWM-capable + 1 standard)
- 1x Momentary push button
- Optional: Rotary encoder with push button
- Resistors and jumper wires
Raspberry Pi
+--------+
3.3V [1] | o o | [2] 5V
SDA1/GPIO2 [3] | o o | [4] 5V
SCL1/GPIO3 [5] | o o | [6] GND
GPIO4 [7] | o o | [8] GPIO14 (Display DIO)
GND [9] | o o | [10] GPIO15 (Display CLK)
GPIO17 [11] | o o | [12] GPIO18 (Single LED)*
GPIO27 [13] | o o | [14] GND
GPIO22 [15] | o o | [16] GPIO23 (Encoder A)
3.3V [17] | o o | [18] GPIO24 (Encoder B)
GPIO10 [19] | o o | [20] GND
GPIO9 [21] | o o | [22] GPIO25
GPIO11 [23] | o o | [24] GPIO8
GND [25] | o o | [26] GPIO7
GPIO0 [27] | o o | [28] GPIO1
GPIO5 [29] | o o | [30] GND
GPIO6 [31] | o o | [32] GPIO12 (Repeat LED)*
GPIO13 [33] | o o | [34] GND
GPIO19 [35] | o o | [36] GPIO16 (Consume LED)*
GPIO26 [37] | o o | [38] GPIO20 (Button)
GND [39] | o o | [40] GPIO21 (Copy LED)
+--------+
* Hardware PWM enabled pins
Important Warning: GPIO pins 18 and 19 conflict with I2S audio output. While these pins can be changed in the configuration, doing so will affect PWM functionality. If you change these pins, LED dimming (brightness control) through software is not recommended.
- Connections:
- CLK: GPIO 15
- DIO: GPIO 14
- VCC: 3.3V
- GND: GND
- Features:
- 4-digit 7-segment display
- Brightness control (0-7)
- Multiple display modes
- PWM-enabled LEDs:
- Repeat mode (GPIO 12)
- Random mode (GPIO 13)
- Single mode (GPIO 18)
- Consume mode (GPIO 19)
- Standard LED:
- Copy status (GPIO 21)
- Main button on GPIO 20
- Functions:
- Short press: Toggle play/pause
- Long press: System shutdown
- Double press: USB copy (when available)
adam-mpd/
├── config/ # Configuration files
│ └── settings.json # Main configuration file
│
├── docs/ # Documentation
│ └── manual.md # Technical manual
│
├── scripts/ # Utility scripts
│ ├── toggle_scripts/ # Display and playback mode toggles
│ │ ├── toggle_brightness.py
│ │ ├── toggle_display.py
│ │ ├── toggle_consume.sh
│ │ ├── toggle_random.sh
│ │ ├── toggle_repeat.sh
│ │ └── toggle_single.sh
│ ├── music_takeaway.py # USB copy utility
│ ├── roulette.sh # Random playback
│ ├── roulette_album.sh # Album-based random
│ └── shutdown.sh # System shutdown
│
├── src/ # Source code
│ ├── core/ # Core functionality
│ │ ├── config.py # Configuration management
│ │ └── mpd_client.py # MPD communication
│ │
│ ├── hardware/ # Hardware interfaces
│ │ ├── button/ # Button control
│ │ ├── display/ # TM1637 display driver
│ │ └── led/ # LED control with PWM
│ │
│ ├── service/ # Main services
│ │ ├── player_service.py # Main player logic
│ │ └── usb_copy_service.py # USB operations
│ │
│ ├── utils/ # Utilities
│ │ ├── logger.py # Logging system
│ │ └── storage.py # Storage operations
│ │
│ └── main.py # Application entry point
│
├── .gitignore # Git ignore rules
├── fix_permissions.sh # Permission fixing script
├── README.md # Project documentation
├── requirements.txt # Python dependencies
└── setup.py # Installation script
config.py
: Configuration management with real-time updatesmpd_client.py
: MPD client wrapper with connection handling
button/
: Button controller with multi-function supportdisplay/
: TM1637 LED display driver implementationled/
: PWM-enabled LED controller for status indicators
player_service.py
: Main service orchestrating display, LEDs, and MPDusb_copy_service.py
: Handles USB detection and music copying
logger.py
: Centralized logging systemstorage.py
: USB storage operations and file management
toggle_scripts/
: User interface control scriptsmusic_takeaway.py
: USB copy utilityroulette.sh
: Random playback scriptsshutdown.sh
: System shutdown handler
settings.json
: Centralized configuration for all components
"mpd": {
"host": "localhost", // MPD server address
"port": 6600 // MPD server port
}
Controls the connection to the MPD server. Used by MPDClient
in src/core/mpd_client.py
.
"gpio": {
"button": 20, // Main control button
"display": {
"clk": 15, // TM1637 clock pin
"dio": 14 // TM1637 data pin
},
"leds": {
"repeat": 12, // Repeat mode LED (PWM)
"random": 13, // Random mode LED (PWM)
"single": 18, // Single mode LED (PWM)
"consume": 19, // Consume mode LED (PWM)
"pwm": {
"frequency": 1000, // PWM frequency in Hz
"pins": [12, 13, 18, 19] // PWM-enabled pins
}
},
"encoder": {
"enabled": false, // Enable/disable encoder support
"pin_a": 23, // Encoder pin A
"pin_b": 24, // Encoder pin B
"poll_interval": 100, // Polling rate in ms
"speed_factor": 2, // Rotation sensitivity
"volume_step": 3 // Volume change per step
}
}
Used by hardware controllers in src/hardware/
. Note the PWM pins configuration for LED brightness control.
"display": {
"brightness": 4, // Default brightness level (0-7)
"brightness_levels": {
"led": [25, 50, 100], // LED brightness percentages
"display": [2, 4, 7] // TM1637 brightness levels
},
"mode": "elapsed", // Time display mode (elapsed/remaining)
"pause_mode": {
"blink_interval": 1 // Display blink rate when paused
},
"play_mode": {
"track_number_time": 2 // How long to show track number
},
"stop_mode": {
"stop_symbol_time": 2, // Duration of stop symbol
"track_total_time": 2, // Duration of track count
"total_time_display": 2 // Duration of total time
}
}
Controls display behavior in src/hardware/display/tm1637.py
and src/service/player_service.py
.
"timing": {
"command_cooldown": 1, // Delay between commands
"long_press_time": 2, // Time for long press detection
"update_interval": 1, // Display refresh rate
"volume_display_duration": 3 // How long volume shows
}
Used throughout the system for timing control, especially in PlayerService
.
"copy": {
"led": 21, // Copy status LED pin
"min_usb_size_gb": 4, // Minimum USB drive size
"destination_skip_folders": [ // Folders to ignore
"NAS",
"Music",
"Metal"
],
"path_structure": {
"min_depth": 4, // Minimum folder depth
"artist_level": 2, // Artist folder position
"album_level": 3, // Album folder position
"music_root": "/var/lib/mpd/music", // MPD music directory
"preserve_levels": [2, 3, 4] // Folder levels to keep
}
}
Controls USB copy functionality in src/service/usb_copy_service.py
.
"updates": {
"trigger": {
"file": ".update_trigger", // Update trigger file
"check_interval": 1, // Check frequency
"debounce_time": 0.1 // Update debounce time
}
}
Used by PlayerService
for real-time configuration updates.
"logging": {
"enable": true, // Enable/disable logging
"level": "DEBUG", // Log level
"format": "[{level}] {message}" // Log message format
}
Controls logging behavior in src/utils/logger.py
.
# Copy current playing album to USB
./scripts/music_takeaway.py
# Options:
# --dry-run Test run without copying
# --verbose Show detailed progress
# Toggle display brightness
./scripts/toggle_scripts/toggle_brightness.py
# Change display mode
./scripts/toggle_scripts/toggle_display.py
- Raspberry Pi (3B+ or 4B recommended)
- moOde audio player 8.3.9 or higher
- Python 3.7 or higher
- Git
- Internet connection for package installation
# Update system packages
sudo apt update
sudo apt upgrade -y
# Install required system packages
sudo apt install -y \
python3-pip \
python3-venv \
git
- Enable GPIO:
sudo raspi-config
# Navigate to: Interface Options
# Enable: GPIO
# Reboot when prompted
# Clone the repository
cd /home/pi
git clone https://github.com/yourusername/adam-mpd.git adam
cd adam
# Create and activate virtual environment
python3 -m venv venv
source venv/bin/activate
# Install Python packages
pip install -e .
# Verify installations
pip list | grep -E "python-mpd2|gpiozero|lgpio|psutil"
# Make scripts executable
sudo chmod +x fix_permissions.sh
sudo ./fix_permissions.sh
# Copy example configuration
cp config/settings.example.json config/settings.json
# Edit configuration
nano config/settings.json
- MPD connection details
- GPIO pin assignments
- Display brightness levels
- Button timing parameters
# Create service file
sudo nano /etc/systemd/system/adam.service
An example service configuration file is available at examples/adam.example.service
. You can copy it directly:
# Copy example service file
sudo cp examples/adam.example.service /etc/systemd/system/adam.service
Or create it manually with the following content:
[Unit]
Description=Adam for moOde
After=mpd.service
Requires=mpd.service
[Service]
User=pi
Group=pi
WorkingDirectory=/home/pi/adam
Environment=PYTHONPATH=/home/pi/adam
Environment=PATH=/home/pi/adam/venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
ExecStart=/home/pi/adam/venv/bin/python3 src/main.py
Restart=always
RestartSec=3
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Note: The example file includes important additional settings such as
Environment=PATH
andEnvironment=PYTHONPATH
which are essential for the service to work correctly with the Python virtual environment.
# Reload systemd
sudo systemctl daemon-reload
# Enable service
sudo systemctl enable adam
# Start service
sudo systemctl start adam
# Check status
sudo systemctl status adam
# View service logs
journalctl -u adam -f
# Check if service is running
systemctl is-active adam
- Display should show "--:--"
- LEDs should reflect MPD status
- Button should respond to presses
# Re-run permissions script
sudo ./fix_permissions.sh
# Check detailed service status
sudo systemctl status adam -l
# Check system logs
sudo journalctl -u adam -n 50 --no-pager
# Stop service
sudo systemctl stop adam.service
# Update repository
cd /home/pi/adam
git pull
# Update dependencies
source venv/bin/activate
pip install -e .
# Restart service
sudo systemctl start adam.service
This project is free to use and modify. Feel free to tinker and tailor it to your setup.
For additional support or bug reports, please visit the project's GitHub repository.