More optimize

This commit is contained in:
2026-05-23 10:23:47 +02:00
parent 50df45fd4e
commit 4e5396f231
10 changed files with 165 additions and 30 deletions
+15 -12
View File
@@ -22,10 +22,10 @@ On every `loop()` call (~60 times per second):
1. Check the button for events; switch show if needed (`load_show()`). 1. Check the button for events; switch show if needed (`load_show()`).
2. Read the current step from `SHOW[].steps` in PROGMEM. 2. Read the current step from `SHOW[].steps` in PROGMEM.
3. Compute progress `t = elapsed / duration_ms`, clamped to 0.01.0. 3. Compute progress `t` (0255) as `elapsed * 255 / duration_ms`, clamped.
4. Blend `s_from_color` toward the target color by `t`. 4. Blend `s_from_color` toward the target color using FastLED's `blend()`.
5. Push the blended color to the LEDs. 5. Push the blended color to the LEDs.
6. If `t >= 1.0`, advance to the next step (loops at end of show). 6. If `t == 255`, advance to the next step. For `SHOW_LOOP` shows this wraps; for `SHOW_SINGLE` shows this loads the next show.
To **hold** a color, add two steps with the same color — the second transition is from the color to itself, which is invisible. To **hold** a color, add two steps with the same color — the second transition is from the color to itself, which is invisible.
@@ -33,23 +33,26 @@ To **hold** a color, add two steps with the same color — the second transition
The Arduino Uno has only 2KB of RAM. Storing all show data in normal arrays would exhaust it quickly. `PROGMEM` stores data in flash memory (32KB), which the Uno has much more of. Both the `Step` arrays and the `ShowDef` index (`SHOWS[]`) live in PROGMEM; the `read_step()` and `read_show_def()` helpers in `lightshow_format.h` extract values safely. The Arduino Uno has only 2KB of RAM. Storing all show data in normal arrays would exhaust it quickly. `PROGMEM` stores data in flash memory (32KB), which the Uno has much more of. Both the `Step` arrays and the `ShowDef` index (`SHOWS[]`) live in PROGMEM; the `read_step()` and `read_show_def()` helpers in `lightshow_format.h` extract values safely.
Each `Step` is 5 bytes (3 bytes RGB + 2 bytes duration). A `ShowDef` is 4 bytes (2-byte pointer + 2-byte length). The sketch currently uses 21% of flash — there is room for many more shows. Each `Step` is 5 bytes (3 bytes RGB + 2 bytes duration). A `ShowDef` is 6 bytes (2-byte pointer + 2-byte length + 1-byte mode + 1 byte padding). The sketch currently uses roughly 25% of flash — there is room for many more shows.
--- ---
## How the button works ## How the button works
`button.h/.cpp` implements a state machine with software debouncing: Button handling uses the [OneButton](https://github.com/mathertel/OneButton) library. A `OneButton` instance is created in `cosplay_lights.ino` with callbacks attached in `setup()`:
1. **Debounce** — the raw pin state must be stable for `DEBOUNCE_MS` (50ms) before the debounced state updates. This filters contact bounce. - **Single tap** → next show
- **Double tap** → previous show
- **Long press** → reset to show 0
2. **Hold detection** — if the debounced state stays pressed for `HOLD_MS` (800ms), `BTN_HOLD` fires immediately (doesn't wait for release). Any pending tap count is discarded. The key timing values (configured via `setClickMs` and `setPressMs`):
3. **Tap counting** — each clean release increments a tap counter and records the release time. | Setting | Value | Effect |
|---------|-------|--------|
| `setClickMs(400)` | 400ms | Single tap confirmed after 400ms of silence (to distinguish from a double tap) |
| `setPressMs(800)` | 800ms | Hold duration before long press fires |
4. **Tap resolution** — after `DOUBLE_TAP_MS` (400ms) of silence, the accumulated tap count is resolved: 1 tap → `BTN_TAP`, 2+ taps → `BTN_DOUBLE_TAP`. This introduces a 400ms delay on single taps (acceptable for show navigation). The button is polled every `loop()` iteration (not just once per frame), so it responds immediately even during the 16ms LED update window. OneButton handles debouncing internally.
Timing constants are at the top of `button.cpp` and can be adjusted.
--- ---
@@ -59,7 +62,7 @@ Timing constants are at the top of `button.cpp` and can be adjusted.
The generation pipeline: The generation pipeline:
1. Reads every `.txt` in `converter/shows/`. 1. Reads every `.txt` in `converter/shows/`.
2. Converts `blue_pulse.txt` first (always index 0), then all others alphabetically. 2. Converts `blue_breath.txt` first (always index 0), then all others alphabetically.
3. Writes one `show_<name>.h` per file into `arduino/cosplay_lights/`. 3. Writes one `show_<name>.h` per file into `arduino/cosplay_lights/`.
4. Regenerates `shows.h` with updated `#include` lines and `SHOWS[]` table. 4. Regenerates `shows.h` with updated `#include` lines and `SHOWS[]` table.
+2 -2
View File
@@ -11,7 +11,7 @@ This file records the original design brief and decisions so future contributors
2. **Multi-show navigation** — all shows are compiled into the firmware. A single button lets you cycle through them: 2. **Multi-show navigation** — all shows are compiled into the firmware. A single button lets you cycle through them:
- 1 tap — next show - 1 tap — next show
- 2 taps — previous show - 2 taps — previous show
- Hold — reset to show 0 (slow blue pulse) - Hold — reset to show 0 (blue breath)
3. **Arduino + WS2812B** — runs on an Arduino Uno with a WS2812B (NeoPixel) LED strip. 3. **Arduino + WS2812B** — runs on an Arduino Uno with a WS2812B (NeoPixel) LED strip.
@@ -56,7 +56,7 @@ Write/edit .txt files in converter/shows/
## Future ideas (not yet implemented) ## Future ideas (not yet implemented)
- Per-LED patterns (chase, sparkle, gradient segments) - Per-LED patterns chase, sparkle (random white flashes over a base color), gradient segments. The `led_controller.cpp` pattern system already has the hook: add a `#define PATTERN_SPARKLE` in `config.h` and implement the branch in `leds_apply_color()`
- Multiple strip support - Multiple strip support
- Battery power optimization (auto-dim, sleep) - Battery power optimization (auto-dim, sleep)
- SD card loading (no recompile needed for new shows) - SD card loading (no recompile needed for new shows)
+42 -12
View File
@@ -51,7 +51,7 @@ Uses the internal pull-up resistor. No external resistor needed. Pin 2 can be ch
|-------|--------| |-------|--------|
| 1 tap | Next show | | 1 tap | Next show |
| 2 taps | Previous show | | 2 taps | Previous show |
| Hold (~0.8s) | Reset to show 0 (slow blue pulse) | | Hold (~0.8s) | Reset to show 0 (blue breath) |
### Power warning — read this ### Power warning — read this
@@ -70,9 +70,12 @@ A 60-LED strip at full white = **3.6A** — far more than the Arduino's 5V pin c
## Quick start ## Quick start
### 1. Install the FastLED library ### 1. Install required libraries
Arduino IDE: **Sketch → Include Library → Manage Libraries** → search `FastLED` → Install. Arduino IDE: **Sketch → Include Library → Manage Libraries**, then install both:
- **FastLED** — LED strip control and color math
- **OneButton** — button debounce and gesture detection
### 2. Configure your hardware ### 2. Configure your hardware
@@ -84,14 +87,28 @@ Edit `arduino/cosplay_lights/config.h`:
### 3. Write or edit show files ### 3. Write or edit show files
Show files live in `converter/shows/`. Four are included: Show files live in `converter/shows/`. These are included:
| File | Description | | File | Description | Mode |
|------|-------------| |------|-------------|------|
| `blue_pulse.txt` | Slow blue breathing — always show 0 (home/reset) | | `blue_breath.txt` | Slow blue breathing — always show 0 (home/reset) | loop |
| `example_fade.txt` | Slow color cycle | | `001_heartbeat.txt` | 3 heartbeats in bright red, then auto-advances to breathing | single |
| `example_party.txt` | Fast rainbow flashes | | `002_breath_red.txt` | Breathing between dark maroon and bright red, 5s cycle | loop |
| `example_pulse.txt` | Red heartbeat | | `003_solid_red.txt` | Constant deep red | loop |
| `example_fade.txt` | Slow color cycle | loop |
| `example_party.txt` | Fast rainbow flashes | loop |
| `example_pulse.txt` | Red heartbeat | loop |
**Show modes:**
- `loop` — repeats indefinitely until the button is pressed
- `single` — plays once, then automatically advances to the next show
Set the mode with a comment anywhere in the `.txt` file:
```
// mode: single
```
If no mode line is present, the show loops.
**Show file format:** **Show file format:**
@@ -133,7 +150,21 @@ make upload PORT=/dev/ttyUSB1 # target a specific port
1. Create a `.txt` file in `converter/shows/` using the format above. 1. Create a `.txt` file in `converter/shows/` using the format above.
2. Run `make upload` — your new show is automatically included. 2. Run `make upload` — your new show is automatically included.
The order in which shows appear when cycling through with the button is: `blue_pulse` first, then all others sorted alphabetically by filename. The order in which shows appear when cycling through with the button is: `blue_breath` first (always show 0), then all others sorted alphabetically by filename. Prefix filenames with numbers (`001_`, `002_`, …) to control order.
---
## File overview
---
## Resources
- [Wokwi](https://wokwi.com/projects/new/arduino-uno) — browser-based Arduino + LED strip emulator, useful for testing shows without hardware
- [tweaking4all — LED strip effects](https://www.tweaking4all.nl/hardware/arduino/adruino-led-strip-effecten/) — reference for animation patterns (sparkle, rainbow, etc.)
- [FastLED color fills reference](https://fastled.io/docs/da/de3/group___color_fills.html)
- [Adafruit LED animation guide](https://learn.adafruit.com/circuitpython-led-animations/basic-animations)
- [htmlcolorcodes.com](https://htmlcolorcodes.com/) — hex color picker for choosing show colors
--- ---
@@ -144,7 +175,6 @@ Amirine_Cosplay_Lights/
├── arduino/cosplay_lights/ ├── arduino/cosplay_lights/
│ ├── cosplay_lights.ino — main sketch (upload this) │ ├── cosplay_lights.ino — main sketch (upload this)
│ ├── config.h — hardware settings │ ├── config.h — hardware settings
│ ├── button.h / .cpp — button debounce and event detection
│ ├── led_controller.h / .cpp — LED abstraction layer │ ├── led_controller.h / .cpp — LED abstraction layer
│ ├── lightshow_format.h — Step and ShowDef structs │ ├── lightshow_format.h — Step and ShowDef structs
│ ├── shows.h — master show index (regenerated by make shows) │ ├── shows.h — master show index (regenerated by make shows)
@@ -0,0 +1,24 @@
// Generated by convert_all.py from: 001_heartbeat.txt
// Do not edit manually — edit the .txt file and run: make shows
// SPDX-License-Identifier: BSD-2-Clause
#pragma once
#include "lightshow_format.h"
const Step SHOW_001_HEARTBEAT[] PROGMEM = {
{ 5, 0, 0, 0}, // #050000, 0ms
{209, 14, 41, 120}, // #D10E29, 120ms
{ 48, 0, 0, 100}, // #300000, 100ms
{209, 14, 41, 100}, // #D10E29, 100ms
{ 5, 0, 0, 700}, // #050000, 700ms
{209, 14, 41, 120}, // #D10E29, 120ms
{ 48, 0, 0, 100}, // #300000, 100ms
{209, 14, 41, 100}, // #D10E29, 100ms
{ 5, 0, 0, 700}, // #050000, 700ms
{209, 14, 41, 120}, // #D10E29, 120ms
{ 48, 0, 0, 100}, // #300000, 100ms
{209, 14, 41, 100}, // #D10E29, 100ms
{ 5, 0, 0, 700}, // #050000, 700ms
{153, 40, 58, 2000}, // #99283A, 2000ms
};
const uint16_t SHOW_001_HEARTBEAT_LENGTH = 14;
@@ -0,0 +1,13 @@
// Generated by convert_all.py from: 002_breath_red.txt
// Do not edit manually — edit the .txt file and run: make shows
// SPDX-License-Identifier: BSD-2-Clause
#pragma once
#include "lightshow_format.h"
const Step SHOW_002_BREATH_RED[] PROGMEM = {
{153, 40, 58, 0}, // #99283A, 0ms
{217, 30, 30, 2500}, // #D91E1E, 2500ms
{153, 40, 58, 2500}, // #99283A, 2500ms
};
const uint16_t SHOW_002_BREATH_RED_LENGTH = 3;
@@ -0,0 +1,12 @@
// Generated by convert_all.py from: 003_solid_red.txt
// Do not edit manually — edit the .txt file and run: make shows
// SPDX-License-Identifier: BSD-2-Clause
#pragma once
#include "lightshow_format.h"
const Step SHOW_003_SOLID_RED[] PROGMEM = {
{153, 40, 58, 0}, // #99283A, 0ms
{153, 40, 58, 30000}, // #99283A, 30000ms
};
const uint16_t SHOW_003_SOLID_RED_LENGTH = 2;
+10 -4
View File
@@ -14,6 +14,9 @@
// ---- Individual show data ---------------------------------------------- // ---- Individual show data ----------------------------------------------
#include "show_blue_breath.h" #include "show_blue_breath.h"
#include "show_001_heartbeat.h"
#include "show_002_breath_red.h"
#include "show_003_solid_red.h"
#include "show_example_fade.h" #include "show_example_fade.h"
#include "show_example_party.h" #include "show_example_party.h"
#include "show_example_pulse.h" #include "show_example_pulse.h"
@@ -21,9 +24,12 @@
// ---- Show index (PROGMEM) ---------------------------------------------- // ---- Show index (PROGMEM) ----------------------------------------------
const ShowDef SHOWS[] PROGMEM = { const ShowDef SHOWS[] PROGMEM = {
{SHOW_BLUE_BREATH, SHOW_BLUE_BREATH_LENGTH, SHOW_LOOP}, // 0 — home show {SHOW_BLUE_BREATH, SHOW_BLUE_BREATH_LENGTH, SHOW_LOOP}, // 0 — home show
{SHOW_EXAMPLE_FADE, SHOW_EXAMPLE_FADE_LENGTH, SHOW_LOOP}, // 1 {SHOW_001_HEARTBEAT, SHOW_001_HEARTBEAT_LENGTH, SHOW_SINGLE}, // 1
{SHOW_EXAMPLE_PARTY, SHOW_EXAMPLE_PARTY_LENGTH, SHOW_LOOP}, // 2 {SHOW_002_BREATH_RED, SHOW_002_BREATH_RED_LENGTH, SHOW_LOOP}, // 2
{SHOW_EXAMPLE_PULSE, SHOW_EXAMPLE_PULSE_LENGTH, SHOW_LOOP}, // 3 {SHOW_003_SOLID_RED, SHOW_003_SOLID_RED_LENGTH, SHOW_LOOP}, // 3
{SHOW_EXAMPLE_FADE, SHOW_EXAMPLE_FADE_LENGTH, SHOW_LOOP}, // 4
{SHOW_EXAMPLE_PARTY, SHOW_EXAMPLE_PARTY_LENGTH, SHOW_LOOP}, // 5
{SHOW_EXAMPLE_PULSE, SHOW_EXAMPLE_PULSE_LENGTH, SHOW_LOOP}, // 6
}; };
const uint8_t SHOW_COUNT = 4; const uint8_t SHOW_COUNT = 7;
+29
View File
@@ -0,0 +1,29 @@
// Monster Hunter cosplay — heartbeat intro
// Three heartbeats in bright red, then fades into the base color of the breathing show.
// Auto-advances to 002_breath_red when done.
//
// mode: single
// Format: #RRGGBB, duration_ms
#050000, 0 // start near black
// beat 1
#d10e29, 120 // lub — fast rise to bright red
#300000, 100 // dip between beats
#d10e29, 100 // dub
#050000, 700 // rest
// beat 2
#d10e29, 120 // lub
#300000, 100 // dip
#d10e29, 100 // dub
#050000, 700 // rest
// beat 3
#d10e29, 120 // lub
#300000, 100 // dip
#d10e29, 100 // dub
#050000, 700 // rest
// 2-second fade into the base color of the breathing show
#99283a, 2000
+10
View File
@@ -0,0 +1,10 @@
// Monster Hunter cosplay — breathing red
// Slow breathing between dark maroon (#99283a) and bright red (#d91e1e).
// One breath cycle lasts 5 seconds. Loops continuously.
//
// mode: loop
// Format: #RRGGBB, duration_ms
#99283a, 0 // snap to dim base color
#d91e1e, 2500 // breathe in — slow rise to bright red
#99283a, 2500 // breathe out — slow fall back to dim
+8
View File
@@ -0,0 +1,8 @@
// Monster Hunter cosplay — solid deep red
// Constant dark maroon (#99283a). Stays on until the button is pressed.
//
// mode: loop
// Format: #RRGGBB, duration_ms
#99283a, 0 // snap on instantly
#99283a, 30000 // hold (30s loop is invisible — same color each cycle)