A dual-device production control system using Adafruit CircuitPython hardware for live sound effects and remote command execution.
cloudfx turns two Adafruit devices into a powerful sound effects and macro controller system:
- MacroPad RP2040: Physical 12-button soundboard with rotary encoder (local control)
- FunHouse ESP32-S2: Network-connected remote trigger via AdafruitIO (remote control)
Both devices act as USB HID keyboards, sending keystrokes to your computer to trigger sounds via AutoHotKey (or any automation software).
- Adafruit MacroPad RP2040
- Adafruit FunHouse ESP32-S2 (optional - for remote control)
- USB-C cables
- Computer running Windows/Mac/Linux
- WiFi network (for FunHouse only)
Download and install CircuitPython 10.0.3 on your devices:
FunHouse users: Update TinyUF2 bootloader to 0.33.0+ first! See CircuitPython documentation.
Download latest release:
git clone https://github.com/wchesher/cloudfx.git
cd cloudfxCopy these 3 files to the root of your MacroPad's CIRCUITPY drive:
cp macropad/code.py /Volumes/CIRCUITPY/code.py
cp shared/macros.json /Volumes/CIRCUITPY/macros.json
cp shared/macros_loader.py /Volumes/CIRCUITPY/macros_loader.pyRequired Libraries (install to /lib/ on device):
adafruit_macropad.mpyadafruit_hid/(folder)adafruit_display_text/(folder)adafruit_display_shapes/(folder)
Download from CircuitPython 10.x Library Bundle.
Copy these files to FunHouse's CIRCUITPY drive:
cp funhouse/code.py /Volumes/CIRCUITPY/code.py
cp shared/macros.json /Volumes/CIRCUITPY/macros.json
cp shared/macros_loader.py /Volumes/CIRCUITPY/macros_loader.py
cp funhouse/settings.toml.example /Volumes/CIRCUITPY/settings.toml
# Edit settings.toml with your WiFi and AdafruitIO credentialsRequired Libraries (install to /lib/ on device):
adafruit_hid/(folder)adafruit_requests.mpyadafruit_io/(folder)adafruit_display_text/(folder)adafruit_dotstar.mpy
Download from CircuitPython 10.x Library Bundle.
Edit settings.toml on your FunHouse:
# WiFi Configuration
CIRCUITPY_WIFI_SSID = "YourWiFiName"
CIRCUITPY_WIFI_PASSWORD = "YourWiFiPassword"
# AdafruitIO Credentials (get from io.adafruit.com)
AIO_USERNAME = "your_username"
AIO_KEY = "your_aio_key"
# Static IP (optional - comment out for DHCP)
STATIC_IP = "192.168.1.100"
GATEWAY = "192.168.1.1"
NETMASK = "255.255.255.0"
DNS = "8.8.8.8"
# Polling Configuration
POLL_INTERVAL = "2" # seconds between AdafruitIO checksCopy your WAV files to a folder (e.g., C:\fx\ on Windows) and update your AutoHotKey script to play them.
Example sound files are in fx/ folder! Copy them to your sound directory.
Create an AHK v2 script to play sounds when keystrokes are received:
#Requires AutoHotkey v2.0
#SingleInstance Force
SoundDir := "C:\fx\"
; SHIFT+ESCAPE: Stop playback (encoder button)
+Escape:: {
SoundPlay(SoundDir . "off.wav")
}
; CTRL+ALT+SHIFT+F13: Play dj.wav (example)
^!+F13:: {
SoundPlay(SoundDir . "dj.wav")
}
; Add more hotkeys for each sound...See your deployed macros.json for all the key combinations!
The MacroPad loads 12 "pages" (apps) from macros.json. Rotate the encoder to switch between pages:
- EFFECTS - Sound effects (dj, crickets, dundun, etc.)
- UPBEAT - Positive sounds (rimshot, tada, applause, etc.)
- DOWNBEAT - Negative sounds (fail, wahwah, nope, etc.)
- RANDOM 1 - Random effects (Jetsons, psycho, train, etc.)
- RANDOM 2 - More random (Shrek, wilhelm scream, etc.)
- RICK 1 - Rick & Morty clips #1
- RICK 2 - Rick & Morty clips #2
- SONGS - Music clips (circus, imperial march, etc.)
- SPONGEBOB - Spongebob sounds
- STAR WARS 1 - Star Wars clips #1
- STAR WARS 2 - Star Wars clips #2
- JEOPARDY - Jeopardy music/effects
Each page has up to 12 buttons mapped to your 12 physical keys.
Encoder Button: Click the encoder to send SHIFT+ESCAPE (stops playback).
Encoder Rotation: Turn left/right to navigate between pages.
Send commands to your FunHouse via AdafruitIO:
- Create an AdafruitIO account at io.adafruit.com
- Create a feed named "macros"
- Send command names (like "dj", "rimshot", "intro") to the feed
- FunHouse receives the command and triggers the keystroke
- Your AutoHotKey script plays the sound
Command List: See macros.json - the "command" field is what you send to AdafruitIO.
cloudfx/
├── README.md # This file
├── LICENSE # MIT License
│
├── macropad/ # MacroPad RP2040 code
│ └── code.py # Main program
│
├── funhouse/ # FunHouse ESP32-S2 code
│ ├── code.py # Main program
│ └── settings.toml.example # WiFi/AdafruitIO config template
│
├── shared/ # Shared between both devices
│ ├── macros.json # 🎯 SINGLE SOURCE OF TRUTH - all macros defined here
│ └── macros_loader.py # JSON parser for both devices
│
└── fx/ # Example WAV sound files
├── dj.wav
├── rimshot.wav
└── ... (145+ sound files)
Everything is defined in shared/macros.json:
- MacroPad button labels, colors, and keycodes
- MacroPad page names and order
- FunHouse command names and keycodes
- Encoder button behavior
{
"apps": [
{
"name": "EFFECTS",
"buttons": [
{
"label": "dj",
"command": "dj",
"color": "0x17A398",
"keycodes": ["LEFT_CONTROL", "LEFT_ALT", "LEFT_SHIFT", "F13"]
}
]
}
]
}- label: Button text on MacroPad (5-7 chars max)
- command: Command name for FunHouse (sent to AdafruitIO)
- color: LED color in hex (MacroPad only)
- keycodes: HID keys to send (both devices)
- Edit
shared/macros.json - Add a new button entry with unique command name and keycodes
- Copy updated
macros.jsonto both devices - Add sound file to your sound directory (e.g.,
C:\fx\newsound.wav) - Add hotkey to AutoHotKey script
- Restart devices
✅ 12 programmable keys per page ✅ 12 pages (144 total sounds!) ✅ RGB LED indicators per key ✅ Rotary encoder for page switching ✅ Click encoder to stop playback (SHIFT+ESCAPE) ✅ OLED display shows current page name ✅ Screensaver after 30s inactivity (prevents LCD burn-in) ✅ Configurable LED brightness (30% default) ✅ Completely standalone (no WiFi needed) ✅ Symmetric encoder navigation (fixed in v1.0)
✅ Remote control via AdafruitIO ✅ 145+ commands available ✅ Fast 2-second polling (configurable) ✅ DotStar LED status indicators ✅ WiFi auto-reconnect (30s health checks) ✅ Display backlight control (turns off when idle) ✅ Synced display and LED timing ✅ Static or DHCP IP configuration ✅ Command queuing system (50 command buffer) ✅ Comprehensive error logging ✅ Memory monitoring and garbage collection ✅ HID error recovery ✅ Network resilience with auto-recovery
- Per-button RGB LEDs: Show page colors and button status
- Dimmed to 5%: During screensaver mode (after 30s idle)
- Full brightness (30%): When active
- Blue: Connecting to WiFi
- Green: Connected (shown during first 2 polls)
- Off: Normal operation (after startup)
- Magenta: Command received and executing (1 second flash, synced with display)
- Red: Error occurred
┌─────────────────┐ ┌──────────────────┐
│ MacroPad │ │ FunHouse │
│ (RP2040) │ │ (ESP32-S2) │
│ │ │ │
│ ┌───────────┐ │ │ ┌────────────┐ │
│ │ Physical │ │ │ │ AdafruitIO │ │
│ │ Buttons │ │ │ │ Listener │ │
│ │ (12 keys) │ │ │ │ (WiFi) │ │
│ └─────┬─────┘ │ │ └──────┬─────┘ │
│ │ │ │ │ │
│ ┌─────▼─────┐ │ │ ┌──────▼─────┐ │
│ │ JSON │ │ │ │ JSON │ │
│ │ Loader │ │ │ │ Loader │ │
│ └─────┬─────┘ │ │ └──────┬─────┘ │
│ │ │ │ │ │
│ ┌─────▼─────┐ │ │ ┌──────▼─────┐ │
│ │HID Output │ │ │ │ HID Output │ │
│ └─────┬─────┘ │ │ └──────┬─────┘ │
└────────┼────────┘ └─────────┼────────┘
│ │
│ USB │ USB
└───────────────────┬───────────────────┘
│
┌─────────▼──────────┐
│ Host Computer │
│ (AutoHotKey/AHK) │
│ Plays WAV Files │
└────────────────────┘
Both devices:
- Read
macros.jsonat startup - Convert keycode strings to HID codes
- Send HID keystrokes via USB
- Computer receives keystrokes
- AutoHotKey plays corresponding sound
They operate independently - no direct communication between them.
✅ Updated exception handling (traceback.print_exception)
✅ Modern settings.toml config (FunHouse)
✅ Compatible with CP 10.x Library Bundle
✅ JSON-based configuration (no Python import issues)
✅ Aggressive memory management
Both devices hold keys for 50ms before releasing - critical for AutoHotKey detection:
kbd.press(keycodes)
time.sleep(0.05) # 50ms hold
kbd.release_all()FunHouse (ESP32-S2):
- Aggressive garbage collection every 5 seconds
- Memory monitoring with warnings below 10KB free
- Loader deleted immediately after use
- Typical free memory: 30-50KB
MacroPad (RP2040):
- Simple memory management
- No aggressive GC needed (more RAM available)
- Typical free memory: 100-150KB
FunHouse includes comprehensive error handling:
- Full tracebacks printed to serial console
- WiFi auto-reconnect with exponential backoff
- HID error recovery (release all keys on failure)
- Display/LED operation failures don't crash the program
- KeyboardInterrupt handler logs diagnostic info
- Separate error handling for backlight control
MacroPad includes basic error handling:
- Safe screensaver wake logic
- Encoder position sync on startup
- Display refresh protection
MacroPad (macropad/code.py):
REGULAR_BRIGHTNESS = 0.3 # Regular LED brightness (30%)
SCREENSAVER_TIMEOUT = 30 # Seconds before screensaver (0 = disable)
DIM_BRIGHTNESS = 0.05 # LED brightness when dimmedFunHouse (funhouse/settings.toml):
POLL_INTERVAL = "2" # Seconds between AdafruitIO pollsFunHouse (funhouse/code.py):
DISPLAY_TIMEOUT = 1.0 # Seconds to show command name
GC_INTERVAL = 5 # Seconds between garbage collection
QUEUE_SIZE = 50 # Max commands in queue
WIFI_CHECK_INTERVAL = 30 # Seconds between WiFi health checks"NO MACROS" on display
- Missing
macros.jsonormacros_loader.py - Check serial console for errors
Keys don't work
- Verify CircuitPython 10.0.3 installed
- Check all required libraries in
/lib/folder - Connect to serial console to see errors
Encoder button doesn't stop playback
- Check AutoHotKey script has
+Escape::hotkey - Verify
off.wavexists in sound directory
Encoder navigation is weird
- Fixed in v1.0 - encoder position now syncs on startup
- Update to latest code
Screensaver not waking
- Fixed in v1.0 - screen_active flag set before display activation
- Update to latest code
Won't connect to WiFi
- Check credentials in
settings.toml - FunHouse only supports 2.4GHz WiFi (not 5GHz)
- Check serial console for detailed error messages
- WiFi will auto-reconnect every 30 seconds if disconnected
Commands don't trigger sounds
- Command name must match exactly (case-sensitive!)
- Check command exists in
macros.json - Serial console will say "✗ Command not found in macro list"
- Verify AutoHotKey is running and has matching hotkey
Display stays on all the time
- v1.0 includes backlight control
- Backlight turns off after 1 second when idle
- Update to latest code
Device crashes or freezes
- v1.0 includes comprehensive error handling
- Check serial console for error messages and tracebacks
- Look for memory warnings (below 10KB free)
- WiFi disconnections are now handled automatically
Verbose logging
- v1.0 includes detailed logging for all operations
- Connect to serial console to see:
- Poll activity and received commands
- Command execution steps (display, LEDs, HID)
- WiFi health checks
- Memory status and garbage collection
- Full error tracebacks
- Initial stable release
- Fixed encoder navigation asymmetry
- Added FunHouse display backlight control
- Synced FunHouse DotStar LEDs with display timing
- Comprehensive error handling and logging
- WiFi auto-reconnect with health checks
- Memory monitoring and aggressive garbage collection
- Improved screensaver wake logic
- Command queuing system
- Network resilience with auto-recovery
🎭 Live Production: Sound effects board for theater, podcasts, live streams 🎬 Content Creation: Quick sound effects while recording/editing 🎮 Streaming: Instant reactions and sound bites 📡 Remote Control: Trigger sounds from phone/tablet via AdafruitIO 🎵 DJing: Quick sound drops and effects 🎓 Education: Learn CircuitPython, USB HID, IoT integration
- Original MacroPad code: Phillip Burgess (Adafruit Industries)
- CloudFX modifications: William C. Chesher
- CircuitPython: Adafruit Industries and contributors
- Sound effects: Various sources (see individual files for attribution)
MIT License - See LICENSE file for details.
- Repository: https://github.com/wchesher/cloudfx
- CircuitPython: https://circuitpython.org/
- Adafruit MacroPad: https://www.adafruit.com/product/5128
- Adafruit FunHouse: https://www.adafruit.com/product/4985
- AdafruitIO: https://io.adafruit.com/
- AutoHotkey v2: https://www.autohotkey.com/
Issues? Check the troubleshooting section above first!
Still stuck? Open an issue: https://github.com/wchesher/cloudfx/issues
🎵 Happy sound boarding! 🎵