Optimize
This commit is contained in:
@@ -12,43 +12,23 @@
|
||||
* 2. Run: make shows
|
||||
* 3. Run: make upload
|
||||
*
|
||||
* Required library: FastLED (Sketch > Include Library > Manage Libraries)
|
||||
* 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 "button.h"
|
||||
#include "shows.h"
|
||||
|
||||
// Milliseconds between LED updates. 16ms ≈ 60 refreshes/second.
|
||||
#define UPDATE_INTERVAL_MS 16
|
||||
|
||||
// ---- Color math --------------------------------------------------------
|
||||
|
||||
// Interpolate a single 8-bit channel from a to b by ratio t (0.0 – 1.0).
|
||||
static uint8_t lerp_channel(uint8_t a, uint8_t b, float t) {
|
||||
return (uint8_t)(a + (b - a) * t);
|
||||
}
|
||||
|
||||
// Blend two colors: t=0.0 returns 'from', t=1.0 returns 'to'.
|
||||
static CRGB blend_colors(CRGB from, CRGB to, float t) {
|
||||
return CRGB(
|
||||
lerp_channel(from.r, to.r, t),
|
||||
lerp_channel(from.g, to.g, t),
|
||||
lerp_channel(from.b, to.b, t)
|
||||
);
|
||||
}
|
||||
|
||||
// ---- Playback state ----------------------------------------------------
|
||||
|
||||
static uint8_t s_show_index = 0; // which show is active (index into SHOWS[])
|
||||
static ShowDef s_show; // active show, read from PROGMEM once on load
|
||||
static uint16_t s_step_index = 0; // current step within the active show
|
||||
static uint32_t s_step_start = 0; // millis() when this step began
|
||||
static CRGB s_from_color = CRGB::Black; // color at the start of this transition
|
||||
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;
|
||||
|
||||
// Load show at 'index', resetting playback to the first step.
|
||||
static void load_show(uint8_t index) {
|
||||
s_show_index = index;
|
||||
s_show = read_show_def(&SHOWS[index]);
|
||||
@@ -57,18 +37,18 @@ static void load_show(uint8_t index) {
|
||||
s_from_color = CRGB::Black;
|
||||
}
|
||||
|
||||
// How far through the current step we are (0.0 – 1.0).
|
||||
// Returns 1.0 immediately for instant steps (duration_ms == 0).
|
||||
static float step_progress(const Step& step) {
|
||||
if (step.duration_ms == 0) return 1.0f;
|
||||
uint32_t elapsed = millis() - s_step_start;
|
||||
float t = (float)elapsed / step.duration_ms;
|
||||
return (t < 1.0f) ? t : 1.0f;
|
||||
// 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) {
|
||||
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) {
|
||||
@@ -79,44 +59,40 @@ static void advance_step(CRGB reached_color) {
|
||||
next = 0;
|
||||
}
|
||||
s_step_index = next;
|
||||
s_step_start = millis();
|
||||
s_step_start = now;
|
||||
}
|
||||
|
||||
// ---- Button ------------------------------------------------------------
|
||||
|
||||
static OneButton s_button(BUTTON_PIN, true, true); // active-low, enable pullup
|
||||
|
||||
// ---- Arduino entry points ----------------------------------------------
|
||||
|
||||
void setup() {
|
||||
leds_begin();
|
||||
button_begin(BUTTON_PIN);
|
||||
load_show(0); // start on show 0 — blue breath
|
||||
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.attachLongPressStart([]() { load_show(0); });
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// ---- Button navigation ----
|
||||
ButtonEvent evt = button_update();
|
||||
if (evt == BTN_TAP) {
|
||||
// Next show, wrapping around to 0 after the last one.
|
||||
load_show((s_show_index + 1) % SHOW_COUNT);
|
||||
}
|
||||
if (evt == BTN_DOUBLE_TAP) {
|
||||
// Previous show, wrapping around to the last one from show 0.
|
||||
load_show((s_show_index + SHOW_COUNT - 1) % SHOW_COUNT);
|
||||
}
|
||||
if (evt == BTN_HOLD) {
|
||||
// Reset to show 0 (blue breath) from any position.
|
||||
load_show(0);
|
||||
}
|
||||
s_button.tick();
|
||||
|
||||
// ---- Playback ----
|
||||
Step step = read_step(&s_show.steps[s_step_index]);
|
||||
CRGB to = CRGB(step.r, step.g, step.b);
|
||||
float t = step_progress(step);
|
||||
static uint32_t s_last_frame = 0;
|
||||
uint32_t now = millis();
|
||||
if (now - s_last_frame < 16) return;
|
||||
s_last_frame = now;
|
||||
|
||||
leds_apply_color(blend_colors(s_from_color, to, t));
|
||||
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 (t >= 1.0f) {
|
||||
advance_step(to);
|
||||
}
|
||||
|
||||
delay(UPDATE_INTERVAL_MS);
|
||||
if (t8 == 255) advance_step(to, now);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user