FastLED 3.10.6
Loading...
Searching...
No Matches
AutoResearchRemotePinMethods.cpp
Go to the documentation of this file.
1// Pin configuration RPC bindings: get/set TX/RX/pair, GPIO connectivity
2// probe, jumper-finder, debug-log toggle, help. GPIO is the implementation;
3// the RPC API is "pins" (the user-facing concept).
4// Extracted from AutoResearchRemote.cpp as part of #3132 / meta #3127.
5
7#if FL_PLATFORM_HAS_LARGE_MEMORY
8
9// Legacy debug macros (no-ops, kept for debugTest RPC function)
10#define DEBUG_PRINT(x) do {} while(0)
11#define DEBUG_PRINTLN(x) do {} while(0)
12
13#include "AutoResearchRemote.h"
14#include "AutoResearchBle.h"
15#include "AutoResearchNet.h"
16#include "AutoResearchOta.h"
18#include "fl/system/heap.h"
19#include "Common.h"
20#include "AutoResearchTest.h"
21#include "AutoResearchHelpers.h"
23#include "fl/stl/sstream.h"
24#include "fl/stl/unique_ptr.h"
25#include "fl/stl/optional.h"
26#include "fl/stl/json.h"
27#include "fl/task/task.h"
28#include "fl/task/executor.h"
30#include "fl/stl/atomic.h"
31#include "fl/task/promise.h"
32#include "fl/math/simd.h"
33#include "AutoResearchSimd.h"
39#include "fl/chipsets/spi.h"
40#include "fl/channels/config.h"
41#include <Arduino.h>
42
43#include "fl/net/ble.h"
44
45#include "fl/codec/h264.h"
46#include "fl/codec/mp4_parser.h"
47#include "fl/stl/cstdio.h"
49#include "fl/fx/frame.h"
50
51namespace {
52
53struct HelpEntry {
54 const char* name;
55 const char* phase;
56 const char* args;
57 const char* returns;
58 const char* description;
59};
60
61void streamHelpEntry(fl::JsonStreamWriter& writer, const HelpEntry& entry) {
62 writer.beginObject();
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);
68 writer.endObject();
69}
70
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)"},
88#endif
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."},
114};
115
116static const fl::size kHelpEntryCount = sizeof(kHelpEntries) / sizeof(kHelpEntries[0]);
117
118} // namespace
119
120
122 // Register "setDebug" function - enable/disable runtime debug logging
123 remote.bind("setDebug", [this](const fl::json& args) -> fl::json {
124 fl::json response = fl::json::object();
125
126 // Validate args: expects [enabled: bool]
127 if (!args.is_array() || args.size() != 1) {
128 response.set("success", false);
129 response.set("error", "InvalidArgs");
130 response.set("message", "Expected [enabled: bool]");
131 return response;
132 }
133
134 if (!args[0].is_bool()) {
135 response.set("success", false);
136 response.set("error", "InvalidType");
137 response.set("message", "Argument must be boolean");
138 return response;
139 }
140
141 bool enabled = args[0].as_bool().value();
142 mState->debug_enabled = enabled;
143 fl::setLogLevel(static_cast<fl::u8>(
146
147 response.set("success", true);
148 response.set("debug_enabled", enabled);
149 response.set("message", enabled ? "Debug logging enabled" : "Debug logging disabled");
150
151 return response;
152 });
153
154 // Register "testGpioConnection" function - test if TX and RX pins are electrically connected
155 // This is a pre-test to diagnose hardware connection issues before running validation
156 remote.bind("testGpioConnection", [](const fl::json& args) -> fl::json {
157 fl::json response = fl::json::object();
158
159 // Validate args: expects [txPin, rxPin]
160 if (!args.is_array() || args.size() != 2) {
161 response.set("error", "InvalidArgs");
162 response.set("message", "Expected [txPin, rxPin]");
163 return response;
164 }
165
166 if (!args[0].is_int() || !args[1].is_int()) {
167 response.set("error", "InvalidPinType");
168 response.set("message", "Pin numbers must be integers");
169 return response;
170 }
171
172 int tx_pin = static_cast<int>(args[0].as_int().value());
173 int rx_pin = static_cast<int>(args[1].as_int().value());
174
175 // Test 1: TX drives LOW, RX has pullup → RX should read LOW if connected
176 pinMode(tx_pin, OUTPUT);
177 pinMode(rx_pin, INPUT_PULLUP);
178 digitalWrite(tx_pin, LOW);
179 delay(5); // Allow signal to settle
180 int rx_when_tx_low = digitalRead(rx_pin);
181
182 // Test 2: TX drives HIGH → RX should read HIGH if connected
183 digitalWrite(tx_pin, HIGH);
184 delay(5); // Allow signal to settle
185 int rx_when_tx_high = digitalRead(rx_pin);
186
187 // Restore pins to safe state
188 pinMode(tx_pin, INPUT);
189 pinMode(rx_pin, INPUT);
190
191 // Analyze results
192 bool connected = (rx_when_tx_low == LOW) && (rx_when_tx_high == HIGH);
193
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);
199
200 if (connected) {
201 response.set("success", true);
202 response.set("message", "GPIO pins are connected");
203 } else {
204 response.set("success", false);
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");
209 } else {
210 response.set("message", "Unexpected GPIO behavior - check wiring");
211 }
212 }
213
214 return response;
215 });
216
217 // ========================================================================
218 // Pin Configuration RPC Functions (Dynamic TX/RX Pin Support)
219 // ========================================================================
220
221 // Register "getPins" function - query current and default pin configuration
222 remote.bind("getPins", [this](const fl::json& args) -> fl::json {
223 fl::json response = fl::json::object();
224 response.set("success", true);
225 response.set("txPin", static_cast<int64_t>(mState->pin_tx));
226 response.set("rxPin", static_cast<int64_t>(mState->pin_rx));
227
228 fl::json defaults = fl::json::object();
229 defaults.set("txPin", static_cast<int64_t>(mState->default_pin_tx));
230 defaults.set("rxPin", static_cast<int64_t>(mState->default_pin_rx));
231 response.set("defaults", defaults);
232
233 // Use the shared chipName() so this agrees with `status`. The
234 // hand-rolled chain here only knew four ESP32 variants and reported
235 // "unknown" for every other target, so an RP2350W answered
236 // getPins -> platform "unknown"
237 // status -> platform "Raspberry Pi Pico 2 W (RP2350)"
238 // on the same firmware (FastLED#3899, whose closeout requires exact
239 // recorded identities). chipName() covers stub, the ESP32 family,
240 // Teensy 4.x and both RP2350 variants.
241 response.set("platform", autoresearch::chipName());
242
243 return response;
244 });
245
246 // Register "setTxPin" function - set TX pin (regenerates test cases)
247 remote.bind("setTxPin", [this](const fl::json& args) -> fl::json {
248 fl::json response = fl::json::object();
249
250 if (!args.is_array() || args.size() != 1 || !args[0].is_int()) {
251 response.set("error", "InvalidArgs");
252 response.set("message", "Expected [pin: int]");
253 return response;
254 }
255
256 int new_pin = static_cast<int>(args[0].as_int().value());
257
258 // Validate pin range (ESP32 GPIO range)
259 if (new_pin < 0 || new_pin > 48) {
260 response.set("error", "InvalidPin");
261 response.set("message", "Pin must be 0-48");
262 return response;
263 }
264
265 int old_pin = mState->pin_tx;
266 if (new_pin != old_pin) {
267 mState->invalidateLedRxValidation();
268 }
269 mState->pin_tx = new_pin;
270
271 response.set("success", true);
272 response.set("txPin", static_cast<int64_t>(new_pin));
273 response.set("previousTxPin", static_cast<int64_t>(old_pin));
274 return response;
275 });
276
277 // Register "setRxPin" function - set RX pin (recreates RX channel)
278 remote.bind("setRxPin", [this](const fl::json& args) -> fl::json {
279 fl::json response = fl::json::object();
280
281 if (!args.is_array() || args.size() != 1 || !args[0].is_int()) {
282 response.set("error", "InvalidArgs");
283 response.set("message", "Expected [pin: int]");
284 return response;
285 }
286
287 int new_pin = static_cast<int>(args[0].as_int().value());
288
289 // Validate pin range (ESP32 GPIO range)
290 if (new_pin < 0 || new_pin > 48) {
291 response.set("error", "InvalidPin");
292 response.set("message", "Pin must be 0-48");
293 return response;
294 }
295
296 int old_pin = mState->pin_rx;
297 bool pin_changed = (new_pin != old_pin);
298 bool rx_recreated = false;
299
300 if (pin_changed) {
301 fl::shared_ptr<fl::RxChannel> replacement_rx;
302 if (mState->rx_factory) {
303 replacement_rx = mState->rx_factory(new_pin);
304 }
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));
309 return response;
310 }
311
312 mState->invalidateLedRxValidation();
313 mState->pin_rx = new_pin;
314 mState->rx_channel = replacement_rx;
315 rx_recreated = true;
316 }
317
318 response.set("success", true);
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);
322 return response;
323 });
324
325 // Register "setPins" function - set both TX and RX pins atomically
326 remote.bind("setPins", [this](const fl::json& args) -> fl::json {
327 fl::json response = fl::json::object();
328
329 // Accept either {txPin, rxPin} object or [txPin, rxPin] array
330 int new_tx_pin = -1;
331 int new_rx_pin = -1;
332
333 if (args.is_array() && args.size() == 1 && args[0].is_object()) {
334 // Object form: [{txPin: int, rxPin: int}]
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());
338 }
339 if (config.contains("rxPin") && config["rxPin"].is_int()) {
340 new_rx_pin = static_cast<int>(config["rxPin"].as_int().value());
341 }
342 } else if (args.is_array() && args.size() == 2) {
343 // Array form: [txPin, rxPin]
344 if (args[0].is_int()) {
345 new_tx_pin = static_cast<int>(args[0].as_int().value());
346 }
347 if (args[1].is_int()) {
348 new_rx_pin = static_cast<int>(args[1].as_int().value());
349 }
350 } else {
351 response.set("error", "InvalidArgs");
352 response.set("message", "Expected [{txPin, rxPin}] or [txPin, rxPin]");
353 return response;
354 }
355
356 // Validate pin ranges
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");
360 return response;
361 }
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");
365 return response;
366 }
367
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;
373
374 fl::shared_ptr<fl::RxChannel> replacement_rx;
375 if (rx_pin_changed) {
376 if (mState->rx_factory) {
377 replacement_rx = mState->rx_factory(new_rx_pin);
378 }
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));
383 return response;
384 }
385 }
386
387 // Commit pins, validation, and channel as one state transition only
388 // after any replacement channel has been created successfully.
389 if (tx_pin_changed || rx_pin_changed) {
390 mState->invalidateLedRxValidation();
391 }
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;
396 rx_recreated = true;
397 }
398
399 response.set("success", true);
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);
405 return response;
406 });
407
408 // Register "findConnectedPins" function - probe adjacent pin pairs to find a jumper wire connection
409 // This allows automatic discovery of TX/RX pin pair without requiring user to specify them
410 remote.bind("findConnectedPins", [this](const fl::json& args) -> fl::json {
412 });
413
414 // Register "help" function - list all RPC functions with descriptions
415 remote.bindStreaming("help", [](fl::JsonStreamWriter& writer, const fl::json& args, const fl::json& requestId) {
416 (void)args;
417 (void)requestId;
418
419 writer.beginObject();
420 writer.member("success", true);
421 writer.member("totalFunctions", static_cast<fl::i64>(kHelpEntryCount));
422 writer.key("functions");
423 writer.beginArray();
424 for (fl::size i = 0; i < kHelpEntryCount; ++i) {
425 streamHelpEntry(writer, kHelpEntries[i]);
426 }
427 writer.endArray();
428 writer.endObject();
429 });
430}
431
432#endif // FL_PLATFORM_HAS_LARGE_MEMORY
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.
int requestId
fl::unique_ptr< fl::Remote > remote
Definition RpcClient.ino:43
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 beginObject() FL_NO_EXCEPT
void key(const char *k) FL_NO_EXCEPT
void member(const char *k, const char *s) FL_NO_EXCEPT
JSON-RPC server with scheduling support.
Definition remote.h:42
fl::optional< i64 > as_int() const FL_NO_EXCEPT
Definition json.h:398
void set(const fl::string &key, const json &value) FL_NO_EXCEPT
Definition json.h:866
bool contains(size_t idx) const FL_NO_EXCEPT
Definition json.h:790
static json object() FL_NO_EXCEPT
Definition json.h:857
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()
unsigned char u8
Definition stdint.h:131
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...
Definition delay.h:98
void setLogLevel(u8 level)
Set the global log level.
@ FL_LOG_LEVEL_DEBUG
All logging including debug (default)
Definition cstdio.h:28
@ FL_LOG_LEVEL_NONE
No logging (completely silent)
Definition cstdio.h:24
void pinMode(int pin, PinMode mode)
Set pin mode (input, output, pull-up, pull-down)
Definition pin.cpp.hpp:378
PinValue digitalRead(int pin)
Read digital value from pin.
Definition pin.cpp.hpp:55
void digitalWrite(int pin, PinValue val)
Write digital value to pin.
Definition pin.cpp.hpp:51
corkscrew_args args
Definition old.h:149
Promise-based fluent API for FastLED - standalone async primitives.
Umbrella header for SIMD subsystem.