feat(uno-q): LED-matrix animation sketches (blue 13x8)

There was only a scroll-text template; add frame-based animations for the
Uno Q's built-in 13x8 monochrome-blue matrix. Both compiled (arduino:zephyr
0.51.0) and flashed E2E on hardware (OpenOCD @ 0x80F0000, per boards.txt).

- matrix_rain/     — digital rain: staggered per-column drops + trails.
- matrix_effects/  — sampler cycling rain / knight-rider / ripple / sparkle
                     / wipe.
- README: the grid->uint32[4] packing pattern (row-major, MSB-first), how to
  add an effect, and the compile/flash recipe (TMPDIR=/tmp trap + the correct
  0x80F0000 address vs QClaw's stale 0x8100000).

Note: the matrix is physically blue + on/off only — no colour/brightness in
software.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Omar Sobh
2026-07-08 14:47:53 -07:00
co-authored by Claude Opus 4.8
parent 19593a637c
commit 2683c64c34
3 changed files with 252 additions and 0 deletions
@@ -0,0 +1,133 @@
// Matrix animation sampler for the Arduino Uno Q's 13x8 blue LED matrix.
// Cycles through several frame-based effects: rain -> knight-rider sweep ->
// ripple -> sparkle -> wipe, then repeats. All monochrome (pixels are on/off;
// the LEDs are physically blue — there is no colour or brightness control).
//
// Shared pattern: each effect fills the 8x13 `grid`, then show() packs it into
// the uint32_t[4] the driver wants and pushes it with loadFrame(). The only
// matrix calls that exist here are begin() and loadFrame().
//
// Compile: arduino-cli compile --fqbn arduino:zephyr:unoq --export-binaries
// Flash: arduino-flash <sketch>.ino.elf-zsk.bin (OpenOCD @ 0x80F0000)
#include "Arduino_LED_Matrix.h"
Arduino_LED_Matrix matrix;
static const uint8_t W = 13; // columns
static const uint8_t H = 8; // rows
bool grid[H][W];
uint32_t frame[4];
void clearGrid() {
for (uint8_t y = 0; y < H; y++)
for (uint8_t x = 0; x < W; x++) grid[y][x] = false;
}
// pack grid -> frame (row-major, MSB-first: pixel 0 = frame[0] bit 31) and show
void show(uint16_t ms) {
frame[0] = frame[1] = frame[2] = frame[3] = 0;
uint16_t bit = 0;
for (uint8_t y = 0; y < H; y++)
for (uint8_t x = 0; x < W; x++) {
if (grid[y][x]) frame[bit >> 5] |= (1UL << (31 - (bit & 31)));
bit++;
}
matrix.loadFrame(frame);
delay(ms);
}
// --- digital rain: per-column drops (head + short trail), staggered speeds ---
int8_t rHead[W];
uint8_t rLen[W], rPer[W], rPh[W];
void rainSeed(uint8_t x) {
rHead[x] = -(int8_t)random(0, H);
rLen[x] = random(2, 5);
rPer[x] = random(1, 4);
rPh[x] = 0;
}
void rain(uint16_t frames) {
for (uint8_t x = 0; x < W; x++) rainSeed(x);
for (uint16_t f = 0; f < frames; f++) {
clearGrid();
for (uint8_t x = 0; x < W; x++) {
for (uint8_t t = 0; t < rLen[x]; t++) {
int y = rHead[x] - t;
if (y >= 0 && y < H) grid[y][x] = true;
}
if (++rPh[x] >= rPer[x]) {
rPh[x] = 0;
rHead[x]++;
if (rHead[x] - (int8_t)rLen[x] >= (int8_t)H) rainSeed(x);
}
}
show(90);
}
}
// --- knight-rider: a full-height bar sweeps left<->right with a light trail ---
void knight(uint8_t sweeps) {
for (uint8_t s = 0; s < sweeps; s++) {
for (uint8_t dir = 0; dir < 2; dir++) {
for (int i = 0; i < W; i++) {
int x = dir ? (W - 1 - i) : i;
int xt = dir ? x + 1 : x - 1; // trailing column
clearGrid();
for (uint8_t y = 0; y < H; y++) {
grid[y][x] = true;
if (xt >= 0 && xt < W && (y % 2 == 0)) grid[y][xt] = true;
}
show(70);
}
}
}
}
// --- ripple: expanding square outline from the centre ---
void ripple(uint8_t reps) {
const int cx = 6, cy = 3;
for (uint8_t r0 = 0; r0 < reps; r0++) {
for (int r = 0; r < 8; r++) {
clearGrid();
for (int y = 0; y < H; y++)
for (int x = 0; x < W; x++)
if (max(abs(x - cx), abs(y - cy)) == r) grid[y][x] = true;
show(110);
}
}
}
// --- sparkle: random pixels twinkle ---
void sparkle(uint16_t frames) {
for (uint16_t f = 0; f < frames; f++) {
clearGrid();
for (uint8_t k = 0; k < 10; k++) grid[random(0, H)][random(0, W)] = true;
show(70);
}
}
// --- wipe: fill column-by-column, then clear column-by-column ---
void wipe(uint8_t reps) {
for (uint8_t r = 0; r < reps; r++) {
for (uint8_t on = 0; on < 2; on++) {
for (int x = 0; x < W; x++) {
for (uint8_t y = 0; y < H; y++) grid[y][x] = (on == 0);
show(45);
}
}
}
}
void setup() {
matrix.begin();
randomSeed(micros());
}
void loop() {
rain(70);
knight(2);
ripple(2);
sparkle(40);
wipe(2);
}