A premium web-based controller for Pioneer CAC-V3000 / V3200 / V5000 / V180M commercial CD autochangers.
Control your Pioneer CD autochanger from any device on your network through a modern, mobile-first web interface. Built with Node.js and designed to run headless on a Raspberry Pi.
The CAC Controller turns a Pioneer CD autochanger into a network-controllable music server. It communicates via RS-232C serial protocol, provides a real-time web interface over WebSocket, and maintains a full CD library database with metadata from MusicBrainz.
Browser (Phone / Tablet / PC)
|
| HTTP + WebSocket (LAN/WLAN)
v
Node.js Server (Raspberry Pi) <--- Optional: Hub (central dashboard)
| connects via REST API + WebSocket
| RS-232C Serial (9600 baud, 8N1) using API key authentication
v
Pioneer CAC CD Autochanger
Each CAC Controller instance runs as a standalone Node. An optional Hub can connect to multiple Nodes across the network for centralized monitoring and control. Each Node remains fully functional on its own — the Hub is purely additive.
┌──────────────────┐
│ Hub (central) │
│ Dashboard App │
└───┬────┬────┬────┘
REST API + │ │ │ REST API +
WebSocket │ │ │ WebSocket
┌───────────────┘ │ └───────────────┐
v v v
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Node: Living │ │ Node: Studio │ │ Node: Bar │
│ Room (RPi) │ │ (RPi) │ │ (RPi) │
│ CAC-V3000 │ │ CAC-V5000 │ │ CAC-V180M │
└───────────────┘ └───────────────┘ └───────────────┘
- Dual Player Support - Independent control of both CD players (V3000/V3200/V5000)
- Full Protocol Implementation - All 27 execution commands and 14 query commands
- Real-time Status - Live updates via WebSocket for player mode, track number, elapsed time, and disc number
- Volume Control - Digital attenuator with 256 steps (0 to -80 dB)
- Speed Control - Playback speed adjustment from 90% to 110%
- Fade - Programmable volume fades with configurable duration
- Continuous Play - Automatic crossover between both players when a disc ends
- Gapless Play - Pre-cues the other player for seamless transitions between discs
- Shuffle - Three modes: current CD, both players, or the entire changer (random disc loading)
- Playlists - Server-side playlist engine with intelligent dual-player management, CD pre-loading, and drag-and-drop reordering
- SQLite Database - Persistent storage for all CD metadata, tracks, playlists, favorites, ratings, and play history
- MusicBrainz Integration - Search by artist, album, barcode, or disc TOC; automatic metadata and cover art download
- Cover Art - Upload custom covers (JPEG/PNG/WebP), client-side resize, or automatic download from Cover Art Archive
- JSON Import - Import existing CD databases with preview/edit UI, slot reassignment, mojibake repair, and format auto-detection
- Advanced Filtering - Filter by genre, artist, year, or label; sort by multiple criteria
- Search - Full-text search across all CDs and tracks
- Bulk Operations - Select mode with checkboxes for batch deletion
- CD Library Editor - Inline editing of all CD metadata (slot, title, artist, year, label, genre, notes) with searchable combobox dropdowns, 47 preset genres and 71 preset labels, custom values addable inline, recently used values at top, value management (rename/delete with bulk update), filter bar with "no value" option, sort controls, and cover thumbnails
- Single Slot Scan - Load a disc, read its TOC (track count, total duration), and catalog it
- Range Scan - Batch-scan any range of slots with real-time progress via WebSocket
- Full Scan - Scan all slots (up to 300 or 500 depending on model)
- Abort - Cancel a running scan at any time
- GPIO Power Control - On/off button to control a relay via configurable GPIO pin (e.g., to power the CAC unit). Uses
pinctrl— no additional packages required on Raspberry Pi - Favorites - Mark individual tracks or entire CDs as favorites; dedicated Favorites page with filter tabs (All / CD Favorites / Track Favorites)
- Ratings - 1-5 star rating system for tracks and CDs with filterable views
- Play History - Automatic tracking of all played tracks with timestamps and actual play duration
- Play Statistics - Top tracks, top CDs, top artists, genre distribution, and activity charts with configurable minimum play duration threshold
- Backup / Restore - Full database export and import as JSON (CDs, tracks, playlists, favorites, ratings, play history, settings); separate ZIP export/import for all cover images
- Cover Thumbnails - Cover art displayed in History, Favorites, Ratings, and Playlists sections
- Serial Terminal - Direct serial command interface for debugging and advanced control
- Bilingual UI - German and English, auto-detected from system locale or manually selectable
- Mobile PWA - Responsive design, installable on home screen, works in fullscreen
- Systemd Service - Runs headless on Raspberry Pi, auto-starts on boot, auto-reconnects serial port
| Model | Capacity | Players | Baud Rate | Volume | Speed |
|---|---|---|---|---|---|
| CAC-V3000 | 300 CDs | 2 | 9600 / 4800 | Yes | Yes |
| CAC-V3200 | 300 CDs | 2 | 9600 / 4800 | Yes | Yes |
| CAC-V5000 | 500 CDs | 2 | 9600 / 4800 | Yes | Yes |
| CAC-V180M | 18 CDs | 1 | 4800 | No | No |
CAC-V3000 DIP Switch: DIP switch 3 must be set to ON for 9600 baud. All DIP switches OFF = 4800 baud.
- Raspberry Pi (Zero W, Zero 2 W, 3, 4, or 5) or any Linux system
- USB-to-Serial adapter (RS-232C, e.g., FTDI FT232R or similar)
- Serial cable with 15-pin D-Sub connector (RS-232C) to the Pioneer CAC changer
- Network connection (WLAN or Ethernet)
The Raspberry Pi Zero W can be mounted inside the CAC chassis for a fully integrated solution:
- Power: HLK-PM01 AC-DC converter (230V→5V, 600mA) tapped from the AC inlet before the power switch, with T500mA fuse
- Serial: Direct TTL connection at the RSIF board input (handoff from MCDR board) — both boards are under the right side cover (viewed from front). Voltage divider (1kΩ/2kΩ) for 5V→3.3V level shifting on RXD; TXD (3.3V) connects directly (5V TTL threshold = 2.0V)
- Power Control: JQC-3FF-S-Z 5V relay module on GPIO17 to switch the CAC power (Active HIGH, 10kΩ pull-down)
- Network: Mini USB to USB-A female adapter + TP-Link TL-WN722N USB WiFi stick with SMA female connector, routed via SMA cable to an external antenna (the metal chassis acts as a Faraday cage)
See the detailed documentation for wiring diagrams and pin assignments.
- Node.js >= 18.x (20 LTS recommended)
- npm (included with Node.js)
- Build tools for native SQLite module:
build-essential,python3
git clone https://github.com/NFDiJee/cac-controller.git
cd cac-controller
npm install
npm startOpen http://<your-ip>:3000 in any browser.
See INSTALL.md for detailed step-by-step instructions including:
- Node.js and build tools setup
- Serial port permissions
- Systemd service configuration
- PWA installation on smartphones
- Troubleshooting guide
All settings are configurable via the web UI under More > Settings:
| Setting | Default | Description |
|---|---|---|
| Serial Port | /dev/ttyUSB0 |
Path to the serial port (auto-detected dropdown in settings UI) |
| Baud Rate | 9600 |
9600 for V3000/V3200/V5000, 4800 for V180M |
| Model | CAC-V3000 |
Your Pioneer changer model |
| Max Discs | 300 |
Maximum slot count (300 or 500) |
| Web Port | 3000 |
HTTP server port |
| Language | auto |
Auto-detect, German, or English |
| Stats Min Seconds | 30 |
Minimum play duration (seconds) for a track to count in statistics |
| GPIO Relay Pin | (empty) | BCM pin number for relay control (e.g., 17). Empty = disabled |
These settings enable a central Hub to discover, monitor, and control this Node remotely. All fields are optional — the controller works fully standalone without them.
| Setting | Default | Description |
|---|---|---|
| Node Name | (empty) | Display name for this node (e.g., "Living Room") |
| Room | (empty) | Room or location identifier |
| API Key | (empty) | Shared secret for Hub authentication (generate via UI) |
When an API key is set, the Hub can authenticate via X-API-Key header (or ?apikey= query parameter) and gains full access to all API endpoints — including library editing, playlists, scanner, settings, and everything else.
To use MusicBrainz metadata lookup, you must register your own application credentials:
| Setting | Description |
|---|---|
| App Name | Your application name (e.g., "MyCACController") |
| Version | Application version (e.g., "1.0") |
| Contact | Your email address (required by MusicBrainz ToS) |
The server exposes a full REST API for programmatic control:
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/player/:id/state |
Get player status |
POST |
/api/player/:id/play |
Start playback |
POST |
/api/player/:id/pause |
Pause playback |
POST |
/api/player/:id/stop |
Stop playback |
POST |
/api/player/:id/load |
Load disc { disc, track } |
POST |
/api/player/:id/eject |
Eject and return disc |
POST |
/api/player/:id/next |
Next track |
POST |
/api/player/:id/previous |
Previous track |
POST |
/api/player/:id/track/:num |
Play specific track |
POST |
/api/player/:id/volume |
Set volume { value: 0-255 } |
POST |
/api/player/:id/speed |
Set speed { value: 90-110 } |
POST |
/api/player/:id/fade |
Fade { target, duration } |
POST |
/api/player/:id/raw |
Send raw command { command } |
GET |
/api/player/states |
Get all player states |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/library |
List all CDs with tracks |
GET |
/api/library/:slot |
Get single CD |
PUT |
/api/library/:slot |
Update CD metadata |
DELETE |
/api/library/:slot |
Delete CD and covers |
POST |
/api/library/bulk-delete |
Delete multiple { slots: [...] } |
POST |
/api/library/bulk-update-field |
Bulk rename/delete field value { field, oldValue, newValue } |
POST |
/api/library/:slot/move |
Move CD to different slot { newSlot } |
POST |
/api/library/:slot/cover |
Upload cover (base64) |
GET |
/api/search?q=... |
Search library |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/musicbrainz/search?q=... |
Search releases |
GET |
/api/musicbrainz/release/:id |
Get release details |
POST |
/api/musicbrainz/apply/:slot |
Apply metadata to slot |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/playlists |
List all playlists |
POST |
/api/playlists |
Create playlist |
GET |
/api/playlists/:id |
Get playlist with items |
PUT |
/api/playlists/:id |
Update playlist |
DELETE |
/api/playlists/:id |
Delete playlist |
POST |
/api/playlists/:id/play |
Start playlist playback |
POST |
/api/playlists/stop |
Stop playlist |
PUT |
/api/playlists/:id/reorder |
Reorder items { itemIds: [...] } |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/stats |
Play statistics (top tracks, CDs, artists, genres, activity) |
DELETE |
/api/stats/reset |
Reset all play history |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/backup |
Export full database as JSON |
POST |
/api/backup |
Import database from JSON backup |
GET |
/api/backup/covers |
Export all cover images as ZIP |
POST |
/api/backup/covers |
Import covers from ZIP upload |
These endpoints require API key authentication (X-API-Key header). They provide convenience aggregations for Hub discovery and monitoring.
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/node/info |
Node info (name, room, model, stats, players) |
GET |
/api/node/status |
Live status (players, scanner, serial) |
Note: When authenticated with an API key, the Hub can access all endpoints listed above (library, playlists, scanner, settings, etc.) — not just the
/api/node/*routes. The API key middleware applies globally to all/api/*routes.
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/power/status |
Get power status { configured, on } |
POST |
/api/power/on |
Power on (GPIO HIGH) |
POST |
/api/power/off |
Power off (GPIO LOW) |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/favorites |
List favorites |
POST |
/api/favorites/toggle |
Toggle favorite |
GET |
/api/ratings |
List ratings |
POST |
/api/ratings |
Set/remove rating |
GET |
/api/history |
Play history |
GET |
/api/stats |
Library statistics |
GET/PUT |
/api/settings |
Get/update settings |
GET/PUT |
/api/playmodes |
Get/set play modes |
POST |
/api/import |
Import JSON data |
POST |
/api/scanner/scan |
Start scan |
POST |
/api/scanner/abort |
Abort scan |
Connect to ws://<host>:3000 for real-time updates.
Hub WebSocket: Connect to ws://<host>:3000/?hub=1&apikey=<your-key> for authenticated hub connections. Hub clients receive a hubInit message with node metadata on connection, plus all regular broadcast messages.
| Message Type | Direction | Description |
|---|---|---|
init |
Server -> Client | Initial state on connection |
hubInit |
Server -> Hub | Node info + initial state (hub connections only) |
playerState |
Server -> Client | Player status change |
scanProgress |
Server -> Client | Scan progress update |
scanComplete |
Server -> Client | Scan finished |
playModeChange |
Server -> Client | Play mode changed |
continuousSwitch |
Server -> Client | Automatic player switch |
playlistUpdate |
Server -> Client | Playlist state change |
playlistComplete |
Server -> Client | Playlist finished |
serialConnected |
Server -> Client | Serial port connected |
serialDisconnected |
Server -> Client | Serial port lost |
serialError |
Server -> Client | Serial communication error |
serialResponse |
Server -> Client | Raw serial response (for terminal) |
cac-controller/
├── server.js # Express + WebSocket server entry point
├── package.json
├── cac-controller.service # Systemd unit file
├── src/
│ ├── database.js # SQLite database (better-sqlite3)
│ ├── serial.js # Serial port connection with auto-reconnect
│ ├── protocol.js # Pioneer CAC serial protocol (commands, queries, parser)
│ ├── player.js # Player manager (polling, state, continuous/gapless/shuffle/playlist)
│ ├── scanner.js # CD scanner (TOC read, slot scanning)
│ ├── musicbrainz.js # MusicBrainz API + Cover Art Archive integration
│ ├── gpio.js # GPIO relay control (power on/off via pinctrl)
│ ├── routes.js # Express REST API routes
│ └── websocket.js # WebSocket manager (broadcasts state changes)
├── public/
│ ├── index.html # Single-page application
│ ├── css/
│ │ └── app.css # Full application styles (dark theme)
│ ├── js/
│ │ ├── app.js # Frontend application logic
│ │ └── i18n.js # Internationalization (DE/EN)
│ └── covers/ # Uploaded cover art images
├── data/
│ └── cac-controller.db # SQLite database (auto-created)
├── INSTALL.md # Raspberry Pi installation guide
├── LICENSE # MIT License
└── README.md # This file
The SQLite database contains the following tables:
- cds - CD metadata (slot, title, artist, year, genre, cover, MusicBrainz ID, barcode, label, country)
- tracks - Track metadata (title, artist, duration, ISRC)
- playlists / playlist_items - User-created playlists
- play_history - Automatic play tracking with duration measurement
- favorites - Favorited tracks and CDs
- ratings - 1-5 star ratings
- settings - Application configuration (key-value store)
This project implements the complete Pioneer CAC serial protocol as documented in the official Programming Manual (Version 2.0, May 1993).
- Baud Rate: 9600 (V3000/V3200/V5000) or 4800 (V180M)
- Data Bits: 8
- Stop Bits: 1
- Parity: None
- Terminator: CR (
\r) - Max Command Length: 20 characters
All commands are prefixed with the player ID and PS (Player Select):
<player_id>PS<command>\r
Example: 1PS001ZSPL\r selects player 1, loads disc 001, and starts playback.
- 27 Execution Commands: Player select, disc select/return, start, reject, play, pause, scan, search, stop marker, auto-cue, volume, speed, fade, and more
- 14 Query Commands: Player mode, disc number, track number, time code, TOC info, model name, disc status, and more
cd /opt/cac-controller
git pull
npm install
sudo systemctl restart cac-controllersudo systemctl stop cac-controller
sudo systemctl disable cac-controller
sudo rm /etc/systemd/system/cac-controller.service
sudo systemctl daemon-reload
sudo rm -rf /opt/cac-controllerMIT License - See LICENSE for details.
- Protocol documentation based on the official Pioneer CAC-V3000 Programming Manual (Version 2.0, May 1993)
- Inspired by the CCC project by Jonathan Dupre
- Uses MusicBrainz API for metadata lookup
- Uses Cover Art Archive for album artwork
This is an independent open-source project. It is not affiliated with, endorsed by, or connected to Pioneer Corporation or any of its subsidiaries. "Pioneer" and "CAC" are trademarks of Pioneer Corporation.


