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"
48#define AR_FL_WARN(...) do { \
49 if (fl::getLogLevel() >= static_cast<fl::u8>(fl::LogLevel::FL_LOG_LEVEL_WARN)) { \
50 FL_WARN(__VA_ARGS__); \
64#if defined(FL_IS_RP2040) || defined(FL_IS_RP2350)
65void countRpPioRxRisingEdges(
void* user_data) {
67 if (edges !=
nullptr) {
74#if defined(ESP32) && FASTLED_ESP32_HAS_PARLIO
75#include "platforms/esp/32/drivers/parlio/channel_driver_parlio.h"
86 AR_FL_WARN(
"[RAW EDGE TIMING] ERROR: RX channel is null");
94 size_t buffer_size = range.
count < 256 ? range.
count : 256;
98 size_t edge_count = rx_channel->getRawEdgeTimes(edges, range.
offset);
100 if (edge_count == 0) {
101 AR_FL_WARN(
"[RAW EDGE TIMING] WARNING: No edge data captured at offset " << range.
offset);
106 size_t start_idx = range.
offset;
107 size_t end_idx = range.
offset + edge_count;
116 edge_dump <<
"[RAW EDGES " << start_idx <<
".." << (end_idx - 1) <<
"]";
117 for (
size_t i = 0; i < edge_count; i++) {
118 edge_dump <<
" " << (edges[i].high ?
"H" :
"L") << edges[i].ns;
120 AR_FL_WARN(edge_dump.
str());
124 if (range.
offset == 0 && edge_count >= 16) {
133 bool has_short_high =
false, has_long_high =
false;
134 bool has_short_low =
false, has_long_low =
false;
136 for (
size_t i = 0; i < edge_count; i++) {
139 if (ns >= expected_bit0_high - tolerance && ns <= expected_bit0_high + tolerance)
140 has_short_high =
true;
141 if (ns >= expected_bit1_high - tolerance && ns <= expected_bit1_high + tolerance)
142 has_long_high =
true;
144 if (ns >= expected_bit1_low - tolerance && ns <= expected_bit1_low + tolerance)
145 has_short_low =
true;
146 if (ns >= expected_bit0_low - tolerance && ns <= expected_bit0_low + tolerance)
152 ss <<
"\n[RAW EDGE TIMING] Pattern Analysis:\n";
153 ss <<
" Short HIGH (~" << expected_bit0_high <<
"ns, Bit 0): " << (has_short_high ?
"FOUND ✓" :
"MISSING ✗") <<
"\n";
154 ss <<
" Long HIGH (~" << expected_bit1_high <<
"ns, Bit 1): " << (has_long_high ?
"FOUND ✓" :
"MISSING ✗") <<
"\n";
155 ss <<
" Short LOW (~" << expected_bit1_low <<
"ns, Bit 1): " << (has_short_low ?
"FOUND ✓" :
"MISSING ✗") <<
"\n";
156 ss <<
" Long LOW (~" << expected_bit0_low <<
"ns, Bit 0): " << (has_long_low ?
"FOUND ✓" :
"MISSING ✗");
157 AR_FL_WARN(ss.
str());
159 if (has_short_high && has_long_high && has_short_low && has_long_low) {
160 AR_FL_WARN(
"\n[RAW EDGE TIMING] ✓ Encoder appears to be working correctly (varied timing patterns)");
161 }
else if (!has_short_high && !has_long_high) {
163 ss <<
"[RAW EDGE TIMING] ✗ ENCODER BROKEN: No valid HIGH pulses detected!\n";
164 ss <<
"[RAW EDGE TIMING] Possible causes:\n";
165 ss <<
"[RAW EDGE TIMING] 1. Encoder not reading pixel buffer data\n";
166 ss <<
"[RAW EDGE TIMING] 2. Bytes encoder state machine stuck\n";
167 ss <<
"[RAW EDGE TIMING] 3. Data pointer not passed correctly to encoder";
169 }
else if (!has_short_low && !has_long_low) {
171 AR_FL_WARN(
"[RAW EDGE TIMING] ✗ ENCODER BROKEN: No valid LOW pulses detected!");
173 AR_FL_WARN(
"[RAW EDGE TIMING] ⚠ Partial pattern match - encoder may have issues");
199 leds.data(),
static_cast<int>(
leds.size()),
247 mode, current,
false ,
gamma.get());
260 if (!rx_channel || clock_hz == 0) {
261 AR_FL_WARN(
"[SPI DECODE] Invalid parameters");
265 const uint32_t bit_period_ns =
static_cast<uint32_t>(1000000000ULL / clock_hz);
266 const uint32_t half_bit_ns = bit_period_ns / 2;
267 AR_FL_WARN(
"[SPI DECODE] clock=" << clock_hz <<
" Hz, bit_period=" << bit_period_ns <<
" ns");
270 constexpr size_t MAX_EDGES = 4096;
273 size_t edge_count = rx_channel->getRawEdgeTimes(edge_span, 0);
275 if (edge_count == 0) {
276 AR_FL_WARN(
"[SPI DECODE] No edges captured");
279 AR_FL_WARN(
"[SPI DECODE] Captured " << edge_count <<
" edges");
287 for (
size_t i = 0; i < edge_count; i++) {
289 uint32_t num_bits = (duration + half_bit_ns) / bit_period_ns;
290 if (num_bits == 0) num_bits = 1;
291 uint8_t bit_val = edges[i].high ? 1 : 0;
292 for (
uint32_t b = 0; b < num_bits; b++) {
297 AR_FL_WARN(
"[SPI DECODE] Reconstructed " << bits.
size() <<
" bits");
299 if (bits.
size() < 32) {
300 AR_FL_WARN(
"[SPI DECODE] Too few bits for APA102 frame");
305 size_t total_bytes = bits.
size() / 8;
307 for (
size_t i = 0; i < total_bytes; i++) {
309 for (
int bit = 7; bit >= 0; bit--) {
310 size_t bit_idx = i * 8 + (7 - bit);
311 if (bit_idx < bits.
size() && bits[bit_idx]) {
312 byte_val |= (1 << bit);
315 raw_bytes[i] = byte_val;
318 AR_FL_WARN(
"[SPI DECODE] Decoded " << total_bytes <<
" raw bytes");
323 dbg <<
"[SPI DECODE] First bytes:";
324 size_t show = total_bytes < 20 ? total_bytes : 20;
325 for (
size_t i = 0; i <
show; i++) {
327 uint8_t hi = raw_bytes[i] >> 4;
328 uint8_t lo = raw_bytes[i] & 0xF;
329 dbg << (char)(hi < 10 ?
'0' + hi :
'A' + hi - 10);
330 dbg << (char)(lo < 10 ?
'0' + lo :
'A' + lo - 10);
332 AR_FL_WARN(dbg.
str());
339 size_t data_start = 0;
342 if (total_bytes >= 4 && raw_bytes[0] == 0x00 && raw_bytes[1] == 0x00 &&
343 raw_bytes[2] == 0x00 && raw_bytes[3] == 0x00) {
345 AR_FL_WARN(
"[SPI DECODE] Found start frame at offset 0");
349 AR_FL_WARN(
"[SPI DECODE] No start frame (first edge = first LED data)");
359 size_t led_bytes_written = 0;
360 size_t pos = data_start;
362 while (
pos + 4 <= total_bytes) {
366 if ((header & 0xE0) != 0xE0) {
367 AR_FL_WARN(
"[SPI DECODE] End of LED data at byte " <<
pos
368 <<
" (header=0x" << ((header >> 4) < 10 ?
'0' + (header >> 4) :
'A' + (header >> 4) - 10)
369 << ((header & 0xF) < 10 ?
'0' + (header & 0xF) :
'A' + (header & 0xF) - 10) <<
")");
377 if (led_bytes_written + 3 <= rx_buffer.
size()) {
378 rx_buffer[led_bytes_written + 0] = c0;
379 rx_buffer[led_bytes_written + 1] = c1;
380 rx_buffer[led_bytes_written + 2] = c2;
381 led_bytes_written += 3;
383 AR_FL_WARN(
"[SPI DECODE] rx_buffer full at LED " << (led_bytes_written / 3));
389 size_t num_leds = led_bytes_written / 3;
390 AR_FL_WARN(
"[SPI DECODE] Extracted " << num_leds <<
" LEDs (" << led_bytes_written <<
" RGB bytes)");
391 return led_bytes_written;
406 static_cast<int>(rx_channel->getRawEdgeTimes(edges, 0));
410 const size_t sample_count = rx_channel->getRawEdgeTimes(sample_edges, 0);
412 for (
size_t i = 0; i < sample_count; ++i) {
416 sample << (sample_edges[i].high ?
'H' :
'L') << sample_edges[i].ns;
423 const char* driver_name) {
438 bool is_spi_driver = (
fl::strcmp(driver_name,
"SPI") == 0);
439 bool is_parlio_driver = (
fl::strcmp(driver_name,
"PARLIO") == 0);
440 bool is_i2s_driver = (
fl::strcmp(driver_name,
"I2S") == 0);
444 bool is_lcd_clockless_driver = (
fl::strcmp(driver_name,
"LCD_CLOCKLESS") == 0);
445 bool lcd_clockless_uses_wave3 =
false;
446 if (is_lcd_clockless_driver) {
450 bool uses_wave8 = is_spi_driver || is_parlio_driver || is_i2s_driver
451 || (is_lcd_clockless_driver && !lcd_clockless_uses_wave3);
452 bool uses_wave3 = is_lcd_clockless_driver && lcd_clockless_uses_wave3;
459 const uint32_t tick_ns = is_parlio_driver ? 125 : (period / 8);
462 static_cast<float>(timing.
t1_ns) / period * 8.0f + 0.5f);
464 static_cast<float>(timing.
t1_ns + timing.
t2_ns) / period * 8.0f + 0.5f);
466 const uint32_t actual_t0h = pulses_bit0 * tick_ns;
467 const uint32_t actual_t1h = pulses_bit1 * tick_ns;
468 const uint32_t actual_period = 8 * tick_ns;
469 const char* wave8_name = is_spi_driver ?
"SPI_wave8" :
470 is_parlio_driver ?
"PARLIO_wave8" :
471 is_lcd_clockless_driver ?
"LCD_CLOCKLESS_wave8" :
"I2S_wave8";
474 actual_t1h - actual_t0h,
475 actual_period - actual_t1h,
479 AR_FL_WARN(
"[RX TIMING] " << wave8_name <<
": pulses_bit0=" << pulses_bit0
480 <<
" pulses_bit1=" << pulses_bit1
481 <<
" tick_ns=" << tick_ns
482 <<
" -> T1=" << tx_timing.
T1 <<
" T2=" << tx_timing.
T2
483 <<
" T3=" << tx_timing.
T3);
484 }
else if (uses_wave3) {
487 const uint32_t tick_ns = period / 3;
489 static_cast<float>(timing.
t1_ns) / period * 3.0f + 0.5f);
491 static_cast<float>(timing.
t1_ns + timing.
t2_ns) / period * 3.0f + 0.5f);
492 const uint32_t actual_t0h = ticks_bit0 * tick_ns;
493 const uint32_t actual_t1h = ticks_bit1 * tick_ns;
494 const uint32_t actual_period = 3 * tick_ns;
497 actual_t1h - actual_t0h,
498 actual_period - actual_t1h,
500 "LCD_CLOCKLESS_wave3"
502 AR_FL_WARN(
"[RX TIMING] LCD_CLOCKLESS_wave3: ticks_bit0=" << ticks_bit0
503 <<
" ticks_bit1=" << ticks_bit1
504 <<
" tick_ns=" << tick_ns
505 <<
" -> T1=" << tx_timing.
T1 <<
" T2=" << tx_timing.
T2
506 <<
" T3=" << tx_timing.
T3);
512 const uint32_t tolerance = (uses_wave8 || uses_wave3) ? 200 : 170;
519 rx_timing.gap_tolerance_ns = 100000;
533 size_t expected_data_bytes,
536 const char* driver_name,
561 rx_config.hz = 40000000;
565 bool is_uart_driver = (
fl::strcmp(driver_name,
"UART") == 0)
569 bool is_object_fled_driver = (
fl::strcmp(driver_name,
"OBJECT_FLED") == 0);
571 rx_buffer.
size(), expected_data_bytes, rx_channel->backend());
572#if defined(FL_IS_TEENSY_4X)
581 if (rx_config.edge_capacity == 0) {
582 FL_ERROR(
"[CAPTURE] RX edge-capacity overflow");
585#if defined(FL_IS_RP2040) || defined(FL_IS_RP2350)
589 const bool is_rp_pio_capture =
594 if (is_rp_pio_capture) {
599 constexpr size_t kPioPhasesPerDataByte = 16;
604 constexpr size_t kPioPhaseHeadroomNumerator = 2;
605 constexpr size_t kPioPhasesPerDataByteWithHeadroom =
606 kPioPhasesPerDataByte * kPioPhaseHeadroomNumerator;
607 if (expected_data_bytes >
608 (
static_cast<size_t>(-1) - 1u) / kPioPhasesPerDataByteWithHeadroom) {
609 FL_ERROR(
"[CAPTURE] PIO edge-capacity overflow");
619 expected_data_bytes * kPioPhasesPerDataByteWithHeadroom + 1u;
623 rx_config.edge_capacity = requested;
627 bool is_rmt_driver = (
fl::strcmp(driver_name,
"RMT") == 0);
628 const int rx_pin = rx_channel->getPin();
635 const bool use_rmt_internal_loopback =
637 is_rmt_driver, tx_pin, rx_pin);
638 rx_config.io_loop_back = use_rmt_internal_loopback;
646 rx_config.use_dma = !is_rmt_driver;
652 rx_config.backend = rx_channel->backend();
656 rx_config.stream_timing = rx_timing;
657 if (use_rmt_internal_loopback) {
658 AR_FL_WARN(
"[CAPTURE] RMT TX -> RMT RX: Same-pin internal loopback enabled");
666 AR_FL_WARN(
"[CAPTURE] " << driver_name
667 <<
" TX GPIO " << tx_pin <<
" -> RX GPIO " << rx_pin
668 <<
": External jumper "
669 <<
"(io_loop_back=false, use_dma="
670 << (rx_config.use_dma ?
"true" :
"false") <<
")");
680 const uint32_t TX_WAIT_TIMEOUT_MS = 1000;
686 AR_FL_WARN(
"[CAPTURE] RMT: Two-TX approach (ESP32-S3 workaround)");
689 if (!
FastLED.wait(TX_WAIT_TIMEOUT_MS)) {
690 FL_ERROR(
"[CAPTURE] TX wait timeout (pre-arm) - driver may be stalled");
695 if (!rx_channel->begin(rx_config)) {
696 if (diagnostics) diagnostics->
rxBeginError = rx_channel->lastBeginError();
697 FL_ERROR(
"Failed to arm RX receiver");
702 if (!
FastLED.wait(TX_WAIT_TIMEOUT_MS)) {
703 FL_ERROR(
"[CAPTURE] TX wait timeout (capture) - driver may be stalled");
708 if (!rx_channel->begin(rx_config)) {
709 if (diagnostics) diagnostics->
rxBeginError = rx_channel->lastBeginError();
710 AR_FL_WARN(
"[CAPTURE] RX begin() failed for pin " << rx_channel->getPin());
714 AR_FL_WARN(
"[CAPTURE] RX armed, calling FastLED.show()...");
717#if defined(FL_IS_RP2040) || defined(FL_IS_RP2350)
720 bool edge_counter_attached =
false;
721 const bool measure_rp_pio =
724 diagnostics !=
nullptr;
725 if (measure_rp_pio) {
734 edge_config.
handler = countRpPioRxRisingEdges;
738 static_cast<fl::u8>(rx_channel->getPin()), edge_config, &edge_counter) == 0;
743 AR_FL_WARN(
"[CAPTURE] FastLED.show() returned, calling wait...");
745#if defined(FL_IS_RP2040) || defined(FL_IS_RP2350)
746 if (edge_counter_attached && edge_counter.
is_valid()) {
751 diagnostics !=
nullptr) {
755 if (!
FastLED.wait(TX_WAIT_TIMEOUT_MS)) {
756 if (is_object_fled_driver) {
757 fl::objectFledDiagnosticsRecord(
"afterFastLedWaitTimeout");
759 AR_FL_WARN(
"[CAPTURE] FastLED.wait() timed out");
762 if (is_object_fled_driver) {
763 fl::objectFledDiagnosticsRecord(
"afterFastLedWait");
766 AR_FL_WARN(
"[CAPTURE] FastLED.wait() done");
776 const uint32_t rx_wait_ms = is_uart_driver ? 500 : 150;
778 AR_FL_WARN(
"[CAPTURE] Waiting for RX completion (" << rx_wait_ms
779 <<
"ms timeout)...");
781 auto wait_result = rx_channel->wait(rx_wait_ms);
786 collectRawEdgeDiagnostics(rx_channel, diagnostics);
790 AR_FL_WARN(
"[CAPTURE] RX wait returned: "
791 <<
static_cast<int>(wait_result));
795 AR_FL_WARN(
"RX wait failed (timeout or no data received)");
797 ss <<
"\n⚠️ TROUBLESHOOTING:\n";
798 ss <<
" 1. Connect physical jumper wire from TX GPIO to RX GPIO " << rx_channel->getPin() <<
"\n";
799 ss <<
" 2. Check that both TX and RX pins are correctly configured\n";
800 ss <<
" 3. Verify the GPIO connection is working (GPIO baseline test should pass)\n";
801 ss <<
" 4. RMT internal loopback requires TX and RX to use the same GPIO";
802 AR_FL_WARN(ss.
str());
803 if (!is_uart_driver) {
807 AR_FL_WARN(
"[CAPTURE] UART: attempting decode with captured edges despite timeout");
820 if (is_uart_driver) {
821 AR_FL_WARN(
"[CAPTURE] UART (inverted TX): using standard WS2812 decoder with UART timing...");
831#if defined(FL_IS_RP2040) || defined(FL_IS_RP2350)
835 }
else if (
fl::strcmp(driver_name,
"UART1") == 0) {
840 if (uart_timing.T1 == 0) {
845 if (uart_timing.T1 == 0 && uart_timing.T2 == 0) {
848 AR_FL_WARN(
"[CAPTURE] UART: no representable wave geometry for "
857 AR_FL_WARN(
"[CAPTURE] UART RX timing: T0H=" << uart_timing.T1
858 <<
" T1H=" << (uart_timing.T1 + uart_timing.T2)
859 <<
" T0L=" << (uart_timing.T2 + uart_timing.T3)
860 <<
" T1L=" << uart_timing.T3);
862 auto decode_result = rx_channel->decode(rx_timing, rx_buffer);
863 if (!decode_result.ok()) {
864 AR_FL_WARN(
"[CAPTURE] UART decode failed (error: " <<
static_cast<int>(decode_result.error()) <<
")");
872 AR_FL_WARN(
"[CAPTURE] UART decoded " << decode_result.value() <<
" LED bytes");
873 return decode_result.value();
879 bool is_lcd_spi_driver = (
fl::strcmp(driver_name,
"LCD_SPI") == 0);
880 bool is_i2s_spi_driver = (
fl::strcmp(driver_name,
"I2S_SPI") == 0);
881 bool is_spi_unified_driver = (
fl::strcmp(driver_name,
"SPI_UNIFIED") == 0);
882 if (is_lcd_spi_driver || is_i2s_spi_driver || is_spi_unified_driver) {
884 const uint32_t spi_clock_hz = 2400000;
885 AR_FL_WARN(
"[CAPTURE] SPI chipset decode: clock=" << spi_clock_hz <<
" Hz");
886 size_t decoded = decodeSpiEdges(rx_channel, rx_buffer, spi_clock_hz);
888 AR_FL_WARN(
"[CAPTURE] SPI decode failed");
900 AR_FL_WARN(
"[CAPTURE] Decoding...");
901 auto decode_result = rx_channel->decode(rx_timing, rx_buffer);
903 diagnostics->
decodeOk = decode_result.ok() ? 1 : 0;
905 decode_result.ok() ? -1 :
static_cast<int>(decode_result.error());
907 decode_result.ok() ?
static_cast<int>(decode_result.value()) : 0;
910 if (!decode_result.ok()) {
913 AR_FL_WARN(
"Decode failed (error code: " <<
static_cast<int>(decode_result.error()) <<
")");
923 AR_FL_WARN(
"[CAPTURE] Decoded " << decode_result.value() <<
" bytes");
924 return decode_result.value();
929void runTest(
const char* test_name,
931 int& total,
int& passed) {
937 AR_FL_WARN(
"\n[MULTI-LANE] Testing " << config.
tx_configs.
size() <<
" lanes, testing Lane 0 only (hardware limitation)");
941 for (
size_t config_idx = 0; config_idx < channels_to_test; config_idx++) {
946 size_t num_leds =
leds.size();
947 const bool lane_uses_rgbw =
948 legacyLaneUsesRgbw(config, config_idx);
953 const bool lane_uses_ucs7604 = isUCS7604(config.
encoder);
955 if (lane_uses_ucs7604) {
956 expected_encoded = buildExpectedUCS7604(
959 const size_t expected_data_bytes =
960 lane_uses_ucs7604 ? expected_encoded.
size()
961 : num_leds * (lane_uses_rgbw ? 4u : 3u);
969 static_cast<int>(config_idx),
971 static_cast<int>(num_leds),
975 AR_FL_WARN(
"\n=== " << test_name <<
" [Lane " << config_idx <<
"/" << config.
tx_configs.
size()
976 <<
", Pin " << config.
tx_configs[config_idx].getDataPin()
977 <<
", LEDs " << config.
tx_configs[config_idx].mLeds.
size() <<
"] ===");
981 FL_ERROR(
"[" << ctx.driver_name <<
"/" << ctx.timing_name <<
"/" << ctx.pattern_name
982 <<
" | Lane " << ctx.lane_index <<
"/" << ctx.lane_count
983 <<
" (Pin " << ctx.pin_number <<
", " << ctx.num_leds <<
" LEDs) | RX:" << ctx.rx_type_name <<
"] "
984 <<
"RX channel is null - must be created in .ino and passed via AutoResearchConfig");
985 FL_ERROR(
"Result: FAIL ✗ (RX channel not provided)");
994 if (bytes_captured == 0) {
995 FL_ERROR(
"[" << ctx.driver_name <<
"/" << ctx.timing_name <<
"/" << ctx.pattern_name
996 <<
" | Lane " << ctx.lane_index <<
"/" << ctx.lane_count
997 <<
" (Pin " << ctx.pin_number <<
", " << ctx.num_leds <<
" LEDs) | RX:" << ctx.rx_type_name <<
"] "
998 <<
"Result: FAIL ✗ (capture failed)");
1004 if (lane_uses_ucs7604) {
1006 size_t expected_len = expected_encoded.
size();
1008 AR_FL_WARN(
"UCS7604 encoded comparison: expected " << expected_len <<
" bytes, captured " << bytes_captured);
1010 size_t compare_len = (bytes_captured < expected_len) ? bytes_captured : expected_len;
1011 for (
size_t i = 0; i < compare_len; i++) {
1012 if (expected_encoded[i] != config.
rx_buffer[i]) {
1017 if (bytes_captured < expected_len) {
1018 mismatches +=
static_cast<int>(expected_len - bytes_captured);
1021 AR_FL_WARN(
"Bytes Captured: " << bytes_captured <<
" (expected: " << expected_len <<
")");
1022 int total_bytes =
static_cast<int>(expected_len);
1023 AR_FL_WARN(
"Accuracy: " << (100.0 * (total_bytes - mismatches) / total_bytes) <<
"% ("
1024 << (total_bytes - mismatches) <<
"/" << total_bytes <<
" bytes match)");
1025 }
else if (lane_uses_rgbw) {
1027 buildExpectedClockless(
1029 const size_t expected_len = expected.
size();
1030 const size_t compare_len =
1031 bytes_captured < expected_len ? bytes_captured : expected_len;
1032 for (
size_t i = 0; i < compare_len; ++i) {
1033 if (expected[i] != config.
rx_buffer[i]) {
1037 if (bytes_captured < expected_len) {
1039 static_cast<int>(expected_len - bytes_captured);
1042 AR_FL_WARN(
"RGBW bytes captured: " << bytes_captured
1043 <<
" (expected: " << expected_len <<
")");
1044 AR_FL_WARN(
"Accuracy: "
1045 << (100.0 * (expected_len - mismatches) / expected_len)
1046 <<
"% (" << (expected_len - mismatches) <<
"/"
1047 << expected_len <<
" bytes match)");
1050 size_t bytes_expected = num_leds * 3;
1053 const size_t front_padding_bytes = 0;
1054 const size_t rx_buffer_offset = front_padding_bytes;
1056 if (bytes_captured > bytes_expected + front_padding_bytes) {
1057 AR_FL_WARN(
"Info: Captured " << bytes_captured <<
" bytes ("
1058 << front_padding_bytes <<
" front pad + "
1059 << bytes_expected <<
" LED data + "
1060 << (bytes_captured - bytes_expected - front_padding_bytes) <<
" back pad/RESET)");
1064 size_t bytes_to_check = (bytes_captured < bytes_expected + rx_buffer_offset) ?
1065 (bytes_captured > rx_buffer_offset ? bytes_captured - rx_buffer_offset : 0) :
1067 (void)bytes_to_check;
1069 for (
size_t i = 0; i < num_leds; i++) {
1070 size_t byte_offset = rx_buffer_offset + i * 3;
1071 if (byte_offset + 2 >= bytes_captured) {
1072 FL_ERROR(
"[" << ctx.driver_name <<
"/" << ctx.timing_name <<
"/" << ctx.pattern_name
1073 <<
" | Lane " << ctx.lane_index <<
"/" << ctx.lane_count
1074 <<
" (Pin " << ctx.pin_number <<
", " << ctx.num_leds <<
" LEDs) | RX:" << ctx.rx_type_name <<
"] "
1075 <<
"Incomplete data for LED[" <<
static_cast<int>(i)
1076 <<
"] (only " << bytes_captured <<
" bytes captured)");
1088 if (expected_r != actual_r || expected_g != actual_g || expected_b != actual_b) {
1093 AR_FL_WARN(
"Bytes Captured: " << bytes_captured <<
" (expected: " << bytes_expected <<
")");
1094 AR_FL_WARN(
"Accuracy: " << (100.0 * (num_leds - mismatches) / num_leds) <<
"% ("
1095 << (num_leds - mismatches) <<
"/" << num_leds <<
" LEDs match)");
1098 if (mismatches == 0) {
1099 AR_FL_WARN(
"Result: PASS ✓");
1102 FL_ERROR(
"[" << ctx.driver_name <<
"/" << ctx.timing_name <<
"/" << ctx.pattern_name
1103 <<
" | Lane " << ctx.lane_index <<
"/" << ctx.lane_count
1104 <<
" (Pin " << ctx.pin_number <<
", " << ctx.num_leds <<
" LEDs) | RX:" << ctx.rx_type_name <<
"] "
1105 <<
"Result: FAIL ✗");
1115 int& total,
int& passed,
1135 ss <<
"\n╔════════════════════════════════════════════════════════════════╗\n";
1136 ss <<
"║ MULTI-RUN TEST: " << test_name <<
"\n";
1137 ss <<
"║ Runs: " << multi_config.
num_runs <<
" | Print Mode: "
1138 << (multi_config.
print_all_runs ?
"All" :
"Errors ONLY") <<
"\n";
1139 ss <<
"╚════════════════════════════════════════════════════════════════╝";
1140 AR_FL_WARN(ss.
str());
1144 int total_passed = 0;
1145 int total_failed = 0;
1146 bool retained_success =
false;
1152 AR_FL_WARN(
"[MULTI-LANE] Testing " << config.
tx_configs.
size() <<
" lanes, testing Lane 0 only");
1159 if (channels_to_test > 0 && legacyLaneUsesRgbw(config, 0)) {
1161 buildExpectedClockless(config.
tx_configs[0].mLeds,
true);
1168 const bool lane_uses_ucs7604 = isUCS7604(config.
encoder);
1170 if (channels_to_test > 0 && lane_uses_ucs7604) {
1185 if (kLogsEnabled && (run % 3 == 1 || multi_config.
num_runs <= 5)) {
1186 AR_FL_WARN(
"[Run " << run <<
"/" << multi_config.
num_runs <<
"] Testing...");
1193 for (
size_t config_idx = 0; config_idx < channels_to_test; config_idx++) {
1195 size_t num_leds =
leds.size();
1196 const bool lane_uses_rgbw =
1197 legacyLaneUsesRgbw(config, config_idx);
1198 const size_t expected_data_bytes =
1199 lane_uses_ucs7604 ? expected_ucs7604.
size()
1200 : num_leds * (lane_uses_rgbw ? 4u : 3u);
1201 result.total_leds = num_leds;
1202 result.totalBytes =
static_cast<int>(expected_data_bytes);
1205 size_t bytes_captured =
1207 expected_data_bytes,
1211 result.capturedBytes =
static_cast<int>(bytes_captured);
1213 if (bytes_captured == 0) {
1214 AR_FL_WARN(
"[Run " << run <<
"] Capture failed");
1215 result.mismatches =
static_cast<int>(num_leds);
1216 result.mismatchedBytes =
static_cast<int>(
result.totalBytes);
1217 result.captureFailed =
true;
1222 collectRawEdgeDiagnostics(config.
rx_channel, &result);
1238 size_t dump_count = (bytes_captured < 24) ? bytes_captured : 24;
1239 for (
size_t i = 0; i < dump_count; i++) {
1240 if (i > 0) hex_dump <<
" ";
1243 AR_FL_WARN(
"[RUN " << run <<
"] Driver=" << config.
driver_name
1244 <<
" bytes_captured=" << bytes_captured
1245 <<
" first" <<
static_cast<int>(dump_count) <<
"=" << hex_dump.
str());
1248 if (lane_uses_ucs7604) {
1251 size_t expected_len = expected_encoded.
size();
1252 result.totalBytes =
static_cast<int>(expected_len);
1254 size_t compare_len = (bytes_captured < expected_len) ? bytes_captured : expected_len;
1255 for (
size_t i = 0; i < compare_len; i++) {
1256 if (expected_encoded[i] != config.
rx_buffer[i]) {
1257 result.mismatchedBytes++;
1258 if ((expected_encoded[i] ^ config.
rx_buffer[i]) == 0x01) {
1264 if (mismatches == 1) {
1265 AR_FL_WARN(
"\n[CORRUPTION @ byte " <<
static_cast<int>(i) <<
", Run " << run
1266 <<
"] expected=0x" <<
fl::hex <<
static_cast<int>(expected_encoded[i])
1273 static_cast<int>(i), expected_encoded[i], 0, 0,
1280 if (bytes_captured < expected_len) {
1281 mismatches +=
static_cast<int>(expected_len - bytes_captured);
1283 }
else if (lane_uses_rgbw) {
1285 size_t verified_leds = 0;
1286 for (
size_t i = 0; i < num_leds; ++i) {
1287 const size_t byte_offset = i * 4;
1288 if (byte_offset + 3 >= bytes_captured) {
1291 verified_leds = i + 1;
1293 bool led_mismatch =
false;
1294 for (
size_t channel = 0; channel < 4; ++channel) {
1296 expected[byte_offset + channel];
1298 config.
rx_buffer[byte_offset + channel];
1299 if (expected_byte != actual_byte) {
1300 ++
result.mismatchedBytes;
1301 if ((expected_byte ^ actual_byte) == 0x01) {
1304 led_mismatch =
true;
1309 if (mismatches == 0) {
1310 AR_FL_WARN(
"\n[RGBW CORRUPTION @ LED "
1311 <<
static_cast<int>(i) <<
", Run "
1313 const size_t corruption_edge_index = i * 64;
1315 corruption_edge_index > 4
1316 ? corruption_edge_index - 4
1324 if (
result.errors.size() <
1325 static_cast<size_t>(
1328 static_cast<int>(i),
1329 expected[byte_offset + 0],
1330 expected[byte_offset + 1],
1331 expected[byte_offset + 2],
1332 expected[byte_offset + 3],
1341 if (verified_leds < num_leds) {
1342 const size_t unchecked = num_leds - verified_leds;
1343 mismatches +=
static_cast<int>(unchecked);
1344 result.mismatchedBytes +=
1345 static_cast<int>(unchecked * 4);
1346 AR_FL_WARN(
"[TRUNCATED RGBW CAPTURE] Only verified "
1347 << verified_leds <<
"/" << num_leds
1348 <<
" LEDs (" << bytes_captured
1349 <<
" bytes captured, needed "
1350 << expected_data_bytes <<
"). Marking "
1352 <<
" unchecked LEDs as mismatches.");
1356 size_t bytes_expected = num_leds * 3;
1359 const size_t rx_buffer_offset = 0;
1361 size_t bytes_to_check = (bytes_captured < bytes_expected + rx_buffer_offset) ?
1362 (bytes_captured > rx_buffer_offset ? bytes_captured - rx_buffer_offset : 0) :
1364 (void)bytes_to_check;
1366 size_t verified_leds = 0;
1367 for (
size_t i = 0; i < num_leds; i++) {
1368 size_t byte_offset = rx_buffer_offset + i * 3;
1369 if (byte_offset + 2 >= bytes_captured) {
1372 verified_leds = i + 1;
1383 uint8_t exp_bytes[3] = {expected_r, expected_g, expected_b};
1384 uint8_t act_bytes[3] = {actual_r, actual_g, actual_b};
1385 bool led_mismatch =
false;
1386 for (
int ch = 0; ch < 3; ch++) {
1387 if (exp_bytes[ch] != act_bytes[ch]) {
1388 result.mismatchedBytes++;
1389 if ((exp_bytes[ch] ^ act_bytes[ch]) == 0x01) {
1392 led_mismatch =
true;
1398 if (mismatches == 0) {
1399 AR_FL_WARN(
"\n[CORRUPTION @ LED " <<
static_cast<int>(i) <<
", Run " << run <<
"]");
1402 size_t corruption_edge_index = i * 48;
1403 size_t offset = corruption_edge_index > 4 ? corruption_edge_index - 4 : 0;
1414 i, expected_r, expected_g, expected_b,
1415 actual_r, actual_g, actual_b
1426 if (verified_leds < num_leds) {
1427 size_t unchecked = num_leds - verified_leds;
1428 mismatches +=
static_cast<int>(unchecked);
1429 result.mismatchedBytes +=
static_cast<int>(unchecked * 3);
1430 AR_FL_WARN(
"[TRUNCATED CAPTURE] Only verified " << verified_leds
1431 <<
"/" << num_leds <<
" LEDs (" << bytes_captured
1432 <<
" bytes captured, needed " << (num_leds * 3)
1433 <<
"). Marking " << unchecked
1434 <<
" unchecked LEDs as mismatches.");
1438 result.mismatches = mismatches;
1439 result.passed = (mismatches == 0);
1441 collectRawEdgeDiagnostics(config.
rx_channel, &result);
1448 AR_FL_WARN(
"[Run " << run <<
"/" << multi_config.
num_runs <<
"] "
1449 << (
result.passed ?
"PASS" :
"FAIL")
1450 <<
" | Errors: " <<
result.mismatches <<
"/" <<
result.total_leds
1451 <<
" (" << (100.0 * (
result.total_leds -
result.mismatches) /
result.total_leds) <<
"%)");
1455 AR_FL_WARN(
" First " <<
result.errors.size() <<
" error(s):");
1456 for (
size_t i = 0; i <
result.errors.size(); i++) {
1457 const auto& err =
result.errors[i];
1458 AR_FL_WARN(
" LED[" << err.led_index <<
"]: expected RGB("
1459 <<
static_cast<int>(err.expected_r) <<
","
1460 <<
static_cast<int>(err.expected_g) <<
","
1461 <<
static_cast<int>(err.expected_b) <<
") got RGB("
1462 <<
static_cast<int>(err.actual_r) <<
","
1463 <<
static_cast<int>(err.actual_g) <<
","
1464 <<
static_cast<int>(err.actual_b) <<
")");
1480 if (!
result.passed || !retained_success || multi_config.
num_runs == 1) {
1482 retained_success =
true;
1494 ss <<
"\n╔════════════════════════════════════════════════════════════════╗\n";
1495 ss <<
"║ MULTI-RUN SUMMARY\n";
1496 ss <<
"╚════════════════════════════════════════════════════════════════╝\n";
1497 ss <<
"Total Runs: " << multi_config.
num_runs <<
"\n";
1498 ss <<
"Passed: " << total_passed <<
" (" << (100.0 * total_passed / multi_config.
num_runs) <<
"%)\n";
1499 ss <<
"Failed: " << total_failed <<
" (" << (100.0 * total_failed / multi_config.
num_runs) <<
"%)";
1500 AR_FL_WARN(ss.
str());
1502 if (total_failed > 0) {
1504 ss <<
"\nFailed Run Numbers:\n";
1505 for (
const auto& r : run_results) {
1507 ss <<
" Run #" << r.run_number <<
" - " << r.mismatches <<
" errors\n";
1508 if (!r.errors.empty()) {
1509 ss <<
" First error at LED[" << r.errors[0].led_index <<
"]: "
1510 <<
"expected RGB(" <<
static_cast<int>(r.errors[0].expected_r) <<
","
1511 <<
static_cast<int>(r.errors[0].expected_g) <<
","
1512 <<
static_cast<int>(r.errors[0].expected_b) <<
") got RGB("
1513 <<
static_cast<int>(r.errors[0].actual_r) <<
","
1514 <<
static_cast<int>(r.errors[0].actual_g) <<
","
1515 <<
static_cast<int>(r.errors[0].actual_b) <<
")\n";
1519 AR_FL_WARN(ss.
str());
1525 for (
const auto& r : run_results) {
1532 if (total_failed == 0) {
1535 AR_FL_WARN(
"\n[OVERALL] PASS ✓ - All " << multi_config.
num_runs <<
" runs succeeded");
1539 AR_FL_WARN(
"\n[OVERALL] FAIL ✗ - " << total_failed <<
"/" << multi_config.
num_runs <<
" runs failed");
1547 int& driver_total,
int& driver_passed,
1550 int num_runs_per_pattern) {
1552 ss <<
"\n========================================\n";
1555 if (has_spi_config) {
1556 const auto* spi_cfg = config.
tx_configs[0].getSpiChipset();
1557 ss <<
" Protocol: SPI (APA102)\n";
1558 ss <<
" Clock: " << (spi_cfg ? spi_cfg->timing.clock_hz : 0) <<
" Hz\n";
1566 ss <<
"========================================";
1567 AR_FL_WARN(ss.
str());
1587 channel =
FastLED.add(channel_config);
1590 FL_ERROR(
"Failed to create channel " << i <<
" (pin " << config.
tx_configs[i].getDataPin() <<
") - platform not supported");
1592 for (
auto& ch : channels) {
1597 channels.push_back(channel);
1608 FL_ERROR(
"[PREINIT] TX wait timeout - driver may be stalled on this platform");
1628 multi_config.
num_runs = (num_runs_per_pattern > 0) ? num_runs_per_pattern : 1;
1637 for (
int pattern_id = 0; pattern_id < 4; pattern_id++) {
1649 out_show_duration_ms +=
millis() - show_start_ms;
1654 driver_total += total;
1655 driver_passed += passed;
1662 fl::objectFledDiagnosticsRecord(
"afterFastLedClear");
1672 int& driver_total,
int& driver_passed,
1675 int num_runs_per_pattern) {
1677 ss <<
"\n========================================\n";
1678 ss <<
"Testing (LEGACY API): " << config.
timing_name <<
"\n";
1685 ss <<
" Lane " << i <<
": pin=" << config.
tx_configs[i].getDataPin()
1688 ss <<
"========================================";
1689 AR_FL_WARN(ss.
str());
1707 if (!proxy->valid()) {
1708 FL_ERROR(
"Legacy proxy invalid for lane " << i <<
" (pin " << pin
1710 <<
" not in supported set)");
1724 FL_ERROR(
"[LEGACY] TX wait timeout - driver may be stalled");
1735 multi_config.
num_runs = (num_runs_per_pattern > 0) ? num_runs_per_pattern : 1;
1743 const bool ws2814_visual_validation =
1746 const int pattern_count = ws2814_visual_validation ? 8 : 4;
1747 for (
int pattern_id = 0; pattern_id < pattern_count; pattern_id++) {
1757 if (ws2814_visual_validation && pattern_id >= 4) {
1765 out_show_duration_ms +=
millis() - show_start_ms;
1767 driver_total += total;
1768 driver_passed += passed;
1775 switch (pattern_id) {
1779 for (
size_t i = 0; i < count; i++) {
1787 for (
size_t i = 0; i < count; i++) {
1795 for (
size_t i = 0; i < count; i++) {
1803 for (
size_t i = 0; i < count; i++) {
1804 int color_index = i % 3;
1805 if (color_index == 0) {
1807 }
else if (color_index == 1) {
1840 switch (pattern_id) {
1841 case 0:
return "Pattern A (R=0xF0, G=0x0F, B=0xAA)";
1842 case 1:
return "Pattern B (R=0x55, G=0xFF, B=0x00)";
1843 case 2:
return "Pattern C (R=0x0F, G=0xAA, B=0xF0)";
1844 case 3:
return "Pattern D (RGB Solid Alternating)";
1845 case 4:
return "Pattern E (Isolated Red)";
1846 case 5:
return "Pattern F (Isolated Green)";
1847 case 6:
return "Pattern G (Isolated Blue)";
1848 case 7:
return "Pattern H (Isolated White)";
1849 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.
const char * legacyClocklessChipsetName(LegacyClocklessChipset chipset)
bool legacyClocklessChipsetHasAutomaticRgbw(LegacyClocklessChipset chipset)
Runtime chipset timing configuration for clockless LED drivers.
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.
fl::UISlider offset("Offset", 0.0f, 0.0f, 1.0f, 0.01f)
#define DISABLE_DITHER
Disable dithering.
UCS7604 LED chipset encoder implementation.
@ CHANNELS
Remove all channels from controller list.
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.
void run(fl::u32 microseconds, ExecFlags flags) FL_NO_EXCEPT
Run selected task subsystems.
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)
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)
const char * toString(RxDeviceType type) FL_NO_EXCEPT
Convert RxDeviceType to human-readable string.
@ 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.