FastLED 3.10.9
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 {"dualSpiHostLoopback", "Phase 4: Utility", "[{activeHost:2|3, dummyFirst:false|true}]", "{success, activeHost, dummyFirst, verifiedLeds, wireMismatches, rawEdges}", "On ESP32 boards with two SPI hosts, register one APA102 output on each host and verify eight LED frames from GPIO 1 to GPIO 2. Reset between calls."},
114 {"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."},
115};
116
117static const fl::size kHelpEntryCount = sizeof(kHelpEntries) / sizeof(kHelpEntries[0]);
118
119} // namespace
120
121
123 // Register "setDebug" function - enable/disable runtime debug logging
124 remote.bind("setDebug", [this](const fl::json& args) -> fl::json {
125 fl::json response = fl::json::object();
126
127 // Validate args: expects [enabled: bool]
128 if (!args.is_array() || args.size() != 1) {
129 response.set("success", false);
130 response.set("error", "InvalidArgs");
131 response.set("message", "Expected [enabled: bool]");
132 return response;
133 }
134
135 if (!args[0].is_bool()) {
136 response.set("success", false);
137 response.set("error", "InvalidType");
138 response.set("message", "Argument must be boolean");
139 return response;
140 }
141
142 bool enabled = args[0].as_bool().value();
143 mState->debug_enabled = enabled;
144 fl::setLogLevel(static_cast<fl::u8>(
147
148 response.set("success", true);
149 response.set("debug_enabled", enabled);
150 response.set("message", enabled ? "Debug logging enabled" : "Debug logging disabled");
151
152 return response;
153 });
154
155 // Register "testGpioConnection" function - test if TX and RX pins are electrically connected
156 // This is a pre-test to diagnose hardware connection issues before running validation
157 remote.bind("testGpioConnection", [](const fl::json& args) -> fl::json {
158 fl::json response = fl::json::object();
159
160 // Validate args: expects [txPin, rxPin]
161 if (!args.is_array() || args.size() != 2) {
162 response.set("error", "InvalidArgs");
163 response.set("message", "Expected [txPin, rxPin]");
164 return response;
165 }
166
167 if (!args[0].is_int() || !args[1].is_int()) {
168 response.set("error", "InvalidPinType");
169 response.set("message", "Pin numbers must be integers");
170 return response;
171 }
172
173 int tx_pin = static_cast<int>(args[0].as_int().value());
174 int rx_pin = static_cast<int>(args[1].as_int().value());
175
176 // Test 1: TX drives LOW, RX has pullup → RX should read LOW if connected
177 pinMode(tx_pin, OUTPUT);
178 // Same-pin RMT loopback needs the output to remain enabled. Turning
179 // that same pin into an input would only measure its pullup.
180 if (rx_pin != tx_pin) {
181 pinMode(rx_pin, INPUT_PULLUP);
182 }
183 digitalWrite(tx_pin, LOW);
184 delay(5); // Allow signal to settle
185 int rx_when_tx_low = digitalRead(rx_pin);
186
187 // Test 2: TX drives HIGH → RX should read HIGH if connected
188 digitalWrite(tx_pin, HIGH);
189 delay(5); // Allow signal to settle
190 int rx_when_tx_high = digitalRead(rx_pin);
191
192 // Restore pins to safe state
193 pinMode(tx_pin, INPUT);
194 pinMode(rx_pin, INPUT);
195
196 // Analyze results
197 bool connected = (rx_when_tx_low == LOW) && (rx_when_tx_high == HIGH);
198
199 response.set("txPin", static_cast<int64_t>(tx_pin));
200 response.set("rxPin", static_cast<int64_t>(rx_pin));
201 response.set("rxWhenTxLow", rx_when_tx_low == LOW ? "LOW" : "HIGH");
202 response.set("rxWhenTxHigh", rx_when_tx_high == HIGH ? "HIGH" : "LOW");
203 response.set("connected", connected);
204
205 if (connected) {
206 response.set("success", true);
207 response.set("message", "GPIO pins are connected");
208 } else {
209 response.set("success", false);
210 if (rx_when_tx_low == HIGH && rx_when_tx_high == HIGH) {
211 response.set("message", "RX pin stuck HIGH - no connection detected (check jumper wire)");
212 } else if (rx_when_tx_low == LOW && rx_when_tx_high == LOW) {
213 response.set("message", "RX pin stuck LOW - possible short to ground");
214 } else {
215 response.set("message", "Unexpected GPIO behavior - check wiring");
216 }
217 }
218
219 return response;
220 });
221
222 // ========================================================================
223 // Pin Configuration RPC Functions (Dynamic TX/RX Pin Support)
224 // ========================================================================
225
226 // Register "getPins" function - query current and default pin configuration
227 remote.bind("getPins", [this](const fl::json& args) -> fl::json {
228 fl::json response = fl::json::object();
229 response.set("success", true);
230 response.set("txPin", static_cast<int64_t>(mState->pin_tx));
231 response.set("rxPin", static_cast<int64_t>(mState->pin_rx));
232
233 fl::json defaults = fl::json::object();
234 defaults.set("txPin", static_cast<int64_t>(mState->default_pin_tx));
235 defaults.set("rxPin", static_cast<int64_t>(mState->default_pin_rx));
236 response.set("defaults", defaults);
237
238 // Use the shared chipName() so this agrees with `status`. The
239 // hand-rolled chain here only knew four ESP32 variants and reported
240 // "unknown" for every other target, so an RP2350W answered
241 // getPins -> platform "unknown"
242 // status -> platform "Raspberry Pi Pico 2 W (RP2350)"
243 // on the same firmware (FastLED#3899, whose closeout requires exact
244 // recorded identities). chipName() covers stub, the ESP32 family,
245 // Teensy 4.x and both RP2350 variants.
246 response.set("platform", autoresearch::chipName());
247
248 return response;
249 });
250
251 // Register "setTxPin" function - set TX pin (regenerates test cases)
252 remote.bind("setTxPin", [this](const fl::json& args) -> fl::json {
253 fl::json response = fl::json::object();
254
255 if (!args.is_array() || args.size() != 1 || !args[0].is_int()) {
256 response.set("error", "InvalidArgs");
257 response.set("message", "Expected [pin: int]");
258 return response;
259 }
260
261 int new_pin = static_cast<int>(args[0].as_int().value());
262
263 // Validate pin range (ESP32 GPIO range)
264 if (new_pin < 0 || new_pin > 48) {
265 response.set("error", "InvalidPin");
266 response.set("message", "Pin must be 0-48");
267 return response;
268 }
269
270 int old_pin = mState->pin_tx;
271 if (new_pin != old_pin) {
272 mState->invalidateLedRxValidation();
273 }
274 mState->pin_tx = new_pin;
275
276 response.set("success", true);
277 response.set("txPin", static_cast<int64_t>(new_pin));
278 response.set("previousTxPin", static_cast<int64_t>(old_pin));
279 return response;
280 });
281
282 // Register "setRxPin" function - set RX pin (recreates RX channel)
283 remote.bind("setRxPin", [this](const fl::json& args) -> fl::json {
284 fl::json response = fl::json::object();
285
286 if (!args.is_array() || args.size() != 1 || !args[0].is_int()) {
287 response.set("error", "InvalidArgs");
288 response.set("message", "Expected [pin: int]");
289 return response;
290 }
291
292 int new_pin = static_cast<int>(args[0].as_int().value());
293
294 // Validate pin range (ESP32 GPIO range)
295 if (new_pin < 0 || new_pin > 48) {
296 response.set("error", "InvalidPin");
297 response.set("message", "Pin must be 0-48");
298 return response;
299 }
300
301 int old_pin = mState->pin_rx;
302 bool pin_changed = (new_pin != old_pin);
303 bool rx_recreated = false;
304
305 if (pin_changed) {
306 fl::shared_ptr<fl::RxChannel> replacement_rx;
307 if (mState->rx_factory) {
308 replacement_rx = mState->rx_factory(new_pin);
309 }
310 if (!replacement_rx) {
311 response.set("error", "RxChannelCreationFailed");
312 response.set("message", "Failed to create RX channel on new pin");
313 response.set("pinRx", static_cast<int64_t>(new_pin));
314 return response;
315 }
316
317 mState->invalidateLedRxValidation();
318 mState->pin_rx = new_pin;
319 mState->rx_channel = replacement_rx;
320 rx_recreated = true;
321 }
322
323 response.set("success", true);
324 response.set("rxPin", static_cast<int64_t>(new_pin));
325 response.set("previousRxPin", static_cast<int64_t>(old_pin));
326 response.set("rxChannelRecreated", rx_recreated);
327 return response;
328 });
329
330 // Register "setPins" function - set both TX and RX pins atomically
331 remote.bind("setPins", [this](const fl::json& args) -> fl::json {
332 fl::json response = fl::json::object();
333
334 // Accept either {txPin, rxPin} object or [txPin, rxPin] array
335 int new_tx_pin = -1;
336 int new_rx_pin = -1;
337
338 if (args.is_array() && args.size() == 1 && args[0].is_object()) {
339 // Object form: [{txPin: int, rxPin: int}]
340 fl::json config = args[0];
341 if (config.contains("txPin") && config["txPin"].is_int()) {
342 new_tx_pin = static_cast<int>(config["txPin"].as_int().value());
343 }
344 if (config.contains("rxPin") && config["rxPin"].is_int()) {
345 new_rx_pin = static_cast<int>(config["rxPin"].as_int().value());
346 }
347 } else if (args.is_array() && args.size() == 2) {
348 // Array form: [txPin, rxPin]
349 if (args[0].is_int()) {
350 new_tx_pin = static_cast<int>(args[0].as_int().value());
351 }
352 if (args[1].is_int()) {
353 new_rx_pin = static_cast<int>(args[1].as_int().value());
354 }
355 } else {
356 response.set("error", "InvalidArgs");
357 response.set("message", "Expected [{txPin, rxPin}] or [txPin, rxPin]");
358 return response;
359 }
360
361 // Validate pin ranges
362 if (new_tx_pin < 0 || new_tx_pin > 48) {
363 response.set("error", "InvalidTxPin");
364 response.set("message", "TX pin must be 0-48");
365 return response;
366 }
367 if (new_rx_pin < 0 || new_rx_pin > 48) {
368 response.set("error", "InvalidRxPin");
369 response.set("message", "RX pin must be 0-48");
370 return response;
371 }
372
373 int old_tx_pin = mState->pin_tx;
374 int old_rx_pin = mState->pin_rx;
375 bool tx_pin_changed = (new_tx_pin != old_tx_pin);
376 bool rx_pin_changed = (new_rx_pin != old_rx_pin);
377 bool rx_recreated = false;
378
379 fl::shared_ptr<fl::RxChannel> replacement_rx;
380 if (rx_pin_changed) {
381 if (mState->rx_factory) {
382 replacement_rx = mState->rx_factory(new_rx_pin);
383 }
384 if (!replacement_rx) {
385 response.set("error", "RxChannelCreationFailed");
386 response.set("message", "Failed to create RX channel - previous pins preserved");
387 response.set("pinRx", static_cast<int64_t>(new_rx_pin));
388 return response;
389 }
390 }
391
392 // Commit pins, validation, and channel as one state transition only
393 // after any replacement channel has been created successfully.
394 if (tx_pin_changed || rx_pin_changed) {
395 mState->invalidateLedRxValidation();
396 }
397 mState->pin_tx = new_tx_pin;
398 mState->pin_rx = new_rx_pin;
399 if (rx_pin_changed) {
400 mState->rx_channel = replacement_rx;
401 rx_recreated = true;
402 }
403
404 response.set("success", true);
405 response.set("txPin", static_cast<int64_t>(new_tx_pin));
406 response.set("rxPin", static_cast<int64_t>(new_rx_pin));
407 response.set("previousTxPin", static_cast<int64_t>(old_tx_pin));
408 response.set("previousRxPin", static_cast<int64_t>(old_rx_pin));
409 response.set("rxChannelRecreated", rx_recreated);
410 return response;
411 });
412
413 // Register "findConnectedPins" function - probe adjacent pin pairs to find a jumper wire connection
414 // This allows automatic discovery of TX/RX pin pair without requiring user to specify them
415 remote.bind("findConnectedPins", [this](const fl::json& args) -> fl::json {
417 });
418
419 // Register "help" function - list all RPC functions with descriptions
420 remote.bindStreaming("help", [](fl::JsonStreamWriter& writer, const fl::json& args, const fl::json& requestId) {
421 (void)args;
422 (void)requestId;
423
424 writer.beginObject();
425 writer.member("success", true);
426 writer.member("totalFunctions", static_cast<fl::i64>(kHelpEntryCount));
427 writer.key("functions");
428 writer.beginArray();
429 for (fl::size i = 0; i < kHelpEntryCount; ++i) {
430 streamHelpEntry(writer, kHelpEntries[i]);
431 }
432 writer.endArray();
433 writer.endObject();
434 });
435}
436
437#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:873
bool contains(size_t idx) const FL_NO_EXCEPT
Definition json.h:790
static json object() FL_NO_EXCEPT
Definition json.h:864
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) FL_NO_EXCEPT
Set the global log level.
@ FL_LOG_LEVEL_DEBUG
All logging including debug (default)
Definition cstdio.h:42
@ FL_LOG_LEVEL_NONE
No logging (completely silent)
Definition cstdio.h:38
fl::i64 i64
Definition s16x16x4.h:222
void digitalWrite(int pin, PinValue val) FL_NO_EXCEPT
Write digital value to pin.
Definition pin.cpp.hpp:51
PinValue digitalRead(int pin) FL_NO_EXCEPT
Read digital value from pin.
Definition pin.cpp.hpp:55
void pinMode(int pin, PinMode mode) FL_NO_EXCEPT
Set pin mode (input, output, pull-up, pull-down)
Definition pin.cpp.hpp:398
corkscrew_args args
Definition old.h:149
Promise-based fluent API for FastLED - standalone async primitives.
Umbrella header for SIMD subsystem.