/* * 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 (blue breath) * * 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 #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); 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); leds_apply_color(blend(s_from_color, to, t8)); leds_show(); if (t8 == 255) advance_step(to, now); }