FastLED 3.10.9
Loading...
Searching...
No Matches
AutoResearchLowMemory.h
Go to the documentation of this file.
1#pragma once
2//
3// examples/AutoResearch/AutoResearchLowMemory.h
4//
5// Low-memory AutoResearch bring-up surface for parts whose flash budget
6// cannot fit the full ESP32 / Teensy LED-protocol matrix. Originally lived
7// as the standalone `examples/AutoResearchLpc/AutoResearchLpc.ino` sketch;
8// retired and folded in here per FastLED #3030 once the soft-FP cascade
9// from #3022 phase 2 freed up the budget on LPC8xx.
10//
11// Included from the main `AutoResearch.ino` when
12// `FL_PLATFORM_HAS_LARGE_MEMORY == 0` (Low + Tiny tiers per the FastLED
13// #3000 memory classification).
14//
15// Same JSON-RPC contract as the retired LPC sketch:
16// echo: [int] -> int
17// pinToggleRx: [tx_pin, rx_pin, freq_hz, duration_ms] -> CSV
18// ws2812SctTest: [test_case, tx_pin, rx_pin, capture_ms] -> CSV
19// See FastLED #3021 for the SCT-RX bring-up flags.
20//
21
22#include "platforms/arm/lpc/is_lpc.h" // ok platform headers - LPC RX driver gate
23
24// Gate FL_DBG to no-op. Without `RELEASE`, fl/log/log.h auto-sets
25// FASTLED_FORCE_DBG=1, which expands every FL_DBG call site (inside
26// fl::Remote etc.) through the fl::println formatting machinery and
27// blows ~4 KB of flash budget. fbuild's nxplpc orchestrator does not
28// yet propagate platformio.ini's `build_flags`, so it's defined here.
29#ifndef RELEASE
30#define RELEASE 1
31#endif
32
33// Dedicated #3039 codec-test builds keep the low-memory surface to echo +
34// IEEE754 only. The SCT/WS2812 handlers are useful diagnostics, but they are
35// unrelated to the codec and push LPC845 past its flash budget when combined.
36#if defined(FL_AUTORESEARCH_IEEE754) && FL_AUTORESEARCH_IEEE754
37#define FASTLED_AUTORESEARCH_IEEE754_MODE 1
38#endif
39
40// ESP8266 has 4 MB of flash but too little DRAM for the full harness, so its
41// low-memory build always carries the IEEE754 codec check next to echo.
42#include "platforms/esp/is_esp.h" // ok platform headers // ok platform specific - ESP8266 surface gate
43#if defined(FL_IS_ESP8266) && !defined(FASTLED_AUTORESEARCH_IEEE754_MODE) // ok platform specific - ESP8266 low-DRAM build always carries IEEE754 check
44#define FASTLED_AUTORESEARCH_IEEE754_MODE 1
45#endif
46// The clockless bench reports the driver's frame/retry counters.
47#if defined(FL_IS_ESP8266) && !defined(FASTLED_DEBUG_COUNT_FRAME_RETRIES) // ok platform specific - clockless bench reads ESP8266 retry counters
48#define FASTLED_DEBUG_COUNT_FRAME_RETRIES 1
49#endif
50
51#if !defined(FASTLED_AUTORESEARCH_LPC_UART_DMA)
52#define FASTLED_AUTORESEARCH_LPC_UART_DMA 0
53#endif
54
55// Opt in to the SCT input-capture RX backend (FastLED #3021). With this
56// flag set, `LpcSctRxChannel::begin()` programs the SCT for hardware
57// edge-capture; without it the driver is a no-op stub (host tests still
58// work via `injectEdges()`).
59#if !defined(FASTLED_AUTORESEARCH_IEEE754_MODE) && \
60 !defined(FASTLED_AUTORESEARCH_FAULT_TEST)
61#ifndef FASTLED_LPC_RX_SCT
62#define FASTLED_LPC_RX_SCT 1
63#endif
64#endif
65
66// The LPC845-BRK low-memory build fits FastLED + Remote + the WS2812 loopback
67// RPC in normal mode. The dedicated IEEE754 mode deliberately omits it.
68#if defined(FL_IS_ARM_LPC_845) && !defined(FASTLED_AUTORESEARCH_IEEE754_MODE) && \
69 !defined(FASTLED_AUTORESEARCH_FAULT_TEST) && !defined(FASTLED_LPC_SPI_DMA) && \
70 !defined(FASTLED_LPC_PWM_DMA) && !FASTLED_AUTORESEARCH_LPC_UART_DMA
71#define FASTLED_AUTORESEARCH_LPC_WS2812 1
72#endif
73
74// The WS2812 loopback uses the SCT->DMA RX capture path. The UART clockless
75// verifier uses USART RX-DMA instead, so keep SCT-DMA out of that build;
76// UM11029 Table 296 fixes USART1_RX/USART1_TX on DMA channels 2/3.
77#if defined(FASTLED_AUTORESEARCH_LPC_WS2812) && \
78 !defined(FASTLED_LPC_RX_SCT_DMA)
79#define FASTLED_LPC_RX_SCT_DMA 1
80#endif
81
82#include <Arduino.h>
83#include "fl/remote/remote.h"
86#include "fl/wdt/watchdog.h"
87#include "fl/log/log.h"
88#include "fl/stl/cstdio.h" // fl::serial_begin -- HWCDC-safe Serial.begin wrapper
89#include "fl/stl/singleton.h"
90
91#if defined(FASTLED_AUTORESEARCH_IEEE754_MODE)
92#include "AutoResearchIeee754.h"
93#endif
94
95// The host-stub example DLL build (`tests/shared/example_dll_wrapper_template.cpp`)
96// compiles this sketch on Linux to surface ABI breaks. The RX SCT driver
97// types are platform-gated behind `FL_IS_ARM_LPC` -- they only exist on
98// the real LPC build. So the include + every RX usage below must be gated
99// the same way.
100#if defined(FL_IS_ARM_LPC) && !defined(FASTLED_AUTORESEARCH_IEEE754_MODE) && \
101 !defined(FASTLED_AUTORESEARCH_FAULT_TEST)
103#include "fl/stl/strstream.h"
104#endif
105
106#if defined(FASTLED_AUTORESEARCH_LPC_WS2812)
107#include "FastLED.h"
109#endif
110
111// FastLED #3468: channels-API SCT+DMA clockless AutoResearch harness.
112// Opt-in via `-DFASTLED_LPC_PWM_DMA=1`; the header self-gates on
113// `FL_IS_ARM_LPC_845 && FASTLED_LPC_PWM_DMA` so unrelated builds are
114// unaffected. Note: exclusive with FASTLED_AUTORESEARCH_LPC_WS2812 —
115// both target the same SCT peripheral and would collide at runtime.
116// The host-side `bash autoresearch --pwm-dma-cl` wrapper enforces
117// mutual exclusion with `--dma-spi` and the legacy `ws2812SctTest`.
118#if defined(FL_IS_ARM_LPC_845) && defined(FASTLED_LPC_PWM_DMA)
120#endif
121
122// FastLED #3456: LPC845 SPI+DMA async AutoResearch harness (Phase 1 of
123// #3453 bench bring-up). Opt-in via `-DFASTLED_LPC_SPI_DMA=1`; the
124// header self-gates on `FL_IS_ARM_LPC_845 && FASTLED_LPC_SPI_DMA`.
125// LPC804 is NOT supported — silicon has no DMA peripheral. See
126// `agents/docs/peripheral-existence.md`. Mutually exclusive with
127// FASTLED_LPC_PWM_DMA (both claim DMA0 channels and the LowMemory
128// flash budget doesn't fit both). Enforced host-side by
129// `bash autoresearch` refusing `--dma-spi --pwm-dma-cl`.
130#if defined(FL_IS_ARM_LPC_845) && defined(FASTLED_LPC_SPI_DMA) && \
131 defined(FASTLED_LPC_PWM_DMA)
132#error "FASTLED_LPC_SPI_DMA and FASTLED_LPC_PWM_DMA are mutually exclusive on LPC845: both claim DMA0 channels and the LowMemory flash budget doesn't fit both. Pick one for the AutoResearch build."
133#endif
134#if defined(FL_IS_ARM_LPC_845) && defined(FASTLED_LPC_SPI_DMA)
135#include "AutoResearchSpiDma.h"
136#endif
137
138// #3453 follow-up: LPC845 async UART TX harness (DMA + ISR chunk chain) and
139// #3611 UART clockless loopback verifier. The UART DMA driver is now the
140// LPC845 library default, so the AutoResearch RPC harness has its own flag.
141#if defined(FL_IS_ARM_LPC_845) && FASTLED_AUTORESEARCH_LPC_UART_DMA && \
142 !FASTLED_LPC_UART_DMA
143#error "FASTLED_AUTORESEARCH_LPC_UART_DMA requires FASTLED_LPC_UART_DMA=1."
144#endif
145#if defined(FL_IS_ARM_LPC_845) && FASTLED_AUTORESEARCH_LPC_UART_DMA && \
146 (defined(FASTLED_LPC_SPI_DMA) || defined(FASTLED_LPC_PWM_DMA))
147#error "FASTLED_AUTORESEARCH_LPC_UART_DMA is mutually exclusive with the SPI/PWM DMA harnesses on LPC845: the LowMemory flash budget fits one bench at a time. Pick one for the AutoResearch build."
148#endif
149#if defined(FL_IS_ARM_LPC_845) && FASTLED_AUTORESEARCH_LPC_UART_DMA
150#include "AutoResearchUartDma.h"
151#endif
152
153// ESP8266 UART WS2812 driver benches (pad self-capture, no jumper needed).
154#if defined(FL_IS_ESP8266) // ok platform specific - ESP8266 bring-up benches
155#include "AutoResearchEsp8266.h"
156#endif
157
158namespace autoresearch {
159namespace low_memory {
160
163
165 : remote(fl::createSerialRequestSource(),
166 fl::createSerialResponseSink("REMOTE: ")) {}
167};
168
172
173#if defined(FL_IS_ARM_LPC) && !defined(FASTLED_AUTORESEARCH_IEEE754_MODE) && \
174 !defined(FASTLED_AUTORESEARCH_FAULT_TEST)
175struct PinToggleState {
176 fl::EdgeTime edges[64];
177};
178
179inline PinToggleState& pinToggleState() {
181}
182#endif
183
184#if defined(FASTLED_AUTORESEARCH_LPC_WS2812)
185struct Ws2812State {
186 CRGB leds[100];
187 uint8_t expected[300];
188 uint8_t decoded[300];
189 fl::EdgeTime probe[64];
190 bool fastled_initialized;
191
192 Ws2812State() : fastled_initialized(false) {}
193};
194
195inline Ws2812State& ws2812State() {
197}
198#endif
199
200} // namespace low_memory
201} // namespace autoresearch
202
203namespace {
204
205#if defined(FL_IS_ARM_LPC) && !defined(FASTLED_AUTORESEARCH_IEEE754_MODE) && \
206 !defined(FASTLED_AUTORESEARCH_FAULT_TEST)
207// Period-stat helpers for `pinToggleRx`. Mirrors the FlexIO RX benchmark
208// logic in `examples/AutoResearch/AutoResearchRemote.cpp::flexioRxBenchmark`
209// but inline so we don't pull in the AutoResearch ObjectFLED bus.
210struct LowMemPinTogglePeriodStats {
211 fl::u32 periods;
212 fl::u32 mean_ns;
213 fl::u32 sigma_ns;
214 fl::u32 min_ns;
215 fl::u32 max_ns;
216};
217
218inline LowMemPinTogglePeriodStats computeLowMemPeriodStats(
219 const fl::EdgeTime* edges, fl::size edge_count) FL_NO_EXCEPT {
220 LowMemPinTogglePeriodStats s{0, 0, 0, 0, 0};
221 fl::u64 sum = 0;
222 fl::u64 sum_sq = 0;
223 fl::u32 min_ns = 0xFFFFFFFFu;
224 fl::u32 max_ns = 0;
225 fl::u32 count = 0;
226 for (fl::size i = 0; i + 1 < edge_count; i += 2) {
227 const fl::u32 period_ns =
228 static_cast<fl::u32>(edges[i].ns) +
229 static_cast<fl::u32>(edges[i + 1].ns);
230 if (period_ns == 0) continue;
231 sum += period_ns;
232 sum_sq += static_cast<fl::u64>(period_ns) * static_cast<fl::u64>(period_ns);
233 if (period_ns < min_ns) min_ns = period_ns;
234 if (period_ns > max_ns) max_ns = period_ns;
235 ++count;
236 }
237 if (count == 0) return s;
238 const fl::u32 mean = static_cast<fl::u32>(sum / static_cast<fl::u64>(count));
239 const fl::u64 mean64 = static_cast<fl::u64>(mean);
240 const fl::u64 var = (sum_sq / static_cast<fl::u64>(count)) - (mean64 * mean64);
241 fl::u32 sig = 0;
242 while (static_cast<fl::u64>(sig + 1) * static_cast<fl::u64>(sig + 1) <= var) ++sig;
243 s.periods = count;
244 s.mean_ns = mean;
245 s.sigma_ns = sig;
246 s.min_ns = (min_ns == 0xFFFFFFFFu) ? 0u : min_ns;
247 s.max_ns = max_ns;
248 return s;
249}
250#endif // FL_IS_ARM_LPC && !FASTLED_AUTORESEARCH_IEEE754_MODE && !FAULT_TEST
251
252#if defined(FL_IS_ARM_LPC_845) && defined(FASTLED_AUTORESEARCH_FAULT_TEST)
253FL_NO_RETURN FL_NO_INLINE void triggerLowMemoryNullFault() FL_NO_EXCEPT {
254 volatile fl::u32* const p =
255 reinterpret_cast<volatile fl::u32*>(0u); // ok reinterpret cast - fault test
256 *p = 0x3302u;
257 while (true) {}
258}
259
261FL_DISABLE_WARNING(infinite-recursion)
262FL_NO_INLINE int recurseLowMemoryStackBlow(int depth) FL_NO_EXCEPT {
263 volatile fl::u8 pad[64] = {0};
264 pad[static_cast<fl::u8>(depth) & 0x3Fu] = static_cast<fl::u8>(depth);
265 // LPC845/M0+ has no stack-limit trap; unbounded recursion can corrupt the
266 // runtime before the HardFault handler can emit. Burn stack frames, then
267 // force the HardFault path while the diagnostic surface is still intact.
268 if (depth >= 32) {
269 triggerLowMemoryNullFault();
270 }
271 return recurseLowMemoryStackBlow(depth + 1) + pad[depth & 0x3F];
272}
274#endif // FL_IS_ARM_LPC_845 && FASTLED_AUTORESEARCH_FAULT_TEST
275
276} // namespace
277
279 fl::serial_begin(115200);
280
281 // Arm the watchdog so a hung sketch unbricks itself on crash.
283
284 // Emit a literal-only warning so the autoresearch harness can verify
285 // the FL_WARN log pipeline reaches the host.
286 FL_WARN_LIT("FL_WARN: low-memory bring-up OK");
287
288 // The C++ inline-ODR rule guarantees a single shared instance across
289 // every translation unit that calls this inline function, which is
290 // exactly what we want (one Remote bound to the singleton Serial
291 // transport). The included-once-per-sketch model of `.ino` builds
292 // also reduces to a single TU in practice.
294 remote.bind("echo", [](int v) -> int {
295 FL_WARN_LIT("FL_WARN: echo invoked");
296 return v;
297 });
298
299#if defined(FL_IS_ARM_LPC_845) && defined(FASTLED_AUTORESEARCH_FAULT_TEST)
300 remote.bind("trigger_oom", []() -> bool {
301 // Intentional #3302 fault input: ask the 16 KB SRAM target for
302 // far more RAM than exists. If the platform allocator returns null
303 // instead of trapping, dereference null to exercise HardFault emit.
304 volatile char* const p = new char[16UL * 1024UL * 1024UL]; // ok bare allocation
305 if (p == nullptr) {
306 triggerLowMemoryNullFault();
307 }
308 p[0] = 0x33;
309 triggerLowMemoryNullFault();
310 return false;
311 });
312 remote.bind("trigger_stackblow", []() -> bool {
313 (void)recurseLowMemoryStackBlow(0);
314 triggerLowMemoryNullFault();
315 return false;
316 });
317#endif // FL_IS_ARM_LPC_845 && FASTLED_AUTORESEARCH_FAULT_TEST
318
319#if defined(FL_IS_ESP8266) // ok platform specific - ESP8266 bring-up benches
320 autoresearch::esp8266::bindEsp8266Benches(remote);
321#endif
322
323#if defined(FASTLED_AUTORESEARCH_IEEE754_MODE)
324 remote.bind("ieee754CodecTest", [](const fl::json& args) -> fl::json {
325 (void)args;
326 const auto r = autoresearch::ieee754_check::run();
327 fl::json response = fl::json::object();
328 response.set("success", r.success);
329 response.set("tests_run", static_cast<int64_t>(r.tests_run));
330 response.set("tests_failed", static_cast<int64_t>(r.tests_failed));
331 response.set("first_failure", r.first_failure ? r.first_failure : "");
332 response.set("expected_bits", static_cast<int64_t>(r.expected_bits));
333 response.set("actual_bits", static_cast<int64_t>(r.actual_bits));
334 return response;
335 });
336#endif
337
338#if defined(FL_IS_ARM_LPC) && !defined(FASTLED_AUTORESEARCH_IEEE754_MODE) && \
339 !defined(FASTLED_AUTORESEARCH_FAULT_TEST)
340 // pinToggleRx (FastLED #3021 Phase 1) — bit-bang square wave on tx_pin
341 // and capture SCT edges on rx_pin. CSV stats out.
342#if !defined(FASTLED_AUTORESEARCH_LPC_DMA_LOOPBACK)
343 // Left out of the RX-DMA loopback builds (--ws2812-loopback/--pwm-dma-cl):
344 // with the DMA capture linked, the 64 KB LPC845 has no room for this
345 // unrelated ~1 KB diagnostic. --pin-toggle-rx builds keep it.
346 remote.bind("pinToggleRx",
347 [](int tx_pin, int rx_pin, int freq_hz, int duration_ms) -> fl::string {
348 if (tx_pin < 0 || rx_pin < 0 || freq_hz <= 0 || duration_ms <= 0) {
349 return fl::string("0,0,0,0,0,0,0");
350 }
351 const fl::u32 expected_edges =
352 2u * static_cast<fl::u32>(freq_hz) *
353 static_cast<fl::u32>(duration_ms) / 1000u;
354 // Keep both the RX vector and diagnostic copy buffer small on
355 // 16 KB SRAM LPC8xx parts. Larger static buffers regressed early
356 // init by leaving too little room for stack/heap (FastLED #3200),
357 // while stack buffers previously overflowed during this handler
358 // (FastLED #3125).
359 constexpr fl::u32 kLowMemEdgeBufSize = 64u;
360 fl::u32 cap = expected_edges + (expected_edges / 2u);
361 if (cap < 2u) cap = 2u;
362 if (cap > kLowMemEdgeBufSize) cap = kLowMemEdgeBufSize;
363
364 auto rx = fl::LpcSctRxChannel::create(rx_pin);
365 if (!rx) return fl::string("0,0,0,0,0,0,0");
366
367 fl::RxConfig cfg;
368 cfg.buffer_size = cap;
369 cfg.start_low = true;
370 if (!rx->begin(cfg)) return fl::string("0,0,0,0,0,0,0");
371
372 pinMode(tx_pin, OUTPUT);
373 digitalWrite(tx_pin, LOW);
374
375 const fl::u32 half_us = 500000u / static_cast<fl::u32>(freq_hz);
376 const fl::u32 cycles = static_cast<fl::u32>(freq_hz) *
377 static_cast<fl::u32>(duration_ms) / 1000u;
378
379 for (fl::u32 i = 0; i < cycles; ++i) {
380 digitalWrite(tx_pin, HIGH);
381 delayMicroseconds(half_us);
382 rx->pollOnce();
383 digitalWrite(tx_pin, LOW);
384 delayMicroseconds(half_us);
385 rx->pollOnce();
386 if ((i & 0x7Fu) == 0u) {
388 }
389 }
390 rx->wait(1);
391
392 // Static storage keeps this off the stack; the small fixed size
393 // keeps .bss below the LPC845-BRK early-init failure threshold.
394 fl::EdgeTime* edges_buf = autoresearch::low_memory::pinToggleState().edges;
395 const fl::size n_read = rx->getRawEdgeTimes(
396 fl::span<fl::EdgeTime>(edges_buf, sizeof(edges_buf) / sizeof(edges_buf[0])));
397
398 LowMemPinTogglePeriodStats stats = computeLowMemPeriodStats(edges_buf, n_read);
399
400 fl::sstream s;
401 const bool success = (stats.periods > 0);
402 s << (success ? 1 : 0) << ',' << static_cast<fl::u32>(n_read) << ','
403 << stats.periods << ',' << stats.mean_ns << ',' << stats.sigma_ns << ','
404 << stats.min_ns << ',' << stats.max_ns;
405 return s.str();
406 });
407#endif // !FASTLED_AUTORESEARCH_LPC_DMA_LOOPBACK
408
409#if defined(FASTLED_AUTORESEARCH_LPC_WS2812)
410 // ws2812SctTest (FastLED #3021 Phase 2) -- WS2812 byte-match loopback.
411 remote.bind("ws2812SctTest",
412 [](int test_case, int tx_pin, int rx_pin, int capture_ms) -> fl::string {
413 if (test_case < 0 || test_case > 4 ||
414 tx_pin < 0 || rx_pin < 0 || capture_ms <= 0) {
415 return fl::string("0,0,0,0,0,0,0,0");
416 }
417 int num_leds = 1;
418 switch (test_case) {
419 case 1: num_leds = 3; break;
420 case 4: num_leds = 100; break;
421 default: num_leds = 1; break;
422 }
423 autoresearch::low_memory::Ws2812State& ws_state =
424 autoresearch::low_memory::ws2812State();
425 CRGB* leds_buf = ws_state.leds;
426 for (int i = 0; i < num_leds; ++i) {
427 switch (test_case) {
428 case 0: leds_buf[i] = CRGB(0xFF, 0x00, 0x00); break;
429 case 1: {
430 if (i == 0) leds_buf[i] = CRGB(0xFF, 0x00, 0x00);
431 else if (i == 1) leds_buf[i] = CRGB(0x00, 0xFF, 0x00);
432 else leds_buf[i] = CRGB(0x00, 0x00, 0xFF);
433 break;
434 }
435 case 2: leds_buf[i] = CRGB(0x00, 0x00, 0x00); break;
436 case 3: leds_buf[i] = CRGB(0xFF, 0xFF, 0xFF); break;
437 case 4: {
438 const uint8_t r = (i % 3 == 0) ? 0xFFu : 0u;
439 const uint8_t g = (i % 3 == 1) ? 0xFFu : 0u;
440 const uint8_t b = (i % 3 == 2) ? 0xFFu : 0u;
441 leds_buf[i] = CRGB(r, g, b);
442 break;
443 }
444 }
445 }
446 const int expected_bytes = num_leds * 3;
447 uint8_t* expected_buf = ws_state.expected;
448 for (int i = 0; i < num_leds; ++i) {
449 expected_buf[i * 3 + 0] = leds_buf[i].g;
450 expected_buf[i * 3 + 1] = leds_buf[i].r;
451 expected_buf[i * 3 + 2] = leds_buf[i].b;
452 }
453 auto rx = fl::LpcSctRxChannel::create(rx_pin);
454 if (!rx) return fl::string("0,0,0,0,0,0,0,0");
455 fl::RxConfig cfg;
456 const fl::u32 expected_edges = (fl::u32)num_leds * 24u * 2u;
457 fl::u32 cap = expected_edges + (expected_edges / 2u);
458 // The low-memory sketch cannot afford the old 2048-edge reserve
459 // on a 16 KB SRAM LPC845. 192 edges covers the 1- and 3-LED
460 // sanity cases and fails the larger diagnostic cases without
461 // starving setup()/loop() stack.
462 constexpr fl::u32 kLowMemWs2812EdgeCapacity = 192u;
463 if (cap < 48u) cap = 48u;
464 if (cap > kLowMemWs2812EdgeCapacity) cap = kLowMemWs2812EdgeCapacity;
465 cfg.buffer_size = cap;
466 cfg.start_low = true;
467 if (!rx->begin(cfg)) return fl::string("0,0,0,0,0,0,0,0");
468
469 constexpr int kFixedTxPin = 10; // P0_10
470 if (tx_pin != kFixedTxPin) {
471 return fl::string("0,0,0,0,0,0,0,0");
472 }
473 if (!ws_state.fastled_initialized) {
474 FastLED.addLeds<WS2812, kFixedTxPin, GRB>(leds_buf, 100);
475 ws_state.fastled_initialized = true;
476 }
477 FastLED.show();
478 rx->wait(capture_ms);
479
480 uint8_t* decoded_buf = ws_state.decoded;
481 for (int i = 0; i < expected_bytes; ++i) decoded_buf[i] = 0;
482
486 /*reset_us=*/0u);
487
488 auto dec = rx->decode(timing, fl::span<u8>(decoded_buf, expected_bytes));
489 const fl::u32 decoded_bytes = dec.ok() ? dec.value() : 0u;
490
491 fl::u32 matched = 0;
492 fl::u32 mismatched = 0;
493 for (fl::u32 i = 0; i < decoded_bytes && i < (fl::u32)expected_bytes; ++i) {
494 if (decoded_buf[i] == expected_buf[i]) ++matched;
495 else ++mismatched;
496 }
497 const fl::u32 missing = (fl::u32)expected_bytes - decoded_bytes;
498 mismatched += missing;
499
500 // Diagnostic-only raw-edge sample. Keep this small in .bss; the
501 // decoder uses the RX channel's bounded capture vector above.
502 constexpr fl::size kLowMemWs2812ProbeSize = 64u;
503 fl::EdgeTime* probe = ws_state.probe;
504 const fl::size edges_captured = rx->getRawEdgeTimes(
505 fl::span<fl::EdgeTime>(probe, kLowMemWs2812ProbeSize));
506
507 const bool success = (mismatched == 0 && decoded_bytes == (fl::u32)expected_bytes);
508
509 fl::sstream s;
510 s << (success ? 1 : 0) << ',' << test_case << ',' << num_leds << ','
511 << expected_bytes << ',' << decoded_bytes << ','
512 << matched << ',' << mismatched << ','
513 << static_cast<fl::u32>(edges_captured);
514 return s.str();
515 });
516#endif // FASTLED_AUTORESEARCH_LPC_WS2812
517
518 // FastLED #3468: channels-API SCT+DMA clockless AutoResearch
519 // harness. Binds pwmDmaClFrameOnce / pwmDmaClFrameBurst /
520 // pwmDmaClCaptureSelf. Header self-gates on
521 // FL_IS_ARM_LPC_845 && FASTLED_LPC_PWM_DMA.
522#if defined(FL_IS_ARM_LPC_845) && defined(FASTLED_LPC_PWM_DMA)
523 autoresearch::pwm_dma_cl::bind(remote);
524#endif
525
526 // FastLED #3456: SPI+DMA async AutoResearch harness. Binds
527 // dmaSpiTransferOnce / dmaSpiTransferOverlap / dmaSpiMeasureSck.
528 // Header self-gates on FL_IS_ARM_LPC_845 && FASTLED_LPC_SPI_DMA.
529#if defined(FL_IS_ARM_LPC_845) && defined(FASTLED_LPC_SPI_DMA)
530 autoresearch::dma_spi::bind(remote);
531#endif
532
533 // #3453/#3611: UART DMA AutoResearch harness. Binds either the raw
534 // UART DMA stream bench or the clockless loopback verifier, depending
535 // on the build flags injected by `bash autoresearch`.
536#if defined(FL_IS_ARM_LPC_845) && FASTLED_AUTORESEARCH_LPC_UART_DMA
537 autoresearch::uart_dma::bind(remote);
538#endif
539
540#endif // FL_IS_ARM_LPC && !FASTLED_AUTORESEARCH_IEEE754_MODE
541}
542
CRGB leds[1]
On-device verification for the integer-only IEEE 754 decimal codec.
void autoResearchLowMemoryLoop()
void autoResearchLowMemorySetup()
FL_DISABLE_WARNING_PUSH FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS CFastLED FastLED
Global LED strip management instance.
fl::unique_ptr< fl::Remote > remote
Definition RpcClient.ino:43
size_t update(u32 currentTimeMs) FL_NO_EXCEPT
Main update: pull + tick + push (overrides Server::update)
JSON-RPC server with scheduling support.
Definition remote.h:42
static T & instance() FL_NO_EXCEPT
Definition singleton.h:81
static Watchdog & instance() FL_NO_EXCEPT
void begin(fl::u32 timeout_ms) FL_NO_EXCEPT
void feed() FL_NO_EXCEPT
static json object() FL_NO_EXCEPT
Definition json.h:864
string str() const FL_NO_EXCEPT
Definition strstream.h:43
constexpr EOrder GRB
Definition eorder.h:19
fl::CRGB CRGB
Definition crgb.h:25
#define FL_WARN_LIT(LITERAL)
Definition log.h:455
Centralized logging categories for FastLED hardware interfaces and subsystems.
unsigned char u8
Definition stdint.h:131
constexpr ChipsetTiming to_runtime_timing() FL_NO_EXCEPT
Convert enum-based timing type to runtime ChipsetTiming struct.
Definition led_timing.h:616
ChipsetTiming4Phase make4PhaseTiming(const ChipsetTiming &timing_3phase, u32 tolerance_ns) FL_NO_EXCEPT
Create 4-phase RX timing from 3-phase chipset timing with tolerance.
Definition rx.cpp.hpp:78
void serial_begin(u32 baudRate) FL_NO_EXCEPT
fl::u64 u64
Definition s16x16x4.h:221
unsigned char uint8_t
Definition s16x16x4.h:209
Base definition for an LED controller.
4-phase RX timing thresholds for chipset detection
Definition rx.h:88
corkscrew_args args
Definition old.h:149
Public re-export of the LPC SCT-capture RX driver (FastLED#3015).
#define FL_DISABLE_WARNING(warning)
#define FL_DISABLE_WARNING_PUSH
#define FL_NO_INLINE
#define FL_DISABLE_WARNING_POP
#define FL_NO_EXCEPT
#define FL_NO_RETURN
u32 ns
Duration in nanoseconds (31 bits, max ~2.1s)
Definition rx.h:35
Universal edge timing representation (platform-agnostic)
Definition rx.h:34
size_t buffer_size
Buffer size in symbols/edges (default: 512)
Definition rx.h:225
bool start_low
Pin idle state: true=LOW (WS2812B), false=HIGH (inverted)
Definition rx.h:232
Configuration for RX device initialization.
Definition rx.h:223
Compile-time timing extraction from ChipsetTiming structs.
Unified cross-platform Watchdog Timer API for FastLED.