Files
Amirine_Cosplay_Lights/DETAILS.md
T
bgrolleman 11eb2584ef Initial commit — Amirine Cosplay Lights
Arduino Uno + WS2812B LED strip controller with a text-based lightshow
system. Shows are defined as .txt files (hex color + fade duration per step),
converted to PROGMEM headers by convert_all.py, and navigated at runtime
via a debounced button (tap/double-tap/hold). BSD 2-Clause license.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 10:16:56 +02:00

155 lines
5.4 KiB
Markdown
Raw 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.
# Architecture Details — Amirine Cosplay Lights
For anyone who wants to understand or modify how the project works.
SPDX-License-Identifier: BSD-2-Clause
---
## How show playback works
The Arduino sketch maintains a small amount of playback state:
| Variable | Type | What it stores |
|---|---|---|
| `s_show_index` | `uint8_t` | Which show is active (index into `SHOWS[]`) |
| `s_show` | `ShowDef` | Active show descriptor (read from PROGMEM once on load) |
| `s_step_index` | `uint16_t` | Current step within the active show |
| `s_step_start` | `uint32_t` | `millis()` when the current step began |
| `s_from_color` | `CRGB` | Color the strip was showing when this step started |
On every `loop()` call (~60 times per second):
1. Check the button for events; switch show if needed (`load_show()`).
2. Read the current step from `SHOW[].steps` in PROGMEM.
3. Compute progress `t = elapsed / duration_ms`, clamped to 0.01.0.
4. Blend `s_from_color` toward the target color by `t`.
5. Push the blended color to the LEDs.
6. If `t >= 1.0`, advance to the next step (loops at end of show).
To **hold** a color, add two steps with the same color — the second transition is from the color to itself, which is invisible.
### Why PROGMEM?
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.
---
## How the button works
`button.h/.cpp` implements a state machine with software debouncing:
1. **Debounce** — the raw pin state must be stable for `DEBOUNCE_MS` (50ms) before the debounced state updates. This filters contact bounce.
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.
3. **Tap counting** — each clean release increments a tap counter and records the release time.
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).
Timing constants are at the top of `button.cpp` and can be adjusted.
---
## File generation
`shows.h` and `show_*.h` are **generated files** — they are the output of `converter/convert_all.py`. The source of truth is the `.txt` files in `converter/shows/`.
The generation pipeline:
1. Reads every `.txt` in `converter/shows/`.
2. Converts `blue_pulse.txt` first (always index 0), then all others alphabetically.
3. Writes one `show_<name>.h` per file into `arduino/cosplay_lights/`.
4. Regenerates `shows.h` with updated `#include` lines and `SHOWS[]` table.
Running `make shows` triggers this. `make upload` runs `make shows` first automatically.
---
## Adding a new LED pattern
The current `ACTIVE_PATTERN` is `PATTERN_SOLID` — all LEDs show the same blended color.
To add, for example, a chase pattern:
**Step 1** — Add a `#define` in `config.h`:
```cpp
#define PATTERN_SOLID 0
#define PATTERN_CHASE 1
#define ACTIVE_PATTERN PATTERN_CHASE
```
**Step 2** — Add the pattern logic in `led_controller.cpp`:
```cpp
#elif ACTIVE_PATTERN == PATTERN_CHASE
// One lit LED chases along the strip using the blended show color.
static uint16_t head = 0;
fill_solid(leds, NUM_LEDS, CRGB::Black);
leds[head] = color;
head = (head + 1) % NUM_LEDS;
```
The `color` parameter is always the show's current blended color, so patterns automatically follow lightshow transitions.
---
## Changing the LED strip type
Edit `config.h`:
```cpp
// WS2812B (default)
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
#define LED_DATA_PIN 6
// APA102 / Dotstar (two-wire)
// #define LED_TYPE APA102
// #define COLOR_ORDER BGR
// #define LED_DATA_PIN 6
// #define LED_CLOCK_PIN 7
```
For APA102, also change the `FastLED.addLeds` call in `led_controller.cpp` to the two-pin form:
```cpp
FastLED.addLeds<LED_TYPE, LED_DATA_PIN, LED_CLOCK_PIN, COLOR_ORDER>(leds, NUM_LEDS);
```
---
## Multiple strips
To drive two strips with the same show, add a second array and register it in `led_controller.cpp`:
```cpp
static CRGB leds_a[NUM_LEDS];
static CRGB leds_b[NUM_LEDS];
void leds_begin() {
FastLED.addLeds<LED_TYPE, 6, COLOR_ORDER>(leds_a, NUM_LEDS);
FastLED.addLeds<LED_TYPE, 5, COLOR_ORDER>(leds_b, NUM_LEDS);
FastLED.setBrightness(MAX_BRIGHTNESS);
FastLED.clear(true);
}
void leds_apply_color(CRGB color) {
fill_solid(leds_a, NUM_LEDS, color);
fill_solid(leds_b, NUM_LEDS, color);
}
```
`FastLED.show()` pushes all registered strips at once — `leds_show()` needs no changes.
---
## Power budget reference
| Scenario | Current draw (approx.) |
|----------|----------------------|
| 60 LEDs, full white, brightness 255 | ~3.6A |
| 60 LEDs, full white, brightness 150 | ~2.1A |
| 60 LEDs, single color, brightness 150 | ~0.7A |
| 60 LEDs, all off | ~20mA (Arduino only) |
Always use a 5V supply rated for at least **1A more** than your calculated draw, and add a 1000µF capacitor on the supply lines near the strip connector.