Files
bgrolleman ab2c1b34b4 Replace example shows with numbered production shows and add sparkle flag
- 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>
2026-05-24 13:59:25 +02:00

106 lines
3.3 KiB
Arduino
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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 (0255).
// 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);
}