ab2c1b34b4
- Rename all show .txt files with NNN_ numeric prefix so order is explicit and controlled by filename (001_heartbeat_red through 006_party) - Drop HOME_SHOW special-casing from convert_all.py; show 0 is simply the lowest-numbered file - Add SHOW_FLAG_SPARKLE support: shows can declare '// flags: sparkle' to overlay random white flashes on top of the base color each frame - Wire sparkle into led_controller and config.h (SPARKLE_CHANCE/FRAMES) - Replace old placeholder/example shows with the six production shows Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
106 lines
3.3 KiB
Arduino
106 lines
3.3 KiB
Arduino
/*
|
||
* Amirine Cosplay Lights
|
||
*
|
||
* Plays lightshows defined in shows.h on a WS2812B LED strip.
|
||
* A single button navigates between shows:
|
||
* 1 tap — next show
|
||
* 2 taps — previous show
|
||
* 3 taps — lights off (any tap or hold resumes)
|
||
* hold — reset to show 0 (red heartbeat)
|
||
*
|
||
* To add or update shows:
|
||
* 1. Add or edit a .txt file in converter/shows/
|
||
* 2. Run: make shows
|
||
* 3. Run: make upload
|
||
*
|
||
* Required libraries: FastLED, OneButton (Sketch > Include Library > Manage Libraries)
|
||
* SPDX-License-Identifier: BSD-2-Clause
|
||
*/
|
||
|
||
#include <OneButton.h>
|
||
#include "config.h"
|
||
#include "led_controller.h"
|
||
#include "shows.h"
|
||
|
||
// ---- Playback state ----------------------------------------------------
|
||
|
||
static uint8_t s_show_index = 0;
|
||
static ShowDef s_show; // PROGMEM cache for the active show
|
||
static uint16_t s_step_index = 0;
|
||
static uint32_t s_step_start = 0;
|
||
static CRGB s_from_color = CRGB::Black;
|
||
static bool s_lights_off = false;
|
||
|
||
static void load_show(uint8_t index) {
|
||
s_lights_off = false;
|
||
s_show_index = index;
|
||
s_show = read_show_def(&SHOWS[index]);
|
||
s_step_index = 0;
|
||
s_step_start = millis();
|
||
s_from_color = CRGB::Black;
|
||
}
|
||
|
||
// How far through the current step we are (0–255).
|
||
// Returns 255 immediately for instant steps (duration_ms == 0).
|
||
static uint8_t step_progress(const Step& step, uint32_t now) {
|
||
if (step.duration_ms == 0) return 255;
|
||
uint32_t elapsed = now - s_step_start;
|
||
if (elapsed >= step.duration_ms) return 255;
|
||
return (uint8_t)((elapsed * 255UL) / step.duration_ms);
|
||
}
|
||
|
||
// Complete the current step and advance to the next.
|
||
// SHOW_LOOP wraps back to step 0; SHOW_SINGLE loads the next show when the last step ends.
|
||
static void advance_step(CRGB reached_color, uint32_t now) {
|
||
s_from_color = reached_color;
|
||
uint16_t next = s_step_index + 1;
|
||
if (next >= s_show.length) {
|
||
if (s_show.mode == SHOW_SINGLE) {
|
||
load_show((s_show_index + 1) % SHOW_COUNT);
|
||
return;
|
||
}
|
||
next = 0;
|
||
}
|
||
s_step_index = next;
|
||
s_step_start = now;
|
||
}
|
||
|
||
// ---- Button ------------------------------------------------------------
|
||
|
||
static OneButton s_button(BUTTON_PIN, true, true); // active-low, enable pullup
|
||
|
||
// ---- Arduino entry points ----------------------------------------------
|
||
|
||
void setup() {
|
||
leds_begin();
|
||
load_show(0);
|
||
|
||
s_button.setClickMs(400);
|
||
s_button.setPressMs(800);
|
||
s_button.attachClick([]() { load_show((s_show_index + 1) % SHOW_COUNT); });
|
||
s_button.attachDoubleClick([]() { load_show((s_show_index + SHOW_COUNT - 1) % SHOW_COUNT); });
|
||
s_button.attachMultiClick([]() { if (s_button.getNumberClicks() == 3) { s_lights_off = true; leds_apply_color(CRGB::Black, false); leds_show(); } });
|
||
s_button.attachLongPressStart([]() { load_show(0); });
|
||
}
|
||
|
||
void loop() {
|
||
s_button.tick();
|
||
|
||
static uint32_t s_last_frame = 0;
|
||
uint32_t now = millis();
|
||
if (now - s_last_frame < 16) return;
|
||
s_last_frame = now;
|
||
|
||
if (s_lights_off) return;
|
||
|
||
Step step = read_step(&s_show.steps[s_step_index]);
|
||
CRGB to = CRGB(step.r, step.g, step.b);
|
||
uint8_t t8 = step_progress(step, now);
|
||
|
||
bool sparkle = (s_show.flags & SHOW_FLAG_SPARKLE) != 0;
|
||
leds_apply_color(blend(s_from_color, to, t8), sparkle);
|
||
leds_show();
|
||
|
||
if (t8 == 255) advance_step(to, now);
|
||
}
|