Add boot self-test and idle indicator; target the Waveshare RP2350-Plus

The board arriving is a Waveshare RP2350-Plus (4MB, USB-C), which is now the
default BOARD. Its pico-sdk header defines neither PICO_DEFAULT_LED_PIN nor
PICO_DEFAULT_WS2812_PIN - it has no onboard indicator at all - so a bare board
gives no sign of life. The status-LED support added for boards that do have one
(zero, one, tiny, usb_a on GPIO16; eth on GPIO25) is kept and compiles out here.

That makes feedback on the strip itself the useful path, and it turns out to be
the better one anyway:

- Boot self-test sweeps one pixel from index 0 to the far end once at startup.
  It answers in a single glance whether the firmware runs, PIO drives the line,
  the strip is the length LED_COUNT claims, the far end holds voltage, and -
  because you see which end it starts from - whether STRIP_REVERSED is right.
  It runs before USB, so the first test needs nothing but 5V.
- Idle indicator holds one dim pixel lit while no host is connected, separating
  "powered and waiting" from "no power" and from "crashed".

Both are platform-independent, so the Circle build gets them too.

Verified: tests pass across seventeen configurations, now including the
self-test and idle paths on and off. Both platforms build clean with no
warnings from project sources.

Note on the previous commit's verification: a filtered build log hid a real
compile error in the Pico target (sleep_ms takes uint32_t, which is unsigned
long here, and did not match the portable void(*)(unsigned) delay callback).
The build script now gets an explicit success check rather than a grep.

Claude-Session: https://claude.ai/code/session_01TVCB25LBsmeteWvaSMz4Ne
This commit is contained in:
prosolis
2026-08-27 23:22:56 -07:00
parent 202adacf7a
commit 59bd3b4cea
10 changed files with 307 additions and 14 deletions
+43
View File
@@ -69,12 +69,16 @@ static void AllOffAndClear (void)
}
#endif
static unsigned g_nDelayCalls = 0;
static void CountingDelay (unsigned) { g_nDelayCalls++; }
int main (void)
{
printf ("STRIP_REVERSED=%d LED_COUNT=%d MAX_LIT_KEYS=%d GLOBAL_BRIGHTNESS=%d\n\n",
STRIP_REVERSED, LED_COUNT, MAX_LIT_KEYS, GLOBAL_BRIGHTNESS);
LEDs.Initialize ();
LEDs.SetHostConnected (true); // tests exercise the connected path
// --- lowest key, A0 = note 21 -------------------------------------
Inject (0x90, 21, 127);
@@ -324,6 +328,45 @@ int main (void)
Inject (0x80, 60, 0);
LEDs.Update ();
// --- idle indicator ---------------------------------------------------
LEDs.AllOff ();
LEDs.SetHostConnected (false);
LEDs.Update ();
#if IDLE_INDICATOR
Check ("with no host, one dim pixel marks the strip alive",
CountLit () == 1 && !Dark (0));
#else
Check ("with no host and no indicator, the strip is dark", CountLit () == 0);
#endif
// notes cannot arrive without a host, and must not display if they do
Inject (0x90, 60, 127);
LEDs.Update ();
Check ("notes do not display while no host is connected",
CountLit () <= 1);
LEDs.SetHostConnected (true);
LEDs.Update ();
Check ("reconnecting restores note display", !Dark (LedFor (60)));
LEDs.AllOff ();
LEDs.Update ();
// --- self test --------------------------------------------------------
g_nDelayCalls = 0;
LEDs.RunSelfTest (CountingDelay);
#if BOOT_SELF_TEST
Check ("self test steps once per pixel", g_nDelayCalls == LED_COUNT);
Check ("self test leaves the strip dark", CountLit () == 0);
// and the renderer must not think the strip still holds what it drew
Inject (0x90, 60, 127);
LEDs.Update ();
Check ("display works after the self test", !Dark (LedFor (60)));
LEDs.AllOff ();
LEDs.Update ();
#else
Check ("self test is compiled out", g_nDelayCalls == 0);
#endif
printf ("\n%s\n", g_nFail ? "FAILURES" : "all tests passed");
return g_nFail != 0;
}