Arduino-compatible firmware for the DefCon 34 FirstBuild / GE Appliances washer/dryer badge. A 3-axis accelerometer drives an infinity-mirror LED ring so the illuminated "water" follows gravity with a damped circular response.
The current production target is an STM32G031F8P6 with an MXC6655XA accelerometer and eight SK6805 side-emitting RGB LEDs. The repository also keeps some of the earlier Adafruit KB2040 prototype profile and drivers.
- Gravity-responsive blue/cyan water effect with configurable lag and damping.
- Six button-selectable modes, including a power-saving Off mode.
- One-third-brightness dim mode.
- Motion wake, inactivity fallback, and LED sleep behavior.
- Sensor fault indication, periodic recovery, and a hardware watchdog.
- Production factory-test firmware for LEDs, orientation, and shake sensing.
- Build-time hardware profiles for the MCU, sensor, pins, axes, and LED ring.
| PlatformIO environment | Purpose | Status |
|---|---|---|
stm32g031f8_mxc6655xa |
Normal production firmware | Supported |
stm32g031f8_mxc6655xa_demo |
Demo firmware with a 3-hour inactivity timeout | Supported |
stm32g031f8_mxc6655xa_factory |
Production factory-test firmware | Supported |
The KB2040 environments in platformio.ini are currently
commented out. The prototype profile remains available for Arduino CLI/IDE use
or for developers who intentionally re-enable those environments. Running
pio run -e adafruit_kb2040_lsm6dsox as the repository stands will report an
unknown environment.
| Component | Configuration |
|---|---|
| MCU | STM32G031F8P6, Cortex-M0+, 64 MHz, 64 KB flash, 8 KB SRAM |
| Accelerometer | MXC6655XA, I2C, +/-2 g, fixed 100 Hz output |
| Sensor address | 0x15 with ADDR low; driver also tries 0x21 |
| LEDs | 8 x SK6805SIDE-G-003, GRB order, 800 kHz |
| LED backend | Adafruit NeoPixel |
| Programming | SWD with ST-Link or CMSIS-DAP |
| Native USB | None |
| MCU pin | Function |
|---|---|
| PB6 | I2C1 SCL |
| PB7 | I2C1 SDA |
| PA6 | LED data through the board's logic-level buffer |
| PA8 | MXC6655XA DRDY interrupt, reserved; firmware currently polls DRDY over I2C |
| PA11 | Active-low mode button and boot-time factory-test input |
| PA13 | SWDIO |
| PA14 | SWCLK |
| NRST | Reset/programming |
The production profile is firmware/defcon34_washer_badge/hardware_profiles/stm32g031f8_mxc6655xa.h. The custom PlatformIO board definition is boards/genericSTM32G031F8.json.
- Python 3 and PlatformIO Core, or the PlatformIO IDE extension for VS Code.
- An ST-Link or CMSIS-DAP SWD probe for production hardware.
- SWDIO, SWCLK, NRST, target ground, and an appropriate target supply connected according to the board schematic and probe documentation.
Install PlatformIO Core if it is not already available:
python3 -m pip install --user platformio
pio --versionPlatformIO downloads the STM32 platform, Arduino core, and Adafruit NeoPixel dependency on the first build.
# Build normal firmware
pio run -e stm32g031f8_mxc6655xa
# Upload normal firmware using the environment's default ST-Link protocol
pio run -e stm32g031f8_mxc6655xa -t upload
# Build or upload demo firmware
pio run -e stm32g031f8_mxc6655xa_demo
pio run -e stm32g031f8_mxc6655xa_demo -t upload
# Build or upload factory-test firmware
pio run -e stm32g031f8_mxc6655xa_factory
pio run -e stm32g031f8_mxc6655xa_factory -t upload
# Build every supported configuration
pio run -e stm32g031f8_mxc6655xa \
-e stm32g031f8_mxc6655xa_demo \
-e stm32g031f8_mxc6655xa_factoryTo use CMSIS-DAP, set upload_protocol = cmsis-dap in the selected environment
or its shared STM32 base before uploading.
Build artifacts are written below .pio/build/<environment>/, including
firmware.elf and firmware.bin.
To repeatedly program badges as they are connected to the SWD probe, run:
./scripts/program-badges.shThe script retries immediately when programming fails, such as when no badge is connected. After a successful upload, it waits 10 seconds before trying the next badge. Stop it with Ctrl-C.
Set a different post-success delay with --delay, and select another supported
build with --environment:
./scripts/program-badges.sh --delay 5
./scripts/program-badges.sh --environment stm32g031f8_mxc6655xa_factoryThe defaults can also be set with PROGRAM_SUCCESS_DELAY and
PROGRAM_ENVIRONMENT.
The prototype uses an Adafruit KB2040, an LSM6DSOX over STEMMA QT/I2C, and 12 WS2812B LEDs on GPIO 10. It uses FastLED and the Earle Philhower RP2040 core.
# Add and install the RP2040 Arduino core
arduino-cli config add board_manager.additional_urls \
https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
arduino-cli core update-index
arduino-cli core install rp2040:rp2040
# Install prototype libraries
arduino-cli lib install "FastLED"
# Build
arduino-cli compile \
--fqbn rp2040:rp2040:adafruit_kb2040 \
--build-property "build.extra_flags=-DPROFILE_ADAFRUIT_KB2040_LSM6DSOX=1 -DDEBUG_SERIAL_ENABLED=1" \
firmware/defcon34_washer_badge
# Upload after replacing <port> with the KB2040 serial device
arduino-cli upload \
--fqbn rp2040:rp2040:adafruit_kb2040 \
-p <port> \
firmware/defcon34_washer_badgeThe sketch folder can also be opened directly in Arduino IDE after installing the same core and libraries.
The active-low button is ignored for mode changes during the first second after boot so a boot-held button does not also change modes.
| Action | Result |
|---|---|
| Short press and release | Advance to the next LED mode |
| Hold for at least 3 seconds | Toggle one-third-brightness dim mode |
| Hold low while powering on | Request factory-test mode |
Modes cycle in this order:
Water: gravity-following blue/cyan water; this is the default.RainbowSpin: a rainbow rotates around the ring at 0.25 revolution/second.ColorFade: the full ring fades through hues at 0.2 cycle/second.OpposingSpin: two evenly spaced white spots rotate at 1 revolution/second.WhiteTest: all LEDs show white at the active brightness cap.Off: LEDs and accelerometer are shut down and the MCU sleeps between button checks. Press the button to wake directly intoWater.
WhiteTest can draw substantially more current than the water effect. Use a
power source and wiring suitable for all eight LEDs at the configured maximum
brightness.
- The application polls at 125 Hz. The production sensor produces data at 100 Hz, and gravity estimation advances only when a fresh sample is available.
- When the ring-plane gravity projection is too small, such as when the badge lies face-up, Water mode holds its last reliable direction instead of following sensor noise.
- Clear motion wakes sleeping LEDs and resets the inactivity timer.
- In the normal production build, a non-Water mode returns to Water after 30 seconds without activity. That transition restarts the inactivity timer.
- Water mode turns the ring off after 3 minutes without activity. Sensor polling continues at the normal cadence so movement can wake it.
- In the demo build, the selected mode is retained and the ring turns off after 3 hours without activity; the 30-second Water fallback is disabled.
- Off mode clears the LED ring, drives its data line low, powers down the accelerometer, and skips normal application work. The MCU sleeps between 20 ms button polls so the 2-second watchdog remains serviced; a button press resumes the sensor and returns directly to Water mode.
- The optional battery monitor would cap brightness at 30/255 below 3.3 V, but battery measurement is disabled in the current production and prototype profiles because no battery ADC input is configured.
- A 2-second watchdog resets the MCU after a runtime hang.
The stationary detector enters the IdleCycle state after approximately 12
seconds of stable, near-1 g readings. The controller calculates fill, agitate,
tumble, and pause phases, but the current SimpleWater renderer still uses the
measured gravity vector in both interactive and idle states. Automatic virtual
drum motion is therefore not currently visible in normal Water mode.
The recommended production path is to flash
stm32g031f8_mxc6655xa_factory, which defines both FACTORY_TEST and
DEBUG_SERIAL_ENABLED.
Factory test can be requested in three ways:
- Flash the factory-test environment; it enters the test automatically.
- Hold PA11 low while powering on any build.
- On a debug-serial build, send
testfollowed by a newline during the first 3 seconds after boot.
The sequence performs:
- Full-ring red, green, blue, and production-brightness blue checks.
- A single-LED white chase to verify order and soldering.
- A blue marker at the configured physical-bottom index.
- Five seconds of live gravity-follow orientation checking.
- Five seconds of shake detection; detected motion lights the ring cyan.
- Five seconds of sinusoidal idle-motion preview.
The test clears the ring when complete and returns to interactive operation.
Debug builds use 115200 baud and print the I2C scan, detected sensor address, watchdog status, mode changes, and raw/filtered angle data. The production MCU has no native USB CDC, and the current badge PCB does not route MCU UART to the SWD header. An ST-Link VCP connection alone therefore does not provide firmware logs; serial diagnostics require access to a routed MCU UART on the hardware.
If sensor initialization or an I2C read fails, LED 0 emits a slow red pulse. The firmware retries sensor initialization every 5 seconds and resumes normal operation after recovery.
For a persistent red pulse, check:
- Sensor power and ground.
- PB6/PB7 continuity and pull-ups.
- The MXC6655XA address strap (
0x15or0x21). - The selected hardware profile and sensor axis configuration.
Hardware-specific values live in
firmware/defcon34_washer_badge/hardware_profile.h
and its hardware_profiles/ headers. Animation and timing values live in
firmware/defcon34_washer_badge/behavior_config.h.
Tune hardware orientation first:
SENSOR_AXIS_MAP: map and invert sensor axes into badge coordinates.LED_INDEX_OFFSET: align logical angle zero with the physical ring.LED_CLOCKWISE: match logical rotation to physical LED order.GRAVITY_ANGLE_OFFSET_DEG: correct accelerometer/ring mounting rotation.
Then tune appearance and behavior:
MAX_BRIGHTNESS: profile-specific output limit.WATER_WIDTH_FRACTION: illuminated fraction of the ring.WATER_CYAN_AMOUNTandDRY_LED_BRIGHTNESS: water color and background.GRAVITY_LOWPASS_ALPHA: gravity filtering response.WATER_DIRECTION_STIFFNESSandWATER_DIRECTION_DAMPING: tracking lag and overshoot.SHAKE_THRESHOLD_G: motion sensitivity and wake behavior.- Mode speeds, sleep timeouts, factory-test timing, I2C timeout, and watchdog
timeout are also centralized in
behavior_config.h.
Use the factory orientation and bottom-marker steps after changing ring or axis settings. The broader design requirements are documented in docs/software-spec.md.
- Add a profile under
firmware/defcon34_washer_badge/hardware_profiles/. - Add its selector to
hardware_profile.h. - Implement the
MotionSensorinterface if the sensor is new. - Add a PlatformIO environment with the profile build flag and dependencies.
- Compile all supported environments and run the factory sequence on hardware.
Core behavior consumes badge-coordinate acceleration in units of g; sensor
library types and raw register formats should not cross the MotionSensor
interface.
| Symptom | Check |
|---|---|
UnknownEnvNamesError for a KB2040 environment |
The prototype PlatformIO environments are disabled; use Arduino CLI/IDE or intentionally re-enable them. |
| Upload cannot find the target | Check probe selection, target power/ground, SWDIO, SWCLK, NRST, and the configured upload protocol. |
| Slow red pulse on LED 0 | The accelerometer is unavailable; check I2C wiring, pull-ups, power, and address. |
| Water points to the wrong location | Calibrate SENSOR_AXIS_MAP, LED_INDEX_OFFSET, LED_CLOCKWISE, and GRAVITY_ANGLE_OFFSET_DEG. |
| Water follows rotation in the wrong direction | Check axis inversion first, then LED_CLOCKWISE. |
| Badge does not wake | Confirm motion exceeds SHAKE_THRESHOLD_G and that fresh sensor samples are available. |
| No serial output through ST-Link VCP | The production PCB does not route MCU UART to the SWD header. |
| LEDs flicker or reset the badge in WhiteTest | Check supply current capacity, grounding, LED power integrity, and the data-level buffer. |
Clean and rebuild after changing profiles or build flags:
pio run -t clean
pio run -e stm32g031f8_mxc6655xaboards/ Custom PlatformIO board definitions
docs/software-spec.md Product and firmware design specification
firmware/defcon34_washer_badge/ Arduino sketch and application modules
hardware_profiles/ Per-board compile-time configuration
sensors/ MotionSensor driver implementations
LICENSE Project license
THIRD_PARTY_NOTICES.md Dependency license notices
platformio.ini Build environments and dependencies
Production builds use the STM32duino Arduino core and Adafruit NeoPixel. Prototype builds use the Earle Philhower RP2040 Arduino core, FastLED, Adafruit LSM6DS, Adafruit BusIO, and Adafruit Unified Sensor. PlatformIO pins compatible dependency versions in platformio.ini.
