dance_party module

Dance Party Routine - Multi-UFO Synchronized Light Show via BLE.

This module implements a synchronized dance party routine where multiple Circuit Playground Bluefruit devices can synchronize their NeoPixel displays over Bluetooth Low Energy (BLE) using advertisement names as the communication protocol.

The routine supports two roles:
  • Leader: Audio-reactive display with beat detection (Mode 1)

  • Follower: Mirrors leader’s display in real-time (Modes 2-4)

Protocol Format:

ILLO_<seq>_<pos1>_<int1>_<col1>_<pos2>_<int2>_<col2>_<pos3>_<int3>_<col3>

Example

>>> from dance_party import DanceParty
>>> dance = DanceParty("ILLO_01", debug_bluetooth=True)
>>> dance.run(mode=1, volume=1)  # Leader mode with audio (volume: 0=off, 1=on)
Author:

Charles Doebler — Feral Cat AI

Dependencies:
  • adafruit_circuitplayground

  • adafruit_ble

  • audio_processor (optional)

Note

The “volume” parameter throughout this module is a sound enable flag (0=off, 1=on) rather than an actual volume control, since the Circuit Playground Bluefruit piezo speaker has no volume adjustment capability. The naming is maintained for consistency with the hardware switch and other routines.

class dance_party.DanceParty(device_name, debug_bluetooth=False, debug_audio=False)

Bases: BaseRoutine

Leader/follower visual synchronization over BLE advertisement names.

This class implements a synchronized light show routine where one device acts as a leader (audio-reactive) and others follow by mirroring the leader’s display.

The visual display consists of three pixels that form an animated baton:
  • Head pixel: Full intensity at current position

  • Trail1: 55% intensity, follows head by 1 step

  • Trail2/Spark: Temporary beat effect at 75% intensity

Audio processing features (leader mode):
  • Two-stage smoothing prevents visual jitter

  • Hysteretic color switching prevents rapid flickering

  • Beat detection triggers direction changes and visual effects

device_name

BLE device identifier

Type:

str

debug_bluetooth

Enable verbose BLE debugging output

Type:

bool

debug_audio

Enable verbose audio processing output

Type:

bool

sync_enabled

Whether BLE sync is enabled via config

Type:

bool

sync_active

Whether BLE is currently initialized and active

Type:

bool

ble

BLE radio instance (None if not initialized)

Type:

BLERadio

Class Attributes:

_NUM_PIXELS (int): Number of NeoPixels on the device (10) _BRIGHTNESS (float): Global brightness setting (0.0-1.0) _STEP_MS (int): Time between position updates in milliseconds _ADV_PERIOD_MS (int): BLE advertisement refresh rate in milliseconds _SCAN_BURST_S (float): Follower scan duration in seconds _LOSS_TIMEOUT_S (float): Follower timeout before declaring leader lost _MIN_RENDER_MS (int): Minimum time between render updates (rate limiting) _SMOOTH_ALPHA (float): Exponential smoothing factor for follower (0.0-1.0)

Example

>>> # Leader mode with debugging
>>> leader = DanceParty("LEADER_01", debug_bluetooth=True, debug_audio=True)
>>> leader.run(mode=1, volume=1)  # volume: 0=off, 1=on
>>> # Follower mode
>>> follower = DanceParty("FOLLOWER_02")
>>> follower.run(mode=2, volume=0)  # silent follower

Note

  • Mode 1 is always Leader

  • Modes 2-4 are always Follower (safe default for undefined modes)

  • Follower mode ignores audio input and mirrors leader visuals

  • Volume parameter is a sound on/off flag, not actual volume control

cleanup()

Clean shutdown of Dance Party resources.

Called by code.py when switching routines. Stops all BLE operations, clears NeoPixel display, and reports cleanup status if debugging.

Note

  • Safe to call multiple times

  • Catches and logs all exceptions during cleanup

  • Always attempts to clear pixels even if BLE cleanup fails

enable_bluetooth()

Enable Bluetooth if not already active.

Returns:

True if BLE is active after call, False otherwise

Return type:

bool

Note

This method is called by code.py to enable BLE dynamically. Safe to call multiple times - idempotent operation.

get_debug_status()

Return current status for debugging and monitoring.

Returns:

Status dictionary with the following keys:
  • sync_active (bool): Whether BLE is initialized

  • seq (int): Current sequence number

  • free_memory (int): Available memory in bytes

  • sync_success (int): Successful sync count (follower)

  • sync_fail (int): Failed sync count (follower)

  • last_seen_age (float): Seconds since last leader packet (follower)

Return type:

dict

Example

>>> dance = DanceParty("ILLO_01")
>>> status = dance.get_debug_status()
>>> print(status['free_memory'])
45632
run(mode, volume)

Main execution loop for Dance Party routine.

Parameters:
  • mode (int) – Determines role. 1=Leader, 2-4=Follower (safe default)

  • volume (int) – Sound enable flag (0=off, 1=on). Note: Called “volume” for consistency with hardware switch, but acts as boolean since piezo speaker has no actual volume control. Passed to audio processor for compatibility with other routines.

Note

  • Called repeatedly by main event loop in code.py

  • Leader mode: Draws audio-reactive visuals and broadcasts via BLE

  • Follower mode: Scans for leader and mirrors received visuals

  • Falls back to local audio visualization if BLE unavailable

Example

>>> dance = DanceParty("ILLO_01")
>>> while True:
...     dance.run(mode=1, volume=1)  # Leader with audio (sound enabled)
set_custom_responsiveness(adv_period_ms, smooth_alpha)

Set custom responsiveness parameters.

Parameters:
  • adv_period_ms (int) – Advertisement period in milliseconds (50-200)

  • smooth_alpha (float) – Smoothing factor 0.0-1.0 (higher = snappier)

Returns:

True if parameters were valid and applied

Return type:

bool

Example

>>> dance = DanceParty("ILLO_01")
>>> dance.set_custom_responsiveness(60, 0.92)  # Custom tuning
set_responsiveness(mode='balanced')

Adjust synchronization responsiveness with preset modes.

Parameters:

mode (str) – One of “fast”, “balanced”, or “smooth” - “fast”: Maximum responsiveness (50ms ads, 0.95 alpha) - “balanced”: Good balance (80ms ads, 0.90 alpha) [DEFAULT] - “smooth”: Smoother motion (120ms ads, 0.70 alpha)

Returns:

True if mode was applied successfully

Return type:

bool

Example

>>> dance = DanceParty("ILLO_01")
>>> dance.set_responsiveness("fast")  # Maximum responsiveness
>>> dance.set_responsiveness("smooth")  # Battery-saving smooth mode