10#if FL_PLATFORM_HAS_LARGE_MEMORY
14#include "platforms/arm/rp/is_rp.h"
15#if defined(FL_IS_RP2040) || defined(FL_IS_RP2350)
18#include "platforms/arm/rp/rpcommon/rx_pio_channel.h"
20#if defined(FL_IS_RP2040) || defined(FL_IS_RP2350)
25#include "platforms/arm/rp/rpcommon/rp_uart_bus_traits.h"
29#include "platforms/arm/teensy/teensy4_common/drivers/objectfled/objectfled_diagnostics.h"
50#define AR_FL_WARN(...) do { \
51 if (fl::getLogLevel() >= static_cast<fl::u8>(fl::LogLevel::FL_LOG_LEVEL_WARN)) { \
52 FL_WARN(__VA_ARGS__); \
66#if defined(FL_IS_RP2040) || defined(FL_IS_RP2350)
67void countRpPioRxRisingEdges(
void* user_data) {
69 if (edges !=
nullptr) {
76#if defined(ESP32) && FASTLED_ESP32_HAS_PARLIO
77#include "platforms/esp/32/drivers/parlio/channel_driver_parlio.h"
88 AR_FL_WARN(
"[RAW EDGE TIMING] ERROR: RX channel is null");
96 size_t buffer_size = range.
count < 256 ? range.
count : 256;
100 size_t edge_count = rx_channel->getRawEdgeTimes(edges, range.
offset);
102 if (edge_count == 0) {
103 AR_FL_WARN(
"[RAW EDGE TIMING] WARNING: No edge data captured at offset " << range.
offset);
108 size_t start_idx = range.
offset;
109 size_t end_idx = range.
offset + edge_count;
118 edge_dump <<
"[RAW EDGES " << start_idx <<
".." << (end_idx - 1) <<
"]";
119 for (
size_t i = 0; i < edge_count; i++) {
120 edge_dump <<
" " << (edges[i].high ?
"H" :
"L") << edges[i].ns;
122 AR_FL_WARN(edge_dump.
str());
126 if (range.
offset == 0 && edge_count >= 16) {
135 bool has_short_high =
false, has_long_high =
false;
136 bool has_short_low =
false, has_long_low =
false;
138 for (
size_t i = 0; i < edge_count; i++) {
141 if (ns >= expected_bit0_high - tolerance && ns <= expected_bit0_high + tolerance)
142 has_short_high =
true;
143 if (ns >= expected_bit1_high - tolerance && ns <= expected_bit1_high + tolerance)
144 has_long_high =
true;
146 if (ns >= expected_bit1_low - tolerance && ns <= expected_bit1_low + tolerance)
147 has_short_low =
true;
148 if (ns >= expected_bit0_low - tolerance && ns <= expected_bit0_low + tolerance)
154 ss <<
"\n[RAW EDGE TIMING] Pattern Analysis:\n";
155 ss <<
" Short HIGH (~" << expected_bit0_high <<
"ns, Bit 0): " << (has_short_high ?
"FOUND ✓" :
"MISSING ✗") <<
"\n";
156 ss <<
" Long HIGH (~" << expected_bit1_high <<
"ns, Bit 1): " << (has_long_high ?
"FOUND ✓" :
"MISSING ✗") <<
"\n";
157 ss <<
" Short LOW (~" << expected_bit1_low <<
"ns, Bit 1): " << (has_short_low ?
"FOUND ✓" :
"MISSING ✗") <<
"\n";
158 ss <<
" Long LOW (~" << expected_bit0_low <<
"ns, Bit 0): " << (has_long_low ?
"FOUND ✓" :
"MISSING ✗");
159 AR_FL_WARN(ss.
str());
161 if (has_short_high && has_long_high && has_short_low && has_long_low) {
162 AR_FL_WARN(
"\n[RAW EDGE TIMING] ✓ Encoder appears to be working correctly (varied timing patterns)");
163 }
else if (!has_short_high && !has_long_high) {
165 ss <<
"[RAW EDGE TIMING] ✗ ENCODER BROKEN: No valid HIGH pulses detected!\n";
166 ss <<
"[RAW EDGE TIMING] Possible causes:\n";
167 ss <<
"[RAW EDGE TIMING] 1. Encoder not reading pixel buffer data\n";
168 ss <<
"[RAW EDGE TIMING] 2. Bytes encoder state machine stuck\n";
169 ss <<
"[RAW EDGE TIMING] 3. Data pointer not passed correctly to encoder";
171 }
else if (!has_short_low && !has_long_low) {
173 AR_FL_WARN(
"[RAW EDGE TIMING] ✗ ENCODER BROKEN: No valid LOW pulses detected!");
175 AR_FL_WARN(
"[RAW EDGE TIMING] ⚠ Partial pattern match - encoder may have issues");
201 leds.data(),
static_cast<int>(
leds.size()),
249 mode, current,
false ,
gamma.get());
262 if (!rx_channel || clock_hz == 0) {
263 AR_FL_WARN(
"[SPI DECODE] Invalid parameters");
267 const uint32_t bit_period_ns =
static_cast<uint32_t>(1000000000ULL / clock_hz);
268 const uint32_t half_bit_ns = bit_period_ns / 2;
269 AR_FL_WARN(
"[SPI DECODE] clock=" << clock_hz <<
" Hz, bit_period=" << bit_period_ns <<
" ns");
272 constexpr size_t MAX_EDGES = 4096;
275 size_t edge_count = rx_channel->getRawEdgeTimes(edge_span, 0);
277 if (edge_count == 0) {
278 AR_FL_WARN(
"[SPI DECODE] No edges captured");
281 AR_FL_WARN(
"[SPI DECODE] Captured " << edge_count <<
" edges");
289 for (
size_t i = 0; i < edge_count; i++) {
291 uint32_t num_bits = (duration + half_bit_ns) / bit_period_ns;
292 if (num_bits == 0) num_bits = 1;
293 uint8_t bit_val = edges[i].high ? 1 : 0;
294 for (uint32_t b = 0; b < num_bits; b++) {
299 AR_FL_WARN(
"[SPI DECODE] Reconstructed " << bits.
size() <<
" bits");
301 if (bits.
size() < 32) {
302 AR_FL_WARN(
"[SPI DECODE] Too few bits for APA102 frame");
307 size_t total_bytes = bits.
size() / 8;
309 for (
size_t i = 0; i < total_bytes; i++) {
311 for (
int bit = 7; bit >= 0; bit--) {
312 size_t bit_idx = i * 8 + (7 - bit);
313 if (bit_idx < bits.
size() && bits[bit_idx]) {
314 byte_val |= (1 << bit);
317 raw_bytes[i] = byte_val;
320 AR_FL_WARN(
"[SPI DECODE] Decoded " << total_bytes <<
" raw bytes");
325 dbg <<
"[SPI DECODE] First bytes:";
326 size_t show = total_bytes < 20 ? total_bytes : 20;
327 for (
size_t i = 0; i <
show; i++) {
329 uint8_t hi = raw_bytes[i] >> 4;
330 uint8_t lo = raw_bytes[i] & 0xF;
331 dbg << (char)(hi < 10 ?
'0' + hi :
'A' + hi - 10);
332 dbg << (char)(lo < 10 ?
'0' + lo :
'A' + lo - 10);
334 AR_FL_WARN(dbg.
str());
341 size_t data_start = 0;
344 if (total_bytes >= 4 && raw_bytes[0] == 0x00 && raw_bytes[1] == 0x00 &&
345 raw_bytes[2] == 0x00 && raw_bytes[3] == 0x00) {
347 AR_FL_WARN(
"[SPI DECODE] Found start frame at offset 0");
351 AR_FL_WARN(
"[SPI DECODE] No start frame (first edge = first LED data)");
361 size_t led_bytes_written = 0;
362 size_t pos = data_start;
364 while (
pos + 4 <= total_bytes) {
368 if ((header & 0xE0) != 0xE0) {
369 AR_FL_WARN(
"[SPI DECODE] End of LED data at byte " <<
pos
370 <<
" (header=0x" << ((header >> 4) < 10 ?
'0' + (header >> 4) :
'A' + (header >> 4) - 10)
371 << ((header & 0xF) < 10 ?
'0' + (header & 0xF) :
'A' + (header & 0xF) - 10) <<
")");
379 if (led_bytes_written + 3 <= rx_buffer.
size()) {
380 rx_buffer[led_bytes_written + 0] = c0;
381 rx_buffer[led_bytes_written + 1] = c1;
382 rx_buffer[led_bytes_written + 2] = c2;
383 led_bytes_written += 3;
385 AR_FL_WARN(
"[SPI DECODE] rx_buffer full at LED " << (led_bytes_written / 3));
391 size_t num_leds = led_bytes_written / 3;
392 AR_FL_WARN(
"[SPI DECODE] Extracted " << num_leds <<
" LEDs (" << led_bytes_written <<
" RGB bytes)");
393 return led_bytes_written;
408 static_cast<int>(rx_channel->getRawEdgeTimes(edges, 0));
412 const size_t sample_count = rx_channel->getRawEdgeTimes(sample_edges, 0);
414 for (
size_t i = 0; i < sample_count; ++i) {
418 sample << (sample_edges[i].high ?
'H' :
'L') << sample_edges[i].ns;
425 const char* driver_name) {
440 bool is_spi_driver = (
fl::strcmp(driver_name,
"SPI") == 0);
441 bool is_parlio_driver = (
fl::strcmp(driver_name,
"PARLIO") == 0);
442 bool is_i2s_driver = (
fl::strcmp(driver_name,
"I2S") == 0);
446 bool is_lcd_clockless_driver = (
fl::strcmp(driver_name,
"LCD_CLOCKLESS") == 0);
447 bool lcd_clockless_uses_wave3 =
false;
448 if (is_lcd_clockless_driver) {
452 bool uses_wave8 = is_spi_driver || is_parlio_driver || is_i2s_driver
453 || (is_lcd_clockless_driver && !lcd_clockless_uses_wave3);
454 bool uses_wave3 = is_lcd_clockless_driver && lcd_clockless_uses_wave3;
461 const uint32_t tick_ns = is_parlio_driver ? 125 : (period / 8);
464 static_cast<float>(timing.
t1_ns) / period * 8.0f + 0.5f);
466 static_cast<float>(timing.
t1_ns + timing.
t2_ns) / period * 8.0f + 0.5f);
468 const uint32_t actual_t0h = pulses_bit0 * tick_ns;
469 const uint32_t actual_t1h = pulses_bit1 * tick_ns;
470 const uint32_t actual_period = 8 * tick_ns;
471 const char* wave8_name = is_spi_driver ?
"SPI_wave8" :
472 is_parlio_driver ?
"PARLIO_wave8" :
473 is_lcd_clockless_driver ?
"LCD_CLOCKLESS_wave8" :
"I2S_wave8";
476 actual_t1h - actual_t0h,
477 actual_period - actual_t1h,
481 AR_FL_WARN(
"[RX TIMING] " << wave8_name <<
": pulses_bit0=" << pulses_bit0
482 <<
" pulses_bit1=" << pulses_bit1
483 <<
" tick_ns=" << tick_ns
484 <<
" -> T1=" << tx_timing.
T1 <<
" T2=" << tx_timing.
T2
485 <<
" T3=" << tx_timing.
T3);
486 }
else if (uses_wave3) {
489 const uint32_t tick_ns = period / 3;
491 static_cast<float>(timing.
t1_ns) / period * 3.0f + 0.5f);
493 static_cast<float>(timing.
t1_ns + timing.
t2_ns) / period * 3.0f + 0.5f);
494 const uint32_t actual_t0h = ticks_bit0 * tick_ns;
495 const uint32_t actual_t1h = ticks_bit1 * tick_ns;
496 const uint32_t actual_period = 3 * tick_ns;
499 actual_t1h - actual_t0h,
500 actual_period - actual_t1h,
502 "LCD_CLOCKLESS_wave3"
504 AR_FL_WARN(
"[RX TIMING] LCD_CLOCKLESS_wave3: ticks_bit0=" << ticks_bit0
505 <<
" ticks_bit1=" << ticks_bit1
506 <<
" tick_ns=" << tick_ns
507 <<
" -> T1=" << tx_timing.
T1 <<
" T2=" << tx_timing.
T2
508 <<
" T3=" << tx_timing.
T3);
514 const uint32_t tolerance = (uses_wave8 || uses_wave3) ? 200 : 170;
521 rx_timing.gap_tolerance_ns = 100000;
535 size_t expected_data_bytes,
538 const char* driver_name,
563 rx_config.hz = 40000000;
567 bool is_uart_driver = (
fl::strcmp(driver_name,
"UART") == 0)
571 bool is_object_fled_driver = (
fl::strcmp(driver_name,
"OBJECT_FLED") == 0);
573 rx_buffer.
size(), expected_data_bytes, rx_channel->backend());
574#if defined(FL_IS_TEENSY_4X)
583 if (rx_config.edge_capacity == 0) {
584 FL_ERROR(
"[CAPTURE] RX edge-capacity overflow");
587#if defined(FL_IS_RP2040) || defined(FL_IS_RP2350)
591 const bool is_rp_pio_capture =
596 if (is_rp_pio_capture) {
601 constexpr size_t kPioPhasesPerDataByte = 16;
606 constexpr size_t kPioPhaseHeadroomNumerator = 2;
607 constexpr size_t kPioPhasesPerDataByteWithHeadroom =
608 kPioPhasesPerDataByte * kPioPhaseHeadroomNumerator;
609 if (expected_data_bytes >
610 (
static_cast<size_t>(-1) - 1u) / kPioPhasesPerDataByteWithHeadroom) {
611 FL_ERROR(
"[CAPTURE] PIO edge-capacity overflow");
621 expected_data_bytes * kPioPhasesPerDataByteWithHeadroom + 1u;
625 rx_config.edge_capacity = requested;
629 bool is_rmt_driver = (
fl::strcmp(driver_name,
"RMT") == 0);
630 const int rx_pin = rx_channel->getPin();
637 const bool use_rmt_internal_loopback =
639 is_rmt_driver, tx_pin, rx_pin);
640 rx_config.io_loop_back = use_rmt_internal_loopback;
648 rx_config.use_dma = !is_rmt_driver;
654 rx_config.backend = rx_channel->backend();
658 rx_config.stream_timing = rx_timing;
659 if (use_rmt_internal_loopback) {
660 AR_FL_WARN(
"[CAPTURE] RMT TX -> RMT RX: Same-pin internal loopback enabled");
668 AR_FL_WARN(
"[CAPTURE] " << driver_name
669 <<
" TX GPIO " << tx_pin <<
" -> RX GPIO " << rx_pin
670 <<
": External jumper "
671 <<
"(io_loop_back=false, use_dma="
672 << (rx_config.use_dma ?
"true" :
"false") <<
")");
682 const uint32_t TX_WAIT_TIMEOUT_MS = 1000;
688 AR_FL_WARN(
"[CAPTURE] RMT: Two-TX approach (ESP32-S3 workaround)");
691 if (!
FastLED.wait(TX_WAIT_TIMEOUT_MS)) {
692 FL_ERROR(
"[CAPTURE] TX wait timeout (pre-arm) - driver may be stalled");
697 if (!rx_channel->begin(rx_config)) {
698 if (diagnostics) diagnostics->
rxBeginError = rx_channel->lastBeginError();
699 FL_ERROR(
"Failed to arm RX receiver");
704 if (!
FastLED.wait(TX_WAIT_TIMEOUT_MS)) {
705 FL_ERROR(
"[CAPTURE] TX wait timeout (capture) - driver may be stalled");
710 if (!rx_channel->begin(rx_config)) {
711 if (diagnostics) diagnostics->
rxBeginError = rx_channel->lastBeginError();
712 AR_FL_WARN(
"[CAPTURE] RX begin() failed for pin " << rx_channel->getPin());
716 AR_FL_WARN(
"[CAPTURE] RX armed, calling FastLED.show()...");
719#if defined(FL_IS_RP2040) || defined(FL_IS_RP2350)
722 bool edge_counter_attached =
false;
723 const bool measure_rp_pio =
726 diagnostics !=
nullptr;
727 if (measure_rp_pio) {
736 edge_config.
handler = countRpPioRxRisingEdges;
740 static_cast<fl::u8>(rx_channel->getPin()), edge_config, &edge_counter) == 0;
745 AR_FL_WARN(
"[CAPTURE] FastLED.show() returned, calling wait...");
747#if defined(FL_IS_RP2040) || defined(FL_IS_RP2350)
748 if (edge_counter_attached && edge_counter.
is_valid()) {
753 diagnostics !=
nullptr) {
757 if (!
FastLED.wait(TX_WAIT_TIMEOUT_MS)) {
758 if (is_object_fled_driver) {
759 fl::objectFledDiagnosticsRecord(
"afterFastLedWaitTimeout");
761 AR_FL_WARN(
"[CAPTURE] FastLED.wait() timed out");
764 if (is_object_fled_driver) {
765 fl::objectFledDiagnosticsRecord(
"afterFastLedWait");
768 AR_FL_WARN(
"[CAPTURE] FastLED.wait() done");
778 const uint32_t rx_wait_ms = is_uart_driver ? 500 : 150;
780 AR_FL_WARN(
"[CAPTURE] Waiting for RX completion (" << rx_wait_ms
781 <<
"ms timeout)...");
783 auto wait_result = rx_channel->wait(rx_wait_ms);
788 collectRawEdgeDiagnostics(rx_channel, diagnostics);
792 AR_FL_WARN(
"[CAPTURE] RX wait returned: "
793 <<
static_cast<int>(wait_result));
797 AR_FL_WARN(
"RX wait failed (timeout or no data received)");
799 ss <<
"\n⚠️ TROUBLESHOOTING:\n";
800 ss <<
" 1. Connect physical jumper wire from TX GPIO to RX GPIO " << rx_channel->getPin() <<
"\n";
801 ss <<
" 2. Check that both TX and RX pins are correctly configured\n";
802 ss <<
" 3. Verify the GPIO connection is working (GPIO baseline test should pass)\n";
803 ss <<
" 4. RMT internal loopback requires TX and RX to use the same GPIO";
804 AR_FL_WARN(ss.
str());
805 if (!is_uart_driver) {
809 AR_FL_WARN(
"[CAPTURE] UART: attempting decode with captured edges despite timeout");
822 if (is_uart_driver) {
823 AR_FL_WARN(
"[CAPTURE] UART (inverted TX): using standard WS2812 decoder with UART timing...");
833#if defined(FL_IS_RP2040) || defined(FL_IS_RP2350)
837 }
else if (
fl::strcmp(driver_name,
"UART1") == 0) {
842 if (uart_timing.T1 == 0) {
847 if (uart_timing.T1 == 0 && uart_timing.T2 == 0) {
850 AR_FL_WARN(
"[CAPTURE] UART: no representable wave geometry for "
859 AR_FL_WARN(
"[CAPTURE] UART RX timing: T0H=" << uart_timing.T1
860 <<
" T1H=" << (uart_timing.T1 + uart_timing.T2)
861 <<
" T0L=" << (uart_timing.T2 + uart_timing.T3)
862 <<
" T1L=" << uart_timing.T3);
864 auto decode_result = rx_channel->decode(rx_timing, rx_buffer);
865 if (!decode_result.ok()) {
866 AR_FL_WARN(
"[CAPTURE] UART decode failed (error: " <<
static_cast<int>(decode_result.error()) <<
")");
874 AR_FL_WARN(
"[CAPTURE] UART decoded " << decode_result.value() <<
" LED bytes");
875 return decode_result.value();
881 bool is_lcd_spi_driver = (
fl::strcmp(driver_name,
"LCD_SPI") == 0);
882 bool is_i2s_spi_driver = (
fl::strcmp(driver_name,
"I2S_SPI") == 0);
883 bool is_spi_unified_driver = (
fl::strcmp(driver_name,
"SPI_UNIFIED") == 0);
884 if (is_lcd_spi_driver || is_i2s_spi_driver || is_spi_unified_driver) {
886 const uint32_t spi_clock_hz = 2400000;
887 AR_FL_WARN(
"[CAPTURE] SPI chipset decode: clock=" << spi_clock_hz <<
" Hz");
888 size_t decoded = decodeSpiEdges(rx_channel, rx_buffer, spi_clock_hz);
890 AR_FL_WARN(
"[CAPTURE] SPI decode failed");
902 AR_FL_WARN(
"[CAPTURE] Decoding...");
903 auto decode_result = rx_channel->decode(rx_timing, rx_buffer);
905 diagnostics->
decodeOk = decode_result.ok() ? 1 : 0;
907 decode_result.ok() ? -1 :
static_cast<int>(decode_result.error());
909 decode_result.ok() ?
static_cast<int>(decode_result.value()) : 0;
912 if (!decode_result.ok()) {
915 AR_FL_WARN(
"Decode failed (error code: " <<
static_cast<int>(decode_result.error()) <<
")");
925 AR_FL_WARN(
"[CAPTURE] Decoded " << decode_result.value() <<
" bytes");
926 return decode_result.value();
931void runTest(
const char* test_name,
933 int& total,
int& passed) {
939 AR_FL_WARN(
"\n[MULTI-LANE] Testing " << config.
tx_configs.
size() <<
" lanes, testing Lane 0 only (hardware limitation)");
943 for (
size_t config_idx = 0; config_idx < channels_to_test; config_idx++) {
948 size_t num_leds =
leds.size();
949 const bool lane_uses_rgbw =
950 legacyLaneUsesRgbw(config, config_idx);
955 const bool lane_uses_ucs7604 = isUCS7604(config.
encoder);
957 if (lane_uses_ucs7604) {
958 expected_encoded = buildExpectedUCS7604(
961 const size_t expected_data_bytes =
962 lane_uses_ucs7604 ? expected_encoded.
size()
963 : num_leds * (lane_uses_rgbw ? 4u : 3u);
971 static_cast<int>(config_idx),
973 static_cast<int>(num_leds),
977 AR_FL_WARN(
"\n=== " << test_name <<
" [Lane " << config_idx <<
"/" << config.
tx_configs.
size()
978 <<
", Pin " << config.
tx_configs[config_idx].getDataPin()
979 <<
", LEDs " << config.
tx_configs[config_idx].mLeds.
size() <<
"] ===");
983 FL_ERROR(
"[" << ctx.driver_name <<
"/" << ctx.timing_name <<
"/" << ctx.pattern_name
984 <<
" | Lane " << ctx.lane_index <<
"/" << ctx.lane_count
985 <<
" (Pin " << ctx.pin_number <<
", " << ctx.num_leds <<
" LEDs) | RX:" << ctx.rx_type_name <<
"] "
986 <<
"RX channel is null - must be created in .ino and passed via AutoResearchConfig");
987 FL_ERROR(
"Result: FAIL ✗ (RX channel not provided)");
996 if (bytes_captured == 0) {
997 FL_ERROR(
"[" << ctx.driver_name <<
"/" << ctx.timing_name <<
"/" << ctx.pattern_name
998 <<
" | Lane " << ctx.lane_index <<
"/" << ctx.lane_count
999 <<
" (Pin " << ctx.pin_number <<
", " << ctx.num_leds <<
" LEDs) | RX:" << ctx.rx_type_name <<
"] "
1000 <<
"Result: FAIL ✗ (capture failed)");
1006 if (lane_uses_ucs7604) {
1008 size_t expected_len = expected_encoded.
size();
1010 AR_FL_WARN(
"UCS7604 encoded comparison: expected " << expected_len <<
" bytes, captured " << bytes_captured);
1012 size_t compare_len = (bytes_captured < expected_len) ? bytes_captured : expected_len;
1013 for (
size_t i = 0; i < compare_len; i++) {
1014 if (expected_encoded[i] != config.
rx_buffer[i]) {
1019 if (bytes_captured < expected_len) {
1020 mismatches +=
static_cast<int>(expected_len - bytes_captured);
1023 AR_FL_WARN(
"Bytes Captured: " << bytes_captured <<
" (expected: " << expected_len <<
")");
1024 int total_bytes =
static_cast<int>(expected_len);
1025 AR_FL_WARN(
"Accuracy: " << (100.0 * (total_bytes - mismatches) / total_bytes) <<
"% ("
1026 << (total_bytes - mismatches) <<
"/" << total_bytes <<
" bytes match)");
1027 }
else if (lane_uses_rgbw) {
1029 buildExpectedClockless(
1031 const size_t expected_len = expected.
size();
1032 const size_t compare_len =
1033 bytes_captured < expected_len ? bytes_captured : expected_len;
1034 for (
size_t i = 0; i < compare_len; ++i) {
1035 if (expected[i] != config.
rx_buffer[i]) {
1039 if (bytes_captured < expected_len) {
1041 static_cast<int>(expected_len - bytes_captured);
1044 AR_FL_WARN(
"RGBW bytes captured: " << bytes_captured
1045 <<
" (expected: " << expected_len <<
")");
1046 AR_FL_WARN(
"Accuracy: "
1047 << (100.0 * (expected_len - mismatches) / expected_len)
1048 <<
"% (" << (expected_len - mismatches) <<
"/"
1049 << expected_len <<
" bytes match)");
1052 size_t bytes_expected = num_leds * 3;
1055 const size_t front_padding_bytes = 0;
1056 const size_t rx_buffer_offset = front_padding_bytes;
1058 if (bytes_captured > bytes_expected + front_padding_bytes) {
1059 AR_FL_WARN(
"Info: Captured " << bytes_captured <<
" bytes ("
1060 << front_padding_bytes <<
" front pad + "
1061 << bytes_expected <<
" LED data + "
1062 << (bytes_captured - bytes_expected - front_padding_bytes) <<
" back pad/RESET)");
1066 size_t bytes_to_check = (bytes_captured < bytes_expected + rx_buffer_offset) ?
1067 (bytes_captured > rx_buffer_offset ? bytes_captured - rx_buffer_offset : 0) :
1069 (void)bytes_to_check;
1071 for (
size_t i = 0; i < num_leds; i++) {
1072 size_t byte_offset = rx_buffer_offset + i * 3;
1073 if (byte_offset + 2 >= bytes_captured) {
1074 FL_ERROR(
"[" << ctx.driver_name <<
"/" << ctx.timing_name <<
"/" << ctx.pattern_name
1075 <<
" | Lane " << ctx.lane_index <<
"/" << ctx.lane_count
1076 <<
" (Pin " << ctx.pin_number <<
", " << ctx.num_leds <<
" LEDs) | RX:" << ctx.rx_type_name <<
"] "
1077 <<
"Incomplete data for LED[" <<
static_cast<int>(i)
1078 <<
"] (only " << bytes_captured <<
" bytes captured)");
1090 if (expected_r != actual_r || expected_g != actual_g || expected_b != actual_b) {
1095 AR_FL_WARN(
"Bytes Captured: " << bytes_captured <<
" (expected: " << bytes_expected <<
")");
1096 AR_FL_WARN(
"Accuracy: " << (100.0 * (num_leds - mismatches) / num_leds) <<
"% ("
1097 << (num_leds - mismatches) <<
"/" << num_leds <<
" LEDs match)");
1100 if (mismatches == 0) {
1101 AR_FL_WARN(
"Result: PASS ✓");
1104 FL_ERROR(
"[" << ctx.driver_name <<
"/" << ctx.timing_name <<
"/" << ctx.pattern_name
1105 <<
" | Lane " << ctx.lane_index <<
"/" << ctx.lane_count
1106 <<
" (Pin " << ctx.pin_number <<
", " << ctx.num_leds <<
" LEDs) | RX:" << ctx.rx_type_name <<
"] "
1107 <<
"Result: FAIL ✗");
1117 int& total,
int& passed,
1137 ss <<
"\n╔════════════════════════════════════════════════════════════════╗\n";
1138 ss <<
"║ MULTI-RUN TEST: " << test_name <<
"\n";
1139 ss <<
"║ Runs: " << multi_config.
num_runs <<
" | Print Mode: "
1140 << (multi_config.
print_all_runs ?
"All" :
"Errors ONLY") <<
"\n";
1141 ss <<
"╚════════════════════════════════════════════════════════════════╝";
1142 AR_FL_WARN(ss.
str());
1146 int total_passed = 0;
1147 int total_failed = 0;
1148 bool retained_success =
false;
1154 AR_FL_WARN(
"[MULTI-LANE] Testing " << config.
tx_configs.
size() <<
" lanes, testing Lane 0 only");
1161 if (channels_to_test > 0 && legacyLaneUsesRgbw(config, 0)) {
1163 buildExpectedClockless(config.
tx_configs[0].mLeds,
true);
1170 const bool lane_uses_ucs7604 = isUCS7604(config.
encoder);
1172 if (channels_to_test > 0 && lane_uses_ucs7604) {
1187 if (kLogsEnabled && (run % 3 == 1 || multi_config.
num_runs <= 5)) {
1188 AR_FL_WARN(
"[Run " << run <<
"/" << multi_config.
num_runs <<
"] Testing...");
1195 for (
size_t config_idx = 0; config_idx < channels_to_test; config_idx++) {
1197 size_t num_leds =
leds.size();
1198 const bool lane_uses_rgbw =
1199 legacyLaneUsesRgbw(config, config_idx);
1200 const size_t expected_data_bytes =
1201 lane_uses_ucs7604 ? expected_ucs7604.
size()
1202 : num_leds * (lane_uses_rgbw ? 4u : 3u);
1203 result.total_leds = num_leds;
1204 result.totalBytes =
static_cast<int>(expected_data_bytes);
1207 size_t bytes_captured =
1209 expected_data_bytes,
1213 result.capturedBytes =
static_cast<int>(bytes_captured);
1215 if (bytes_captured == 0) {
1216 AR_FL_WARN(
"[Run " << run <<
"] Capture failed");
1217 result.mismatches =
static_cast<int>(num_leds);
1218 result.mismatchedBytes =
static_cast<int>(
result.totalBytes);
1219 result.captureFailed =
true;
1224 collectRawEdgeDiagnostics(config.
rx_channel, &result);
1240 size_t dump_count = (bytes_captured < 24) ? bytes_captured : 24;
1241 for (
size_t i = 0; i < dump_count; i++) {
1242 if (i > 0) hex_dump <<
" ";
1245 AR_FL_WARN(
"[RUN " << run <<
"] Driver=" << config.
driver_name
1246 <<
" bytes_captured=" << bytes_captured
1247 <<
" first" <<
static_cast<int>(dump_count) <<
"=" << hex_dump.
str());
1250 if (lane_uses_ucs7604) {
1253 size_t expected_len = expected_encoded.
size();
1254 result.totalBytes =
static_cast<int>(expected_len);
1256 size_t compare_len = (bytes_captured < expected_len) ? bytes_captured : expected_len;
1257 for (
size_t i = 0; i < compare_len; i++) {
1258 if (expected_encoded[i] != config.
rx_buffer[i]) {
1259 result.mismatchedBytes++;
1260 if ((expected_encoded[i] ^ config.
rx_buffer[i]) == 0x01) {
1266 if (mismatches == 1) {
1267 AR_FL_WARN(
"\n[CORRUPTION @ byte " <<
static_cast<int>(i) <<
", Run " << run
1268 <<
"] expected=0x" <<
fl::hex <<
static_cast<int>(expected_encoded[i])
1275 static_cast<int>(i), expected_encoded[i], 0, 0,
1282 if (bytes_captured < expected_len) {
1283 mismatches +=
static_cast<int>(expected_len - bytes_captured);
1285 }
else if (lane_uses_rgbw) {
1287 size_t verified_leds = 0;
1288 for (
size_t i = 0; i < num_leds; ++i) {
1289 const size_t byte_offset = i * 4;
1290 if (byte_offset + 3 >= bytes_captured) {
1293 verified_leds = i + 1;
1295 bool led_mismatch =
false;
1296 for (
size_t channel = 0; channel < 4; ++channel) {
1298 expected[byte_offset + channel];
1300 config.
rx_buffer[byte_offset + channel];
1301 if (expected_byte != actual_byte) {
1302 ++
result.mismatchedBytes;
1303 if ((expected_byte ^ actual_byte) == 0x01) {
1306 led_mismatch =
true;
1311 if (mismatches == 0) {
1312 AR_FL_WARN(
"\n[RGBW CORRUPTION @ LED "
1313 <<
static_cast<int>(i) <<
", Run "
1315 const size_t corruption_edge_index = i * 64;
1317 corruption_edge_index > 4
1318 ? corruption_edge_index - 4
1326 if (
result.errors.size() <
1327 static_cast<size_t>(
1330 static_cast<int>(i),
1331 expected[byte_offset + 0],
1332 expected[byte_offset + 1],
1333 expected[byte_offset + 2],
1334 expected[byte_offset + 3],
1343 if (verified_leds < num_leds) {
1344 const size_t unchecked = num_leds - verified_leds;
1345 mismatches +=
static_cast<int>(unchecked);
1346 result.mismatchedBytes +=
1347 static_cast<int>(unchecked * 4);
1348 AR_FL_WARN(
"[TRUNCATED RGBW CAPTURE] Only verified "
1349 << verified_leds <<
"/" << num_leds
1350 <<
" LEDs (" << bytes_captured
1351 <<
" bytes captured, needed "
1352 << expected_data_bytes <<
"). Marking "
1354 <<
" unchecked LEDs as mismatches.");
1358 size_t bytes_expected = num_leds * 3;
1361 const size_t rx_buffer_offset = 0;
1363 size_t bytes_to_check = (bytes_captured < bytes_expected + rx_buffer_offset) ?
1364 (bytes_captured > rx_buffer_offset ? bytes_captured - rx_buffer_offset : 0) :
1366 (void)bytes_to_check;
1368 size_t verified_leds = 0;
1369 for (
size_t i = 0; i < num_leds; i++) {
1370 size_t byte_offset = rx_buffer_offset + i * 3;
1371 if (byte_offset + 2 >= bytes_captured) {
1374 verified_leds = i + 1;
1385 uint8_t exp_bytes[3] = {expected_r, expected_g, expected_b};
1386 uint8_t act_bytes[3] = {actual_r, actual_g, actual_b};
1387 bool led_mismatch =
false;
1388 for (
int ch = 0; ch < 3; ch++) {
1389 if (exp_bytes[ch] != act_bytes[ch]) {
1390 result.mismatchedBytes++;
1391 if ((exp_bytes[ch] ^ act_bytes[ch]) == 0x01) {
1394 led_mismatch =
true;
1400 if (mismatches == 0) {
1401 AR_FL_WARN(
"\n[CORRUPTION @ LED " <<
static_cast<int>(i) <<
", Run " << run <<
"]");
1404 size_t corruption_edge_index = i * 48;
1405 size_t offset = corruption_edge_index > 4 ? corruption_edge_index - 4 : 0;
1416 i, expected_r, expected_g, expected_b,
1417 actual_r, actual_g, actual_b
1428 if (verified_leds < num_leds) {
1429 size_t unchecked = num_leds - verified_leds;
1430 mismatches +=
static_cast<int>(unchecked);
1431 result.mismatchedBytes +=
static_cast<int>(unchecked * 3);
1432 AR_FL_WARN(
"[TRUNCATED CAPTURE] Only verified " << verified_leds
1433 <<
"/" << num_leds <<
" LEDs (" << bytes_captured
1434 <<
" bytes captured, needed " << (num_leds * 3)
1435 <<
"). Marking " << unchecked
1436 <<
" unchecked LEDs as mismatches.");
1440 result.mismatches = mismatches;
1441 result.passed = (mismatches == 0);
1443 collectRawEdgeDiagnostics(config.
rx_channel, &result);
1450 AR_FL_WARN(
"[Run " << run <<
"/" << multi_config.
num_runs <<
"] "
1451 << (
result.passed ?
"PASS" :
"FAIL")
1452 <<
" | Errors: " <<
result.mismatches <<
"/" <<
result.total_leds
1453 <<
" (" << (100.0 * (
result.total_leds -
result.mismatches) /
result.total_leds) <<
"%)");
1457 AR_FL_WARN(
" First " <<
result.errors.size() <<
" error(s):");
1458 for (
size_t i = 0; i <
result.errors.size(); i++) {
1459 const auto& err =
result.errors[i];
1460 AR_FL_WARN(
" LED[" << err.led_index <<
"]: expected RGB("
1461 <<
static_cast<int>(err.expected_r) <<
","
1462 <<
static_cast<int>(err.expected_g) <<
","
1463 <<
static_cast<int>(err.expected_b) <<
") got RGB("
1464 <<
static_cast<int>(err.actual_r) <<
","
1465 <<
static_cast<int>(err.actual_g) <<
","
1466 <<
static_cast<int>(err.actual_b) <<
")");
1482 if (!
result.passed || !retained_success || multi_config.
num_runs == 1) {
1484 retained_success =
true;
1496 ss <<
"\n╔════════════════════════════════════════════════════════════════╗\n";
1497 ss <<
"║ MULTI-RUN SUMMARY\n";
1498 ss <<
"╚════════════════════════════════════════════════════════════════╝\n";
1499 ss <<
"Total Runs: " << multi_config.
num_runs <<
"\n";
1500 ss <<
"Passed: " << total_passed <<
" (" << (100.0 * total_passed / multi_config.
num_runs) <<
"%)\n";
1501 ss <<
"Failed: " << total_failed <<
" (" << (100.0 * total_failed / multi_config.
num_runs) <<
"%)";
1502 AR_FL_WARN(ss.
str());
1504 if (total_failed > 0) {
1506 ss <<
"\nFailed Run Numbers:\n";
1507 for (
const auto& r : run_results) {
1509 ss <<
" Run #" << r.run_number <<
" - " << r.mismatches <<
" errors\n";
1510 if (!r.errors.empty()) {
1511 ss <<
" First error at LED[" << r.errors[0].led_index <<
"]: "
1512 <<
"expected RGB(" <<
static_cast<int>(r.errors[0].expected_r) <<
","
1513 <<
static_cast<int>(r.errors[0].expected_g) <<
","
1514 <<
static_cast<int>(r.errors[0].expected_b) <<
") got RGB("
1515 <<
static_cast<int>(r.errors[0].actual_r) <<
","
1516 <<
static_cast<int>(r.errors[0].actual_g) <<
","
1517 <<
static_cast<int>(r.errors[0].actual_b) <<
")\n";
1521 AR_FL_WARN(ss.
str());
1527 for (
const auto& r : run_results) {
1534 if (total_failed == 0) {
1537 AR_FL_WARN(
"\n[OVERALL] PASS ✓ - All " << multi_config.
num_runs <<
" runs succeeded");
1541 AR_FL_WARN(
"\n[OVERALL] FAIL ✗ - " << total_failed <<
"/" << multi_config.
num_runs <<
" runs failed");
1549 int& driver_total,
int& driver_passed,
1550 uint32_t& out_show_duration_ms,
1552 int num_runs_per_pattern) {
1554 ss <<
"\n========================================\n";
1557 if (has_spi_config) {
1558 const auto* spi_cfg = config.
tx_configs[0].getSpiChipset();
1559 ss <<
" Protocol: SPI (APA102)\n";
1560 ss <<
" Clock: " << (spi_cfg ? spi_cfg->timing.clock_hz : 0) <<
" Hz\n";
1568 ss <<
"========================================";
1569 AR_FL_WARN(ss.
str());
1589 channel =
FastLED.add(channel_config);
1592 FL_ERROR(
"Failed to create channel " << i <<
" (pin " << config.
tx_configs[i].getDataPin() <<
") - platform not supported");
1594 for (
auto& ch : channels) {
1599 channels.push_back(channel);
1610 FL_ERROR(
"[PREINIT] TX wait timeout - driver may be stalled on this platform");
1630 multi_config.
num_runs = (num_runs_per_pattern > 0) ? num_runs_per_pattern : 1;
1639 for (
int pattern_id = 0; pattern_id < 4; pattern_id++) {
1651 out_show_duration_ms +=
fl::millis() - show_start_ms;
1656 driver_total += total;
1657 driver_passed += passed;
1664 fl::objectFledDiagnosticsRecord(
"afterFastLedClear");
1674 int& driver_total,
int& driver_passed,
1675 uint32_t& out_show_duration_ms,
1677 int num_runs_per_pattern) {
1679 ss <<
"\n========================================\n";
1680 ss <<
"Testing (LEGACY API): " << config.
timing_name <<
"\n";
1687 ss <<
" Lane " << i <<
": pin=" << config.
tx_configs[i].getDataPin()
1690 ss <<
"========================================";
1691 AR_FL_WARN(ss.
str());
1709 if (!proxy->valid()) {
1710 FL_ERROR(
"Legacy proxy invalid for lane " << i <<
" (pin " << pin
1712 <<
" not in supported set)");
1726 FL_ERROR(
"[LEGACY] TX wait timeout - driver may be stalled");
1737 multi_config.
num_runs = (num_runs_per_pattern > 0) ? num_runs_per_pattern : 1;
1745 const bool ws2814_visual_validation =
1748 const int pattern_count = ws2814_visual_validation ? 8 : 4;
1749 for (
int pattern_id = 0; pattern_id < pattern_count; pattern_id++) {
1759 if (ws2814_visual_validation && pattern_id >= 4) {
1767 out_show_duration_ms +=
fl::millis() - show_start_ms;
1769 driver_total += total;
1770 driver_passed += passed;
1777 switch (pattern_id) {
1781 for (
size_t i = 0; i < count; i++) {
1789 for (
size_t i = 0; i < count; i++) {
1797 for (
size_t i = 0; i < count; i++) {
1805 for (
size_t i = 0; i < count; i++) {
1806 int color_index = i % 3;
1807 if (color_index == 0) {
1809 }
else if (color_index == 1) {
1842 switch (pattern_id) {
1843 case 0:
return "Pattern A (R=0xF0, G=0x0F, B=0xAA)";
1844 case 1:
return "Pattern B (R=0x55, G=0xFF, B=0x00)";
1845 case 2:
return "Pattern C (R=0x0F, G=0xAA, B=0xF0)";
1846 case 3:
return "Pattern D (RGB Solid Alternating)";
1847 case 4:
return "Pattern E (Isolated Red)";
1848 case 5:
return "Pattern F (Isolated Green)";
1849 case 6:
return "Pattern G (Isolated Blue)";
1850 case 7:
return "Pattern H (Isolated White)";
1851 default:
return "Unknown Pattern";
size_t capture(fl::shared_ptr< fl::RxChannel > rx_channel, fl::span< uint8_t > rx_buffer, size_t expected_data_bytes, int tx_pin, const fl::ChipsetTimingConfig &timing, const char *driver_name, fl::RunResult *diagnostics=nullptr)
void runMultiTest(const char *test_name, fl::AutoResearchConfig &config, const fl::MultiRunConfig &multi_config, int &total, int &passed, fl::vector< fl::RunResult > *out_results=nullptr)
void autoResearchChipsetTimingLegacy(fl::AutoResearchConfig &config, int &total, int &passed, uint32_t &out_show_duration_ms, fl::vector< fl::RunResult > *out_results=nullptr, int num_runs_per_pattern=1)
const char * getBitPatternName(int pattern_id)
void runTest(const char *test_name, fl::AutoResearchConfig &config, int &total, int &passed)
void autoResearchChipsetTiming(fl::AutoResearchConfig &config, int &total, int &passed, uint32_t &out_show_duration_ms, fl::vector< fl::RunResult > *out_results=nullptr, int num_runs_per_pattern=1)
void setMixedBitPattern(CRGB *leds, size_t count, int pattern_id)
FL_DISABLE_WARNING_PUSH FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS CFastLED FastLED
Global LED strip management instance.
@ CHANNELS
Remove all channels from controller list.
const char * legacyClocklessChipsetName(LegacyClocklessChipset chipset)
bool legacyClocklessChipsetHasAutomaticRgbw(LegacyClocklessChipset chipset)
Runtime chipset timing configuration for clockless LED drivers.
FastLED chrono implementation - duration types for time measurements.
static fl::shared_ptr< const Gamma8 > getOrCreate(float gamma) FL_NO_EXCEPT
void writeWS2812(CONTAINER_UIN8_T *out) FL_NO_EXCEPT
void clear(bool freeMemory=false) FL_NO_EXCEPT
bool empty() const FL_NO_EXCEPT
constexpr bool empty() const FL_NO_EXCEPT
const T * data() const FL_NO_EXCEPT
constexpr fl::size size() const FL_NO_EXCEPT
void clear() FL_NO_EXCEPT
string str() const FL_NO_EXCEPT
fl::size size() const FL_NO_EXCEPT
void reserve(fl::size n) FL_NO_EXCEPT
void resize(fl::size n) FL_NO_EXCEPT
void push_back(const T &value) FL_NO_EXCEPT
void fill_solid(CRGB *targetArray, int numToFill, const CRGB &color) FL_NO_EXCEPT
Fill a range of LEDs with a solid color.
Delay utilities for FastLED Includes nanosecond-precision delays, cycle counting, and microsecond del...
fl::UISlider offset("Offset", 0.0f, 0.0f, 1.0f, 0.01f)
#define DISABLE_DITHER
Disable dithering.
UCS7604 LED chipset encoder implementation.
Non-templated low level pixel data writing class.
Umbrella header for ISR subsystem.
Centralized LED chipset timing definitions with nanosecond precision.
constexpr remove_reference< T >::type && move(T &&t) FL_NO_EXCEPT
fl::enable_if<!fl::is_array< T >::value, unique_ptr< T > >::type make_unique(Args &&... args) FL_NO_EXCEPT
constexpr u32 ISR_FLAG_EDGE_RISING
int attach_external_handler(u8 pin, const config &cfg, handle *out_handle) FL_NO_EXCEPT
Attach an external interrupt handler (GPIO-based).
int detach_handler(handle &h) FL_NO_EXCEPT
Detach an ISR handler.
bool useRmtInternalLoopback(bool is_rmt_driver, int tx_pin, int rx_pin) FL_NO_EXCEPT
Whether an RMT validation capture should use ESP-IDF loopback.
size_t captureEdgeCapacity(size_t shared_buffer_bytes, size_t expected_data_bytes, RxBackend backend) FL_NO_EXCEPT
Compute the RX edge-buffer capacity for a validation frame.
back_insert_iterator< Container > back_inserter(Container &c) FL_NO_EXCEPT
Helper function to create a back_insert_iterator.
u8 getLogLevel() FL_NO_EXCEPT
Get the current global log level.
ClocklessEncoder
Identifies which encoder to use for clockless chipsets in the Channel API.
@ CLOCKLESS_ENCODER_UCS7604_8BIT
UCS7604 8-bit 800KHz.
@ CLOCKLESS_ENCODER_UCS7604_16BIT
UCS7604 16-bit 800KHz.
@ CLOCKLESS_ENCODER_UCS7604_16BIT_1600
UCS7604 16-bit 1600KHz.
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...
CRGB sample(const CRGB *grid, const XYMap &xyMap, float x, float y, SampleMode mode) FL_NO_EXCEPT
Sample a pixel from a 2D CRGB grid at floating-point coordinates.
constexpr u32 gamma(float g) FL_NO_EXCEPT
FASTLED_FORCE_INLINE int strcmp(const char *s1, const char *s2) FL_NO_EXCEPT
fl::u32 millis() FL_NO_EXCEPT
Universal millisecond timer - returns milliseconds since system startup.
ChipsetTiming4Phase make4PhaseTiming(const ChipsetTiming &timing_3phase, u32 tolerance_ns) FL_NO_EXCEPT
Create 4-phase RX timing from 3-phase chipset timing with tolerance.
constexpr u32 kMaxUartBaudRate
Maximum UART baud rate supported by ESP32 variants.
FASTLED_FORCE_INLINE void * memset(void *s, int c, size_t n) FL_NO_EXCEPT
@ PIO
RP2040/RP2350 PIO receive backend.
@ FLEXIO
Teensy 4.x-only FlexIO capture backend (FLEXIO1 — FLEXIO2 is owned by the WS2812 TX driver)....
@ PLATFORM_DEFAULT
Use the recommended backend for the active platform (RMT on ESP32; FlexPWM on Teensy 4....
@ ISR
Platform-neutral interrupt-driven edge capture backend when available.
constexpr size_t kRpPioRxEdgeCapacity
Phase slots in the shared capture pool.
@ FL_LOG_LEVEL_WARN
Errors and warnings.
expected< T, E > result
Alias for expected (Rust-style naming)
const char * toString(RxBackend backend) FL_NO_EXCEPT
UCS7604Mode
UCS7604 protocol configuration modes.
@ UCS7604_MODE_8BIT_800KHZ
@ UCS7604_MODE_16BIT_1600KHZ
@ UCS7604_MODE_16BIT_800KHZ
FL_OPTIMIZE_FUNCTION bool canUseWave3(const ChipsetTiming &timing) FL_NO_EXCEPT
Check if a chipset timing is eligible for wave3 encoding.
void encodeUCS7604(PixelIterator &pixel_iter, size_t num_leds, OutputIterator out, UCS7604Mode mode, const UCS7604CurrentControl ¤t, bool is_rgbw, const Gamma8 *gamma=nullptr, bool wide_source=false) FL_NO_EXCEPT
Encode complete UCS7604 frame (preamble + padding + pixel data)
@ SUCCESS
Operation completed successfully.
ChipsetTiming uartWireTiming(const ChipsetTimingConfig &timing, u32 max_baud_rate) FL_NO_EXCEPT
The wire timing the UART encoder actually produces for a chipset.
u32 T2
Additional high time for bit 1 (nanoseconds)
u32 gap_tolerance_ns
Maximum gap duration to tolerate (0 = no gap tolerance, treat as error)
u32 T3
Low tail duration (nanoseconds)
u32 T1
High time for bit 0 (nanoseconds)
4-phase RX timing thresholds for chipset detection
Test context for detailed error reporting Aggregates all test configuration parameters for error mess...
Primary template — intentionally undefined.
Generic chipset timing entry Provides T1, T2, T3 timing parameters in nanoseconds for any LED protoco...
Low level pixel data writing class.
fl::PixelIterator PixelIterator
static ColorAdjustment noAdjustment() FL_NO_EXCEPT
the per-channel scale values premixed with brightness.
static Rgbw value() FL_NO_EXCEPT
static Rgbw value() FL_NO_EXCEPT
const fl::ChipsetTimingConfig & timing
Chipset timing configuration to test.
fl::RxDeviceType rx_type
RX device type (RMT or ISR)
const char * timing_name
Timing name for logging (e.g., "WS2812B-V5")
const char * driver_name
Driver name for logging (e.g., "RMT", "SPI", "PARLIO")
int base_strip_size
Base strip size (10 or 300 LEDs)
bool legacy_rgbw
Enable RGBW on legacy controllers.
fl::span< fl::ChannelConfig > tx_configs
TX channel configurations to test (mutable for LED manipulation)
fl::ClocklessEncoder encoder
Encoder selector (peer of timing; see issue #2467)
fl::shared_ptr< fl::RxChannel > rx_channel
RX channel for loopback capture (created in .ino, passed in)
fl::span< uint8_t > rx_buffer
Buffer to store received bytes.
fl::span< const LegacyClocklessChipset > legacy_chipsets
Optional per-lane legacy templates.
Configuration for driver-agnostic autoresearch testing Contains all input parameters needed for autor...
@ Green
<div style='background:#008000;width:4em;height:4em;'></div>
@ Red
<div style='background:#FF0000;width:4em;height:4em;'></div>
@ Blue
<div style='background:#0000FF;width:4em;height:4em;'></div>
@ White
<div style='background:#FFFFFF;width:4em;height:4em;'></div>
@ Black
<div style='background:#000000;width:4em;height:4em;'></div>
Configuration for a single LED channel.
u32 t1_ns
T0H: High time for bit 0 (nanoseconds)
u32 t2_ns
T1H-T0H: Additional high time for bit 1 (nanoseconds)
u32 reset_us
Reset/latch time (microseconds)
const char * name
Human-readable chipset name.
u32 t3_ns
T0L: Low tail duration (nanoseconds)
Runtime bit-period timing for a clockless chipset.
Clockless chipset configuration (single data pin)
size_t offset
Starting edge index.
size_t count
Number of edges to extract.
Edge range specification for getRawEdgeTimes() debugging.
LED error information for a single run.
bool print_per_led_errors
Print every LED error (default: false)
int max_errors_per_run
Max errors to store per run (default: 5)
bool print_all_runs
Print all run results (default: only errors)
int num_runs
Number of runs to execute.
Multi-run test configuration.
int captureWaitResult
RxWaitResult value, or -1 before wait.
int decodeOutputCapacity
Output span size passed to decode()
int rawEdgesAfterWait
Raw edges visible after RX wait/decode.
int decodeError
DecodeError value, or -1 when decode succeeded/not run.
fl::string rawEdgeSample
Compact first-edge sample for failures.
fl::string rxBeginError
Machine-readable reason when RX begin() fails.
int decodeBytes
Bytes reported directly by decode()
int rpPioGpioTransitions
CPU-observed RX-pin transitions during RP PIO TX.
int decodeOk
1 if RX decode returned success, 0 on error, -1 before decode
Single run result with error tracking.
UCS7604 current control structure with 4-bit fields for each channel.
Configuration for ISR attachment.
bool is_valid() const FL_NO_EXCEPT
Opaque handle to an attached ISR.
Platform-neutral UART wave4/wave5 encoder API.