317 {
318 if (!rx_channel) {
320 return 0;
321 }
322
323
325
326
328 rx_config.hz = 40000000;
329
330
331
332 bool is_uart_driver = (
fl::strcmp(driver_name,
"UART") == 0);
333 rx_config.edge_capacity = rx_buffer.
size() * 8;
334
335
336
337
338
339 bool is_rmt_driver = (
fl::strcmp(driver_name,
"RMT") == 0);
340 rx_config.io_loop_back = is_rmt_driver;
341
342
343
344
345
346
347
348 rx_config.use_dma = !is_rmt_driver;
349 if (is_rmt_driver) {
350 FL_WARN(
"[CAPTURE] RMT TX -> RMT RX: Internal loopback enabled (io_loop_back=true)");
351 } else {
352
353
354
355
356
357 FL_WARN(
"[CAPTURE] " << driver_name <<
" TX -> external RX: External GPIO wire (io_loop_back=false, use_dma=true)");
358 }
359
360
361
362
363
364
365
366 const uint32_t TX_WAIT_TIMEOUT_MS = 1000;
367
368 if (is_rmt_driver) {
369
370
371 FL_WARN(
"[CAPTURE] RMT: Two-TX approach (ESP32-S3 workaround)");
373 if (!
FastLED.wait(TX_WAIT_TIMEOUT_MS)) {
374 FL_ERROR(
"[CAPTURE] TX wait timeout (pre-arm) - driver may be stalled");
375 return 0;
376 }
377
378
379 if (!rx_channel->
begin(rx_config)) {
380 FL_ERROR(
"Failed to arm RX receiver");
381 return 0;
382 }
383
385 if (!
FastLED.wait(TX_WAIT_TIMEOUT_MS)) {
386 FL_ERROR(
"[CAPTURE] TX wait timeout (capture) - driver may be stalled");
387 return 0;
388 }
389 } else {
390
391 if (!rx_channel->
begin(rx_config)) {
392 FL_WARN(
"[CAPTURE] RX begin() failed for pin " << rx_channel->
getPin());
393 return 0;
394 }
395 FL_WARN(
"[CAPTURE] RX armed, calling FastLED.show()...");
396
398 FL_WARN(
"[CAPTURE] FastLED.show() returned, calling wait...");
399 if (!
FastLED.wait(TX_WAIT_TIMEOUT_MS)) {
400 FL_WARN(
"[CAPTURE] FastLED.wait() timed out");
401 return 0;
402 }
403 FL_WARN(
"[CAPTURE] FastLED.wait() done");
404 }
405
406
407
408
409
410
411
412 const uint32_t rx_wait_ms = is_uart_driver ? 500 : 150;
413 FL_WARN(
"[CAPTURE] Waiting for RX completion (" << rx_wait_ms <<
"ms timeout)...");
414 auto wait_result = rx_channel->
wait(rx_wait_ms);
415 FL_WARN(
"[CAPTURE] RX wait returned: " <<
static_cast<int>(wait_result));
416
418 FL_WARN(
"RX wait failed (timeout or no data received)");
420 ss << "\n⚠️ TROUBLESHOOTING:\n";
421 ss <<
" 1. Connect physical jumper wire from TX GPIO to RX GPIO " << rx_channel->
getPin() <<
"\n";
422 ss << " 2. Check that both TX and RX pins are correctly configured\n";
423 ss << " 3. Verify the GPIO connection is working (GPIO baseline test should pass)\n";
424 ss << " 4. For RMT TX → RMT RX: Ensure io_loop_back=true in RxConfig";
426 if (!is_uart_driver) {
427 return 0;
428 }
429
430 FL_WARN(
"[CAPTURE] UART: attempting decode with captured edges despite timeout");
431 }
432
433
434
435
436
437
438
439 if (is_uart_driver) {
440 FL_WARN(
"[CAPTURE] UART (inverted TX): using standard WS2812 decoder with UART timing...");
441
443 250,
444 500,
445 500,
446 50,
447 "UART_4Mbps"
448 };
449
450
452 rx_timing.gap_tolerance_ns = 100000;
453
454 FL_WARN(
"[CAPTURE] UART RX timing: T0H=" << uart_timing.T1
455 << " T1H=" << (uart_timing.T1 + uart_timing.T2)
456 << " T0L=" << (uart_timing.T2 + uart_timing.T3)
457 << " T1L=" << uart_timing.T3);
458
459 auto decode_result = rx_channel->
decode(rx_timing, rx_buffer);
460 if (!decode_result.ok()) {
461 FL_WARN(
"[CAPTURE] UART decode failed (error: " <<
static_cast<int>(decode_result.error()) <<
")");
463 return 0;
464 }
465 FL_WARN(
"[CAPTURE] UART decoded " << decode_result.value() <<
" LED bytes");
466 return decode_result.value();
467 }
468
469
470
471
472 bool is_lcd_spi_driver = (
fl::strcmp(driver_name,
"LCD_SPI") == 0);
473 bool is_i2s_spi_driver = (
fl::strcmp(driver_name,
"I2S_SPI") == 0);
474 if (is_lcd_spi_driver || is_i2s_spi_driver) {
475
476 const uint32_t spi_clock_hz = 2400000;
477 FL_WARN(
"[CAPTURE] SPI chipset decode: clock=" << spi_clock_hz <<
" Hz");
478 size_t decoded =
decodeSpiEdges(rx_channel, rx_buffer, spi_clock_hz);
479 if (decoded == 0) {
480 FL_WARN(
"[CAPTURE] SPI decode failed");
482 }
483 return decoded;
484 }
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501 bool is_spi_driver = (
fl::strcmp(driver_name,
"SPI") == 0);
502 bool is_parlio_driver = (
fl::strcmp(driver_name,
"PARLIO") == 0);
503 bool is_i2s_driver = (
fl::strcmp(driver_name,
"I2S") == 0);
504
505
506
507 bool is_lcd_clockless_driver = (
fl::strcmp(driver_name,
"LCD_CLOCKLESS") == 0);
508 bool lcd_clockless_uses_wave3 = false;
509 if (is_lcd_clockless_driver) {
512 }
513 bool uses_wave8 = is_spi_driver || is_parlio_driver || is_i2s_driver
514 || (is_lcd_clockless_driver && !lcd_clockless_uses_wave3);
515 bool uses_wave3 = is_lcd_clockless_driver && lcd_clockless_uses_wave3;
517 if (uses_wave8) {
518
520
521
522 const uint32_t tick_ns = is_parlio_driver ? 125 : (period / 8);
523
525 static_cast<float>(timing.
t1_ns) / period * 8.0f + 0.5f);
527 static_cast<float>(timing.
t1_ns + timing.
t2_ns) / period * 8.0f + 0.5f);
528
529 const uint32_t actual_t0h = pulses_bit0 * tick_ns;
530 const uint32_t actual_t1h = pulses_bit1 * tick_ns;
531 const uint32_t actual_period = 8 * tick_ns;
532 const char* wave8_name = is_spi_driver ? "SPI_wave8" :
533 is_parlio_driver ? "PARLIO_wave8" :
534 is_lcd_clockless_driver ? "LCD_CLOCKLESS_wave8" : "I2S_wave8";
536 actual_t0h,
537 actual_t1h - actual_t0h,
538 actual_period - actual_t1h,
540 wave8_name
541 };
542 FL_WARN(
"[RX TIMING] " << wave8_name <<
": pulses_bit0=" << pulses_bit0
543 << " pulses_bit1=" << pulses_bit1
544 << " tick_ns=" << tick_ns
545 <<
" -> T1=" << tx_timing.
T1 <<
" T2=" << tx_timing.
T2
546 <<
" T3=" << tx_timing.
T3);
547 } else if (uses_wave3) {
548
550 const uint32_t tick_ns = period / 3;
552 static_cast<float>(timing.
t1_ns) / period * 3.0f + 0.5f);
554 static_cast<float>(timing.
t1_ns + timing.
t2_ns) / period * 3.0f + 0.5f);
555 const uint32_t actual_t0h = ticks_bit0 * tick_ns;
556 const uint32_t actual_t1h = ticks_bit1 * tick_ns;
557 const uint32_t actual_period = 3 * tick_ns;
559 actual_t0h,
560 actual_t1h - actual_t0h,
561 actual_period - actual_t1h,
563 "LCD_CLOCKLESS_wave3"
564 };
565 FL_WARN(
"[RX TIMING] LCD_CLOCKLESS_wave3: ticks_bit0=" << ticks_bit0
566 << " ticks_bit1=" << ticks_bit1
567 << " tick_ns=" << tick_ns
568 <<
" -> T1=" << tx_timing.
T1 <<
" T2=" << tx_timing.
T2
569 <<
" T3=" << tx_timing.
T3);
570 } else {
572 }
573
574
575 const uint32_t tolerance = (uses_wave8 || uses_wave3) ? 200 : 170;
577
578
579
580
581
582 rx_timing.gap_tolerance_ns = 100000;
583
584 FL_WARN(
"[CAPTURE] Decoding...");
585 auto decode_result = rx_channel->
decode(rx_timing, rx_buffer);
586
587 if (!decode_result.ok()) {
588
589
590 FL_WARN(
"Decode failed (error code: " <<
static_cast<int>(decode_result.error()) <<
")");
591
593 return 0;
594 }
595
596 FL_WARN(
"[CAPTURE] Decoded " << decode_result.value() <<
" bytes");
597 return decode_result.value();
598}
void dumpRawEdgeTiming(fl::shared_ptr< fl::RxChannel > rx_channel, const fl::ChipsetTimingConfig &timing, fl::EdgeRange range)
Dump raw edge timing data to console for debugging.
static size_t decodeSpiEdges(fl::shared_ptr< fl::RxChannel > rx_channel, fl::span< uint8_t > rx_buffer, uint32_t clock_hz)
FL_DISABLE_WARNING_PUSH FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS CFastLED FastLED
Global LED strip management instance.
fl::result< u32, DecodeError > decode(const ChipsetTiming4Phase &timing, fl::span< u8 > out) FL_NOEXCEPT
bool begin(const RxChannelConfig &config) FL_NOEXCEPT
RxWaitResult wait(u32 timeout_ms) FL_NOEXCEPT
int getPin() const FL_NOEXCEPT
const T * data() const FL_NOEXCEPT
constexpr fl::size size() const FL_NOEXCEPT
string str() const FL_NOEXCEPT
void * memset(void *s, int c, size_t n) FL_NOEXCEPT
ChipsetTiming4Phase make4PhaseTiming(const ChipsetTiming &timing_3phase, u32 tolerance_ns) FL_NOEXCEPT
Create 4-phase RX timing from 3-phase chipset timing with tolerance.
@ SUCCESS
Operation completed successfully.
int strcmp(const char *s1, const char *s2) FL_NOEXCEPT
FL_OPTIMIZE_FUNCTION bool canUseWave3(const ChipsetTiming &timing)
Check if a chipset timing is eligible for wave3 encoding.
u32 T2
Additional high time for bit 1 (nanoseconds)
u32 T3
Low tail duration (nanoseconds)
u32 T1
High time for bit 0 (nanoseconds)
Generic chipset timing entry Provides T1, T2, T3 timing parameters in nanoseconds for any LED protoco...
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)
Edge range specification for getRawEdgeTimes() debugging.