7#if FL_PLATFORM_HAS_LARGE_MEMORY
10#define DEBUG_PRINT(x) do {} while(0)
11#define DEBUG_PRINTLN(x) do {} while(0)
63 writer.
member(
"name", entry.name);
64 writer.
member(
"phase", entry.phase);
65 writer.
member(
"args", entry.args);
66 writer.
member(
"returns", entry.returns);
67 writer.
member(
"description", entry.description);
71static const HelpEntry kHelpEntries[] = {
72 {
"rpc.discover",
"JSON-RPC",
"[]",
"{schema:[[name, ...], ...]}",
"Discover the registered JSON-RPC method schema"},
73 {
"start",
"Phase 1: Basic Control",
"[]",
"void",
"Trigger test matrix execution"},
74 {
"status",
"Phase 1: Basic Control",
"[]",
"{ready, platform, rpcReady, ledRxAvailable, pinTx, pinRx}",
"Query RPC, platform, and LED RX readiness"},
75 {
"drivers",
"Phase 1: Basic Control",
"[]",
"[{name, priority, enabled}, ...]",
"List available drivers"},
76 {
"getConfig",
"Phase 2: Configuration",
"[]",
"{drivers, laneRange, stripSizes, totalTestCases}",
"Query current test matrix configuration"},
77 {
"setDrivers",
"Phase 2: Configuration",
"[driver1, driver2, ...]",
"{success, driversSet, testCases}",
"Configure enabled drivers"},
78 {
"setLaneRange",
"Phase 2: Configuration",
"[minLanes, maxLanes]",
"{success, minLanes, maxLanes, testCases}",
"Configure lane range (1-16)"},
79 {
"setStripSizes",
"Phase 2: Configuration",
"[size] or [shortSize, longSize]",
"{success, stripSizesSet, testCases}",
"Configure strip sizes"},
80 {
"runTestCase",
"Phase 3: Selective Execution",
"[testCaseIndex]",
"{success, testCaseIndex, result}",
"Run single test case by index"},
81 {
"runDriver",
"Phase 3: Selective Execution",
"[driverName]",
"{success, driver, testsRun, results}",
"Run all tests for specific driver"},
82 {
"runAll",
"Phase 3: Selective Execution",
"[]",
"{success, totalCases, passedCases, skippedCases, results}",
"Run full test matrix with JSON results"},
83 {
"getResults",
"Phase 3: Selective Execution",
"[]",
"[{driver, lanes, stripSize, ...}, ...]",
"Return all test results"},
84 {
"getResult",
"Phase 3: Selective Execution",
"[testCaseIndex]",
"{driver, lanes, stripSize, ...}",
"Return specific test case result"},
85 {
"perfProbeBitBangCost",
"Phase 2: Performance",
"[{iterations, ns, pin}]",
"{success, nop_us_per_iter, delay_us_per_iter, delay_hz_us_per_iter, write_byte_us_per_iter, clock_query_us, predicted_bit_overhead_us}",
"Attribute the BIT_BANG per-bit overhead across delay vs pin-write calls (FastLED#4203)"},
86#if FL_COLOR_PIPELINE_SHARED
87 {
"mixedPowerShowPerf",
"Phase 2: Performance",
"[{pixels, frames, budget_mw}] (optional; max 128 pixels/channel)",
"{success, on_median_us, off_median_us, on_plan_modeled_mw, off_plan_modeled_mw, on_plan_flux_q16, off_plan_flux_q16, on_fnv1a, off_fnv1a}",
"Benchmark mixed RGB/RGBW/RGBWW power planning plus encode through FastLED.show(), excluding physical wire time; resets power settings to unlimited (#4499)"},
89 {
"reset",
"Phase 4: Utility",
"[]",
"{success, message, testCasesCleared}",
"Reset test state without device reboot"},
90 {
"halt",
"Phase 4: Utility",
"[]",
"{success, message}",
"Trigger sketch halt"},
91 {
"ping",
"Phase 4: Utility",
"[]",
"{success, message, timestamp, uptimeMs, lastResetCause, lastResetWasWatchdog, frameCounter}",
"Health check with timestamp and reset evidence"},
92 {
"debugTest",
"Phase 4: Utility",
"object",
"{success, received}",
"Echo an exact nested JSON payload"},
93 {
"testNoSerial",
"Phase 4: Utility",
"[]",
"{success, message, serial_safe}",
"Verify RPC execution without device-side serial logging"},
94 {
"testRpConcurrency",
"Phase 4: Utility",
"[]",
"{success, supported, backend, core1Ready, core1Done, recursiveMutexReady, generation, expected, actual}",
"Exercise mutex and semaphore synchronization across both RP physical cores"},
95 {
"testRpPioContention",
"Phase 4: Utility",
"[]",
"{success, numPios, pin, baselineFreeSms, control:{proxyValid, freeDuring, claimedOne, freeAfter, released}, contention:{starvedSms, freeAfterStarve, proxyValid, claimsIntact, noTheft, survivedShow, freeAfter}, noLeak}",
"FastLED#1471 repro: claim every PIO state machine the way Adafruit TinyUSB does, then build a clockless controller and verify FastLED declines cleanly instead of stealing a state machine from the other library."},
96 {
"testRpPioParallelResources",
"Phase 4: Utility",
"[{pinA, pinB, numLeds, iterations}] (all optional)",
"{success, pinA, pinB, baseline, busiestInFlight, released, framesSampled, samplesTaken, pio0HeldOne, pio1HeldOne, bothHeldAtOnce, noOverClaim, observedInFlight, collisionFree, showsCompleted, noResidueBetweenFrames, noLeak}",
"FastLED#3899: drive PIO0 and PIO1 simultaneously and assert at the SDK-claim level that neither engine over-claims mid-flight, both give everything back between frames, and nothing is left claimed on teardown. Whether the two are ever in flight at the same instant is reported as a diagnostic, not asserted."},
97 {
"getPins",
"Phase 5: Pin Configuration",
"[]",
"{txPin, rxPin, defaults: {txPin, rxPin}, platform}",
"Query current and default pin configuration"},
98 {
"setTxPin",
"Phase 5: Pin Configuration",
"[pin]",
"{success, txPin, previousTxPin, testCases}",
"Set TX pin (regenerates test cases)"},
99 {
"setRxPin",
"Phase 5: Pin Configuration",
"[pin]",
"{success, rxPin, previousRxPin, rxChannelRecreated}",
"Set RX pin (recreates RX channel)"},
100 {
"setPins",
"Phase 5: Pin Configuration",
"[{txPin, rxPin}] or [txPin, rxPin]",
"{success, txPin, rxPin, rxChannelRecreated, testCases}",
"Set both TX and RX pins atomically"},
101 {
"findConnectedPins",
"Phase 5: Pin Configuration",
"[{startPin, endPin, autoApply}] (all optional)",
"{success, found, txPin, rxPin, autoApplied, testedPairs}",
"Probe adjacent pin pairs to find jumper wire connection"},
102 {
"help",
"Phase 4: Utility",
"[]",
"[{name, phase, args, returns, description}, ...]",
"List all RPC functions with descriptions"},
103 {
"testSimd",
"Phase 4: Utility",
"[]",
"{success, passed, totalTests, passedTests, failedTests, failures:[string]}",
"Run comprehensive SIMD test suite (85 tests)"},
104 {
"testSimdBenchmark",
"Phase 4: Utility",
"[{iterations}] (optional, default 10000)",
"{success, iterations, float_us, s16x16_us, simd_us}",
"Benchmark multiply speed: float vs s16x16 vs s16x16x4 SIMD"},
105 {
"animartrixPerlinBench",
"Phase 4: Utility",
"[{iterations}] (optional, default 100, max 10000)",
"{success, iterations, pnoise_calls_per_iter, pnoise_float_us, pnoise_i16_us, speedup_x1000}",
"Animartrix-representative Perlin noise bench: scalar float pnoise vs s16x16 fixed-point pnoise2d (16x16 grid per iter, mirrors a real frame). Answers: how much does fixed-point beat float for Animartrix's hot path on this hardware?"},
106 {
"wave8ExpandBenchmark",
"Phase 4: Utility",
"[{iterations}] (optional, default 30000, max 200000)",
"{success, iterations, expand_nibble_us, expand_byte_us, expand_batched_us, transpose16_nibble_us, transpose16_byte_us, sink}",
"Bench PARLIO Wave8 expansion (#2526): nibble vs byte vs batched LUT, plus full per-byte-position cost (expansion + 16-lane transpose)"},
107 {
"ieee754CodecTest",
"Phase 4: Utility",
"[]",
"{success, tests_run, tests_failed, first_failure, expected_bits, actual_bits}",
"On-device verification of the integer-only IEEE 754 decimal codec (#3039): parse, format, round-trip, overflow/underflow, and NaN rejection without strtof/libm."},
108 {
"mp3CodecTest",
"Phase 4: Utility",
"[]",
"{success, streams_run, streams_failed, first_failure, expected_fnv1a, actual_fnv1a, expected_samples, actual_samples, decode_micros, scratch_bytes, shared_decoder_matched, samples_decoded, frames_decoded, combined_fnv1a}",
"Bit-exact on-device check of the fixed-point MP3 decoder against host-computed FNV-1a checksums, over a Layer I and a Layer III stream, decoded both with a fresh decoder each and with one decoder shared across layers (FastLED#4116 scratch union)."},
109 {
"parlioEncodeBenchmark",
"Phase 4: Utility",
"[{iterations}] (optional, default 12000, max 200000)",
"{success, iters, lanes, leds_per_lane, scratchPsramOk, outputPsramOk, perpos_ss_us, perpos_sp_us, perpos_ps_us, perpos_pp_us, frame_ss_us, frame_sp_us, frame_ps_us, frame_pp_us, sink}",
"Bench full PARLIO encode hot loop (16-lane gather + BF1 pipe4 direct encode) with SRAM and optional PSRAM placements; answers PSRAM hypothesis + ISR-streaming feasibility"},
110 {
"timingDriftTest",
"Phase 4: Utility",
"[{pin, numLeds, iterations}] (all optional; default pin=4, numLeds=35, iterations=10)",
"{success, pin, num_leds, iterations, cpu_mhz, show_count, show_min_us, show_max_us, show_total_us, iter_ms:[...]}",
"Issue #2994 repro for compounded per-sequence timing drift on master vs 3.10.3. Replays the reporter's WS2812B-ring + millisDelay-gated fade sketch and returns per-sequence wall time (theoretical 2495 ms; on 3.10.3 rock-steady, on master 2563-2752)."},
111 {
"parlioStreamValidate",
"Phase 4: Utility",
"[{baseTxPin, txPins, numLanes, numLeds, iterations, timeoutMs}] (all optional; txPins overrides contiguous baseTxPin)",
"{success, completed, baseTxPin, txPins, lanes, leds_per_lane, iterations, perIterUs:[...], steadyAvgUs, failedIter, underrunCount, txDoneCount, workerIsrCount, ringError, hardwareIdle}",
"Functional test of the PARLIO ISR-chunked streaming engine (#2548). Drives N back-to-back FastLED.show() calls through the production engine (which uses BF1+pipe4 on 16-lane Wave8 since #2559) and verifies all complete within timeout. Catches hangs/stalls."},
112 {
"flexioRxBenchmark",
"Phase 4: Utility",
"[{frequency_hz=1000, duration_ms=100, tx_pin=3, rx_pin=4}] (all optional)",
"{success, frequency_hz, duration_ms, tx_pin, rx_pin, edges_captured, periods, period_mean_ns, period_sigma_ns, period_min_ns, period_max_ns}",
"Square-wave validation for the FlexIO RX backend (Teensy 4.x only, FastLED#2764 Phase 2). Drives tx_pin via analogWriteFrequency at 50%% duty, captures via RxBackend::FLEXIO on rx_pin, reports per-period statistics."},
113 {
"flexioObjectFledTest",
"Phase 4: Utility",
"[{test_case=0..4, tx_pin=3, rx_pin=4, capture_ms=50}] (all optional)",
"{success, test_case, tx_pin, rx_pin, num_leds, expected_bytes, decoded_bytes, matched, mismatched, edges_captured}",
"End-to-end ObjectFLED TX -> FlexIO RX loopback verification (Teensy 4.x only, FastLED#2764 Phase 3). Drives WS2812 patterns through Bus::FLEX_IO slot 0, captures via RxBackend::FLEXIO, decodes the bit stream, and reports byte-level match counts. Five fixed test patterns: 0=red, 1=RGB triple, 2=all zeros, 3=all ones, 4=100-LED alternating."},
116static const fl::size kHelpEntryCount =
sizeof(kHelpEntries) /
sizeof(kHelpEntries[0]);
123 remote.bind(
"setDebug", [
this](
const fl::json&
args) -> fl::json {
127 if (!
args.is_array() ||
args.size() != 1) {
129 response.set(
"error",
"InvalidArgs");
130 response.set(
"message",
"Expected [enabled: bool]");
134 if (!
args[0].is_bool()) {
136 response.set(
"error",
"InvalidType");
137 response.set(
"message",
"Argument must be boolean");
141 bool enabled =
args[0].as_bool().value();
142 mState->debug_enabled = enabled;
148 response.set(
"debug_enabled", enabled);
149 response.set(
"message", enabled ?
"Debug logging enabled" :
"Debug logging disabled");
156 remote.bind(
"testGpioConnection", [](
const fl::json&
args) -> fl::json {
160 if (!
args.is_array() ||
args.size() != 2) {
161 response.set(
"error",
"InvalidArgs");
162 response.set(
"message",
"Expected [txPin, rxPin]");
166 if (!
args[0].is_int() || !
args[1].is_int()) {
167 response.set(
"error",
"InvalidPinType");
168 response.set(
"message",
"Pin numbers must be integers");
172 int tx_pin =
static_cast<int>(
args[0].as_int().value());
173 int rx_pin =
static_cast<int>(
args[1].as_int().value());
192 bool connected = (rx_when_tx_low == LOW) && (rx_when_tx_high == HIGH);
194 response.set(
"txPin",
static_cast<int64_t
>(tx_pin));
195 response.set(
"rxPin",
static_cast<int64_t
>(rx_pin));
196 response.set(
"rxWhenTxLow", rx_when_tx_low == LOW ?
"LOW" :
"HIGH");
197 response.set(
"rxWhenTxHigh", rx_when_tx_high == HIGH ?
"HIGH" :
"LOW");
198 response.set(
"connected", connected);
202 response.set(
"message",
"GPIO pins are connected");
205 if (rx_when_tx_low == HIGH && rx_when_tx_high == HIGH) {
206 response.set(
"message",
"RX pin stuck HIGH - no connection detected (check jumper wire)");
207 }
else if (rx_when_tx_low == LOW && rx_when_tx_high == LOW) {
208 response.set(
"message",
"RX pin stuck LOW - possible short to ground");
210 response.set(
"message",
"Unexpected GPIO behavior - check wiring");
222 remote.bind(
"getPins", [
this](
const fl::json&
args) -> fl::json {
229 defaults.
set(
"txPin",
static_cast<int64_t
>(
mState->default_pin_tx));
230 defaults.
set(
"rxPin",
static_cast<int64_t
>(
mState->default_pin_rx));
247 remote.bind(
"setTxPin", [
this](
const fl::json&
args) -> fl::json {
250 if (!
args.is_array() ||
args.size() != 1 || !
args[0].is_int()) {
251 response.set(
"error",
"InvalidArgs");
252 response.set(
"message",
"Expected [pin: int]");
256 int new_pin =
static_cast<int>(
args[0].as_int().value());
259 if (new_pin < 0 || new_pin > 48) {
260 response.set(
"error",
"InvalidPin");
261 response.set(
"message",
"Pin must be 0-48");
265 int old_pin =
mState->pin_tx;
266 if (new_pin != old_pin) {
267 mState->invalidateLedRxValidation();
272 response.set(
"txPin",
static_cast<int64_t
>(new_pin));
273 response.set(
"previousTxPin",
static_cast<int64_t
>(old_pin));
278 remote.bind(
"setRxPin", [
this](
const fl::json&
args) -> fl::json {
281 if (!
args.is_array() ||
args.size() != 1 || !
args[0].is_int()) {
282 response.set(
"error",
"InvalidArgs");
283 response.set(
"message",
"Expected [pin: int]");
287 int new_pin =
static_cast<int>(
args[0].as_int().value());
290 if (new_pin < 0 || new_pin > 48) {
291 response.set(
"error",
"InvalidPin");
292 response.set(
"message",
"Pin must be 0-48");
296 int old_pin =
mState->pin_rx;
297 bool pin_changed = (new_pin != old_pin);
298 bool rx_recreated =
false;
301 fl::shared_ptr<fl::RxChannel> replacement_rx;
303 replacement_rx =
mState->rx_factory(new_pin);
305 if (!replacement_rx) {
306 response.set(
"error",
"RxChannelCreationFailed");
307 response.set(
"message",
"Failed to create RX channel on new pin");
308 response.set(
"pinRx",
static_cast<int64_t
>(new_pin));
312 mState->invalidateLedRxValidation();
314 mState->rx_channel = replacement_rx;
319 response.set(
"rxPin",
static_cast<int64_t
>(new_pin));
320 response.set(
"previousRxPin",
static_cast<int64_t
>(old_pin));
321 response.set(
"rxChannelRecreated", rx_recreated);
326 remote.bind(
"setPins", [
this](
const fl::json&
args) -> fl::json {
333 if (
args.is_array() &&
args.size() == 1 &&
args[0].is_object()) {
335 fl::json config =
args[0];
336 if (config.
contains(
"txPin") && config[
"txPin"].is_int()) {
337 new_tx_pin =
static_cast<int>(config[
"txPin"].
as_int().value());
339 if (config.
contains(
"rxPin") && config[
"rxPin"].is_int()) {
340 new_rx_pin =
static_cast<int>(config[
"rxPin"].
as_int().value());
342 }
else if (
args.is_array() &&
args.size() == 2) {
344 if (
args[0].is_int()) {
345 new_tx_pin =
static_cast<int>(
args[0].as_int().value());
347 if (
args[1].is_int()) {
348 new_rx_pin =
static_cast<int>(
args[1].as_int().value());
351 response.set(
"error",
"InvalidArgs");
352 response.set(
"message",
"Expected [{txPin, rxPin}] or [txPin, rxPin]");
357 if (new_tx_pin < 0 || new_tx_pin > 48) {
358 response.set(
"error",
"InvalidTxPin");
359 response.set(
"message",
"TX pin must be 0-48");
362 if (new_rx_pin < 0 || new_rx_pin > 48) {
363 response.set(
"error",
"InvalidRxPin");
364 response.set(
"message",
"RX pin must be 0-48");
368 int old_tx_pin =
mState->pin_tx;
369 int old_rx_pin =
mState->pin_rx;
370 bool tx_pin_changed = (new_tx_pin != old_tx_pin);
371 bool rx_pin_changed = (new_rx_pin != old_rx_pin);
372 bool rx_recreated =
false;
374 fl::shared_ptr<fl::RxChannel> replacement_rx;
375 if (rx_pin_changed) {
377 replacement_rx =
mState->rx_factory(new_rx_pin);
379 if (!replacement_rx) {
380 response.set(
"error",
"RxChannelCreationFailed");
381 response.set(
"message",
"Failed to create RX channel - previous pins preserved");
382 response.set(
"pinRx",
static_cast<int64_t
>(new_rx_pin));
389 if (tx_pin_changed || rx_pin_changed) {
390 mState->invalidateLedRxValidation();
392 mState->pin_tx = new_tx_pin;
393 mState->pin_rx = new_rx_pin;
394 if (rx_pin_changed) {
395 mState->rx_channel = replacement_rx;
400 response.set(
"txPin",
static_cast<int64_t
>(new_tx_pin));
401 response.set(
"rxPin",
static_cast<int64_t
>(new_rx_pin));
402 response.set(
"previousTxPin",
static_cast<int64_t
>(old_tx_pin));
403 response.set(
"previousRxPin",
static_cast<int64_t
>(old_rx_pin));
404 response.set(
"rxChannelRecreated", rx_recreated);
410 remote.bind(
"findConnectedPins", [
this](
const fl::json&
args) -> fl::json {
415 remote.bindStreaming(
"help", [](fl::JsonStreamWriter& writer,
const fl::json&
args,
const fl::json&
requestId) {
420 writer.
member(
"success",
true);
421 writer.
member(
"totalFunctions",
static_cast<fl::i64
>(kHelpEntryCount));
422 writer.
key(
"functions");
424 for (fl::size i = 0; i < kHelpEntryCount; ++i) {
425 streamHelpEntry(writer, kHelpEntries[i]);
fl::UIDescription description("Demo of the Animatrix effects. @author of fx is StefanPetrick")
Full PARLIO encode bench for ESP32-P4 (post-byte-LUT, #2526 landed).
PARLIO ISR-streaming functional validation (#2548 deep-dive follow-up).
Issue #2994 repro: compounded per-sequence timing drift vs 3.10.3.
Wave8 expansion micro-benchmark for #2526.
fl::unique_ptr< fl::Remote > remote
fl::net::ble — BLE GATT transport layer for JSON-RPC
SPI encoder configuration for clocked LED chipsets.
fl::shared_ptr< AutoResearchState > mState
fl::json findConnectedPinsImpl(const fl::json &args)
void bindPinMethods(fl::Remote &remote)
void endArray() FL_NO_EXCEPT
void beginArray() FL_NO_EXCEPT
void beginObject() FL_NO_EXCEPT
void key(const char *k) FL_NO_EXCEPT
void member(const char *k, const char *s) FL_NO_EXCEPT
void endObject() FL_NO_EXCEPT
JSON-RPC server with scheduling support.
fl::optional< i64 > as_int() const FL_NO_EXCEPT
void set(const fl::string &key, const json &value) FL_NO_EXCEPT
bool contains(size_t idx) const FL_NO_EXCEPT
static json object() FL_NO_EXCEPT
Task executor — runs registered task runners and manages the run loop.
Platform-abstracted heap memory query functions.
FastLED's Elegant JSON Library: fl::json
constexpr const char * chipName()
void delay(u32 ms, bool run_async=true) FL_NO_EXCEPT
Public delay wrapper that keeps bare Arduino delay() preferred after using fl::delay; while still all...
void setLogLevel(u8 level)
Set the global log level.
@ FL_LOG_LEVEL_DEBUG
All logging including debug (default)
@ FL_LOG_LEVEL_NONE
No logging (completely silent)
void pinMode(int pin, PinMode mode)
Set pin mode (input, output, pull-up, pull-down)
PinValue digitalRead(int pin)
Read digital value from pin.
void digitalWrite(int pin, PinValue val)
Write digital value to pin.
Promise-based fluent API for FastLED - standalone async primitives.
Umbrella header for SIMD subsystem.