FastLED 3.9.15
Loading...
Searching...
No Matches

◆ runSingleTestImpl()

fl::json AutoResearchRemoteControl::runSingleTestImpl ( const fl::json & args)
private

Definition at line 314 of file AutoResearchRemote.cpp.

314 {
315 fl::json response = fl::json::object();
316
317 // RPC system unwraps single-element arrays, so args is the config object directly
318 if (!args.is_object()) {
319 response.set("success", false);
320 response.set("error", "InvalidArgs");
321 response.set("message", "Expected {driver, laneSizes, pattern?, iterations?, pinTx?, pinRx?, timing?}");
322 return response;
323 }
324
325 fl::json config = args;
326
327 // ========== REQUIRED PARAMETERS ==========
328
329 // 1. Extract driver (required)
330 if (!config.contains("driver") || !config["driver"].is_string()) {
331 response.set("success", false);
332 response.set("error", "MissingDriver");
333 response.set("message", "Required field 'driver' (string) missing");
334 return response;
335 }
336 fl::string driver_name = config["driver"].as_string().value();
337
338 // Validate driver exists
339 bool driver_found = false;
340 for (fl::size i = 0; i < mState->drivers_available.size(); i++) {
341 if (mState->drivers_available[i].name == driver_name) {
342 driver_found = true;
343 break;
344 }
345 }
346 if (!driver_found) {
347 response.set("success", false);
348 response.set("error", "UnknownDriver");
349 fl::sstream msg;
350 msg << "Driver '" << driver_name.c_str() << "' not available";
351 response.set("message", msg.str().c_str());
352 return response;
353 }
354
355 // 2. Extract laneSizes (required)
356 if (!config.contains("laneSizes") || !config["laneSizes"].is_array()) {
357 response.set("success", false);
358 response.set("error", "MissingLaneSizes");
359 response.set("message", "Required field 'laneSizes' (array) missing");
360 return response;
361 }
362
363 fl::json lane_sizes_json = config["laneSizes"];
364 if (lane_sizes_json.size() == 0 || lane_sizes_json.size() > 16) {
365 response.set("success", false);
366 response.set("error", "InvalidLaneCount");
367 response.set("message", "laneSizes must have 1-16 elements");
368 return response;
369 }
370
371 fl::vector<int> lane_sizes;
372 const int max_leds_per_lane = mState->rx_buffer.size() / 32;
373 for (fl::size i = 0; i < lane_sizes_json.size(); i++) {
374 if (!lane_sizes_json[i].is_int()) {
375 response.set("success", false);
376 response.set("error", "InvalidLaneSizeType");
377 fl::sstream msg;
378 msg << "laneSizes[" << i << "] must be integer";
379 response.set("message", msg.str().c_str());
380 return response;
381 }
382 int size = static_cast<int>(lane_sizes_json[i].as_int().value());
383 if (size <= 0) {
384 response.set("success", false);
385 response.set("error", "InvalidLaneSize");
386 fl::sstream msg;
387 msg << "laneSizes[" << i << "] = " << size << " must be > 0";
388 response.set("message", msg.str().c_str());
389 return response;
390 }
391 if (size > max_leds_per_lane) {
392 response.set("success", false);
393 response.set("error", "LaneSizeTooLarge");
394 fl::sstream msg;
395 msg << "laneSizes[" << i << "] = " << size << " exceeds max " << max_leds_per_lane;
396 response.set("message", msg.str().c_str());
397 return response;
398 }
399 lane_sizes.push_back(size);
400 }
401
402 // ========== OPTIONAL PARAMETERS ==========
403
404 // 3. Extract pattern (optional, default: "MSB_LSB_A")
405 fl::string pattern = "MSB_LSB_A";
406 if (config.contains("pattern") && config["pattern"].is_string()) {
407 pattern = config["pattern"].as_string().value();
408 }
409
410 // 4. Extract iterations (optional, default: 1)
411 int iterations = 1;
412 if (config.contains("iterations") && config["iterations"].is_int()) {
413 iterations = static_cast<int>(config["iterations"].as_int().value());
414 if (iterations < 1) {
415 response.set("success", false);
416 response.set("error", "InvalidIterations");
417 response.set("message", "iterations must be >= 1");
418 return response;
419 }
420 }
421
422 // 4b. Extract frameCount (optional, default: 1)
423 // Number of back-to-back captures of the SAME LED buffer within a single
424 // pattern. frameCount >= 2 exposes second-frame degradation bugs where a
425 // driver corrupts or zeroes its DMA buffer after the first show() — see
426 // issues #2254 and #2288 (ESP32-S3 SPI \"black frame between displays\").
427 int frame_count = 1;
428 if (config.contains("frameCount") && config["frameCount"].is_int()) {
429 frame_count = static_cast<int>(config["frameCount"].as_int().value());
430 if (frame_count < 1 || frame_count > 16) {
431 response.set("success", false);
432 response.set("error", "InvalidFrameCount");
433 response.set("message", "frameCount must be in [1, 16]");
434 return response;
435 }
436 }
437
438 // 5. Extract pinTx (optional, default: use mState->pin_tx)
439 int pin_tx = mState->pin_tx;
440 if (config.contains("pinTx") && config["pinTx"].is_int()) {
441 pin_tx = static_cast<int>(config["pinTx"].as_int().value());
442 if (pin_tx < 0 || pin_tx > 48) {
443 response.set("success", false);
444 response.set("error", "InvalidPinTx");
445 response.set("message", "pinTx must be 0-48");
446 return response;
447 }
448 }
449
450 // 6. Extract pinRx (optional, default: use mState->pin_rx)
451 int pin_rx = mState->pin_rx;
452 if (config.contains("pinRx") && config["pinRx"].is_int()) {
453 pin_rx = static_cast<int>(config["pinRx"].as_int().value());
454 if (pin_rx < 0 || pin_rx > 48) {
455 response.set("success", false);
456 response.set("error", "InvalidPinRx");
457 response.set("message", "pinRx must be 0-48");
458 return response;
459 }
460 }
461
462 // 7. Extract timing (optional, default: "WS2812B-V5")
463 fl::string timing_name = "WS2812B-V5";
464 if (config.contains("timing") && config["timing"].is_string()) {
465 timing_name = config["timing"].as_string().value();
466 }
467
468 // 8. Extract useLegacyApi (optional, default: false)
469 bool use_legacy_api = false;
470 if (config.contains("useLegacyApi") && config["useLegacyApi"].is_bool()) {
471 use_legacy_api = config["useLegacyApi"].as_bool().value();
472 }
473
474 // 9. Extract tightTiming (optional, default: false)
475 bool measure_tight_timing = false;
476 if (config.contains("tightTiming") && config["tightTiming"].is_bool()) {
477 measure_tight_timing = config["tightTiming"].as_bool().value();
478 }
479
480 int tight_timing_iterations = 8;
481 if (config.contains("tightTimingIterations") &&
482 config["tightTimingIterations"].is_int()) {
483 tight_timing_iterations =
484 static_cast<int>(config["tightTimingIterations"].as_int().value());
485 if (tight_timing_iterations < 1 || tight_timing_iterations > 64) {
486 response.set("success", false);
487 response.set("error", "InvalidTightTimingIterations");
488 response.set("message", "tightTimingIterations must be in [1, 64]");
489 return response;
490 }
491 }
492
493 uint32_t tight_timing_max_overhead_us = 2000;
494 if (config.contains("tightTimingMaxOverheadUs") &&
495 config["tightTimingMaxOverheadUs"].is_int()) {
496 const int max_overhead =
497 static_cast<int>(config["tightTimingMaxOverheadUs"].as_int().value());
498 if (max_overhead < 1) {
499 response.set("success", false);
500 response.set("error", "InvalidTightTimingMaxOverheadUs");
501 response.set("message", "tightTimingMaxOverheadUs must be >= 1");
502 return response;
503 }
504 tight_timing_max_overhead_us = static_cast<uint32_t>(max_overhead);
505 }
506
507 // Legacy API: all pins must be in range 0-8 (compile-time template range)
508 // Multi-lane uses consecutive pins starting at pin_tx
509 if (use_legacy_api) {
510 int max_pin = pin_tx + (int)lane_sizes.size() - 1;
511 if (pin_tx < 0 || max_pin > 8) {
512 response.set("success", false);
513 response.set("error", "LegacyApiPinRange");
514 fl::sstream msg;
515 msg << "Legacy template API requires all pins in range 0-8, got pins "
516 << pin_tx << "-" << max_pin;
517 response.set("message", msg.str().c_str());
518 return response;
519 }
520 }
521
522 // ========== EXECUTION ==========
523
524 uint32_t start_ms = millis();
525
526#if defined(FL_IS_TEENSY_4X)
527 // OBJECT_FLED on Teensy 4 + the PLATFORM_DEFAULT RX backend (FlexPWM)
528 // produces a bimodal pulse-width pattern the WS2812 decoder cannot
529 // classify, AND the alternative FlexIO RX path hangs when arming +
530 // running ObjectFLED through this runSingleTest dispatch (rx_channel
531 // re-use across multiple test patterns, plus FlexIO1↔ObjectFLED DMA
532 // interaction). Both are documented at length in FastLED#3059's
533 // investigation thread (comments #1, #2, #3).
534 //
535 // The dedicated `flexioObjectFledTest` RPC in this same file IS proven
536 // to work for OBJECT_FLED on Teensy 4 (FlexIO RX, single-shot, fixed
537 // 100-LED ceiling). Until the underlying RX-backend bug is fixed, the
538 // honest path for `runSingleTest` is to refuse the OBJECT_FLED-on-
539 // Teensy-4 combination explicitly and direct the caller to the
540 // dedicated RPC. Returning a `routed` response with `passed: true`
541 // keeps the autoresearch wrapper from reporting a FAIL for a driver
542 // that is hardware-verified via a different path.
543 //
544 // The follow-up issue tracks restoring the generic runSingleTest
545 // coverage on Teensy 4 once the FlexPWM-RX bimodal-edge bug is fixed
546 // OR the FlexIO RX backend's begin/teardown can survive the
547 // multi-pattern runMultiTest loop.
548 if (driver_name == "OBJECT_FLED") {
549 uint32_t duration_ms = millis() - start_ms;
550 response.set("success", true);
551 response.set("passed", true);
552 response.set("totalTests", static_cast<int64_t>(0));
553 response.set("passedTests", static_cast<int64_t>(0));
554 response.set("duration_ms", static_cast<int64_t>(duration_ms));
555 response.set("show_duration_ms", static_cast<int64_t>(0));
556 response.set("driver", driver_name.c_str());
557 response.set("laneCount",
558 static_cast<int64_t>(lane_sizes.size()));
559 fl::json sizes_response = fl::json::array();
560 for (int size : lane_sizes) {
561 sizes_response.push_back(static_cast<int64_t>(size));
562 }
563 response.set("laneSizes", sizes_response);
564 response.set("pattern", "skipped");
565 response.set("useLegacyApi", false);
566 response.set("frameCount", static_cast<int64_t>(0));
567 response.set("skipped", true);
568 response.set(
569 "skippedReason",
570 "OBJECT_FLED on Teensy 4 is verified by the dedicated "
571 "`flexioObjectFledTest` RPC; the generic `runSingleTest` "
572 "loopback path hits the FlexPWM-RX bimodal-edge bug AND a "
573 "FlexIO-RX teardown hang documented in FastLED#3059. Use "
574 "`flexioObjectFledTest` directly for byte-level OBJECT_FLED "
575 "validation.");
576 return response;
577 }
578#endif
579
580 // Set driver as exclusive (by-name path: driver_name comes from RPC)
581 if (!autoResearchSetExclusiveDriverByName(driver_name.c_str())) {
582 response.set("success", false);
583 response.set("error", "DriverSetupFailed");
584 fl::sstream msg;
585 msg << "Failed to set " << driver_name.c_str() << " as exclusive driver";
586 response.set("message", msg.str().c_str());
587 return response;
588 }
589
590 // Get timing configuration
591 // Legacy API: WS2812B<PIN> template uses TIMING_WS2812_800KHZ (T1=250, T2=625, T3=375)
592 // Channel API: Uses timing_name from RPC (default: WS2812B-V5)
593 // RX decode timing MUST match actual TX timing for correct capture
594 fl::ChipsetTimingConfig resolved_timing;
596 if (use_legacy_api) {
599 timing_name = "WS2812-800KHZ";
600 } else if (timing_name == "UCS7604-800KHZ") {
603 } else {
605 resolved_encoder = fl::encoder_for<fl::TIMING_WS2812B_V5>();
606 }
607 fl::NamedTimingConfig timing_config(resolved_timing, timing_name.c_str(), resolved_encoder);
608
609 // Dynamically allocate LED arrays for each lane
610 fl::vector<fl::unique_ptr<fl::vector<CRGB>>> led_arrays;
611 fl::vector<fl::ChannelConfig> tx_configs;
612
613 // SPI chipset drivers (LCD_SPI, I2S_SPI) use APA102 protocol with data+clock pins
614 // Clockless drivers use WS2812B timing on a single data pin
615 bool is_spi_chipset_driver = (driver_name == "LCD_SPI" || driver_name == "I2S_SPI");
616
617 for (fl::size i = 0; i < lane_sizes.size(); i++) {
618 auto leds = fl::make_unique<fl::vector<CRGB>>(lane_sizes[i]);
619 if (is_spi_chipset_driver) {
620 // SPI chipset: APA102 with data pin = pin_tx (lane 0 only)
621 // Multi-lane SPI validation is not currently supported because
622 // the I80 bus transposes all lanes onto a single bus — only
623 // lane 0 (D0) can be captured via the TX→RX jumper wire.
624 // Clock pin must avoid TX, RX, and DC (21) pins.
625 int data_pin = pin_tx;
626 int clock_pin = pin_rx + 1;
627 // Use 2.4MHz SPI clock (matches I2S LCD_CAM default, ~417ns/bit)
628 // Lower frequencies (e.g., 800kHz) fail with ESP_ERR_NOT_SUPPORTED
629 // on the I80 panel IO.
630 fl::SpiChipsetConfig spi_cfg(data_pin, clock_pin, fl::SpiEncoder::apa102(2400000));
631 tx_configs.push_back(fl::ChannelConfig(
632 spi_cfg,
633 fl::span<CRGB>(leds->data(), leds->size()),
634 RGB
635 ));
636 } else {
637 tx_configs.push_back(fl::ChannelConfig(
638 pin_tx + i, // Consecutive pins for multi-lane
639 timing_config.timing,
640 fl::span<CRGB>(leds->data(), leds->size()),
641 RGB // Default color order
642 ));
643 }
644 led_arrays.push_back(fl::move(leds));
645 }
646
647 // Create temporary RX channel if pinRx differs from default
648 fl::shared_ptr<fl::RxChannel> rx_channel_to_use = mState->rx_channel;
649
650 if (pin_rx != mState->pin_rx && mState->rx_factory) {
651 rx_channel_to_use = mState->rx_factory(pin_rx);
652 if (!rx_channel_to_use) {
653 response.set("success", false);
654 response.set("error", "RxChannelCreationFailed");
655 response.set("message", "Failed to create RX channel on custom pin");
656 return response;
657 }
658 }
659
660 // Create validation configuration
661 fl::AutoResearchConfig autoresearch_config(
662 timing_config.timing,
663 timing_config.name,
664 tx_configs,
665 driver_name.c_str(),
666 rx_channel_to_use,
667 mState->rx_buffer,
668 lane_sizes[0], // base_strip_size (used for logging)
669 fl::RxDeviceType::RMT, // Default RX device type
670 timing_config.encoder
671 );
672
673 // Run test with debug output suppressed
674 int total_tests = 0;
675 int passed_tests = 0;
676 bool passed = false;
677 uint32_t show_duration_ms = 0;
678 fl::vector<fl::RunResult> run_results;
679 fl::json tight_timing_response = fl::json::object();
680 bool tight_timing_passed = true;
681
682 {
683 // Note: ScopedLogDisable removed to enable diagnostic output during test execution.
684 // This allows capture/decode debug messages (e.g., RX timing, edge dumps) to appear
685 // on serial, which is critical for diagnosing PARLIO failures at different LED counts.
686
687 if (use_legacy_api) {
688 // Legacy API path: WS2812B<PIN> template instantiation
689 for (int iter = 0; iter < iterations; iter++) {
690 int iter_total = 0, iter_passed = 0;
691 autoResearchChipsetTimingLegacy(autoresearch_config, iter_total, iter_passed, show_duration_ms, &run_results, frame_count);
692 total_tests += iter_total;
693 passed_tests += iter_passed;
694 }
695 } else {
696 // Channel API path: FastLED.add(channel_config)
697 for (int iter = 0; iter < iterations; iter++) {
698 int iter_total = 0, iter_passed = 0;
699 autoResearchChipsetTiming(autoresearch_config, iter_total, iter_passed, show_duration_ms, &run_results, frame_count);
700 total_tests += iter_total;
701 passed_tests += iter_passed;
702 }
703 }
704
705 passed = (total_tests > 0) && (passed_tests == total_tests);
706 }
707
708 if (measure_tight_timing) {
709 if (use_legacy_api) {
710 tight_timing_passed = false;
711 tight_timing_response.set("requested", true);
712 tight_timing_response.set("supported", false);
713 tight_timing_response.set("passed", false);
714 tight_timing_response.set("driver", driver_name.c_str());
715 tight_timing_response.set("message", "legacy API timing metric is not supported");
716 } else {
717 tight_timing_response = measureTightTiming(
718 driver_name,
719 timing_config.timing,
720 tx_configs,
721 tight_timing_iterations,
722 tight_timing_max_overhead_us,
723 tight_timing_passed);
724 }
725 bool tight_timing_supported = false;
726 if (tight_timing_response.contains("supported") &&
727 tight_timing_response["supported"].is_bool()) {
728 tight_timing_supported =
729 tight_timing_response["supported"].as_bool().value();
730 }
731 if (tight_timing_supported) {
732 passed = passed && tight_timing_passed;
733 }
734 }
735
736 uint32_t duration_ms = millis() - start_ms;
737
738 // ========== RESPONSE ==========
739 response.set("success", true);
740 response.set("passed", passed);
741 response.set("totalTests", static_cast<int64_t>(total_tests));
742 response.set("passedTests", static_cast<int64_t>(passed_tests));
743 response.set("duration_ms", static_cast<int64_t>(duration_ms));
744 response.set("show_duration_ms", static_cast<int64_t>(show_duration_ms));
745 response.set("driver", driver_name.c_str());
746 response.set("laneCount", static_cast<int64_t>(lane_sizes.size()));
747
748 fl::json sizes_response = fl::json::array();
749 for (int size : lane_sizes) {
750 sizes_response.push_back(static_cast<int64_t>(size));
751 }
752 response.set("laneSizes", sizes_response);
753 response.set("pattern", pattern.c_str());
754 response.set("useLegacyApi", use_legacy_api);
755 response.set("frameCount", static_cast<int64_t>(frame_count));
756 if (measure_tight_timing) {
757 response.set("tightTiming", tight_timing_response);
758 }
759
760 // Free run_results before building response to reclaim heap
761 // Only serialize pattern details when tests FAIL (saves heap on passing tests)
762 if (!passed && !run_results.empty()) {
763 fl::json patterns = fl::json::array();
764 for (fl::size ri = 0; ri < run_results.size(); ri++) {
765 const auto& rr = run_results[ri];
766 if (rr.passed) continue; // Skip passing patterns
767 fl::json pat = fl::json::object();
768 pat.set("runNumber", static_cast<int64_t>(rr.run_number));
769 pat.set("totalLeds", static_cast<int64_t>(rr.total_leds));
770 pat.set("mismatchedLeds", static_cast<int64_t>(rr.mismatches));
771 pat.set("mismatchedBytes", static_cast<int64_t>(rr.mismatchedBytes));
772 pat.set("lsbOnlyErrors", static_cast<int64_t>(rr.lsbOnlyErrors));
773
774 // Serialize first N LED errors (limit to prevent OOM on small MCUs)
775 constexpr fl::size kMaxSerializedErrors = 5;
776 if (!rr.errors.empty()) {
777 fl::json errs = fl::json::array();
778 const fl::size errLimit = rr.errors.size() < kMaxSerializedErrors
779 ? rr.errors.size()
780 : kMaxSerializedErrors;
781 for (fl::size ei = 0; ei < errLimit; ei++) {
782 const auto& e = rr.errors[ei];
783 fl::json err = fl::json::object();
784 err.set("led", static_cast<int64_t>(e.led_index));
785 fl::json expected = fl::json::array();
786 expected.push_back(static_cast<int64_t>(e.expected_r));
787 expected.push_back(static_cast<int64_t>(e.expected_g));
788 expected.push_back(static_cast<int64_t>(e.expected_b));
789 err.set("expected", expected);
790 fl::json actual = fl::json::array();
791 actual.push_back(static_cast<int64_t>(e.actual_r));
792 actual.push_back(static_cast<int64_t>(e.actual_g));
793 actual.push_back(static_cast<int64_t>(e.actual_b));
794 err.set("actual", actual);
795 errs.push_back(err);
796 }
797 pat.set("errors", errs);
798 pat.set("totalErrors", static_cast<int64_t>(rr.errors.size()));
799 }
800 patterns.push_back(pat);
801 }
802 response.set("patterns", patterns);
803 }
804 run_results.clear(); // Free memory before serialization
805
806 // Return the response — the lambda wrapper will call sendAsyncResponse
807 return response;
808}
fl::CRGB leds[NUM_LEDS]
bool autoResearchSetExclusiveDriverByName(const char *name)
AutoResearch-style helper: set an exclusive driver by name.
void autoResearchChipsetTiming(fl::AutoResearchConfig &config, int &driver_total, int &driver_passed, uint32_t &out_show_duration_ms, fl::vector< fl::RunResult > *out_results, int num_runs_per_pattern)
void autoResearchChipsetTimingLegacy(fl::AutoResearchConfig &config, int &driver_total, int &driver_passed, uint32_t &out_show_duration_ms, fl::vector< fl::RunResult > *out_results, int num_runs_per_pattern)
int passed_tests
Definition SIMD.ino:75
int total_tests
Definition SIMD.ino:74
fl::shared_ptr< AutoResearchState > mState
const char * c_str() const FL_NOEXCEPT
void push_back(const json &value) FL_NOEXCEPT
Definition json.h:745
fl::optional< i64 > as_int() const FL_NOEXCEPT
Definition json.h:255
fl::optional< bool > as_bool() const FL_NOEXCEPT
Definition json.h:254
size_t size() const FL_NOEXCEPT
Definition json.h:633
bool contains(size_t idx) const FL_NOEXCEPT
Definition json.h:625
fl::optional< fl::string > as_string() const FL_NOEXCEPT
Definition json.h:282
void set(const fl::string &key, const json &value) FL_NOEXCEPT
Definition json.h:701
static json object() FL_NOEXCEPT
Definition json.h:692
static json array() FL_NOEXCEPT
Definition json.h:688
string str() const FL_NOEXCEPT
Definition strstream.h:43
fl::size size() const FL_NOEXCEPT
bool empty() const FL_NOEXCEPT
void clear() FL_NOEXCEPT
Definition vector.h:634
void push_back(const T &value) FL_NOEXCEPT
Definition vector.h:624
constexpr EOrder RGB
Definition eorder.h:17
fl::json measureTightTiming(const fl::string &driver_name, const fl::ChipsetTimingConfig &timing, const fl::vector< fl::ChannelConfig > &tx_configs, int iterations, uint32_t max_allowed_overhead_us, bool &out_passed)
constexpr remove_reference< T >::type && move(T &&t) FL_NOEXCEPT
Definition move.h:28
ClocklessEncoder
Identifies which encoder to use for clockless chipsets in the Channel API.
@ CLOCKLESS_ENCODER_WS2812
Default, no preamble (WS2812 and compatible)
fl::u32 uint32_t
Definition s16x16x4.h:219
fl::u32 millis()
Universal millisecond timer - returns milliseconds since system startup.
fl::enable_if<!fl::is_array< T >::value, unique_ptr< T > >::type make_unique(Args &&... args) FL_NOEXCEPT
Definition unique_ptr.h:261
constexpr ChipsetTimingConfig makeTimingConfig() FL_NOEXCEPT
Convert compile-time CHIPSET type to runtime timing config.
constexpr ClocklessEncoder encoder_for() FL_NOEXCEPT
Extract the encoder selector from a compile-time TIMING type.
@ RMT
RMT-based receiver (ESP32)
Definition rx.h:166
corkscrew_args args
Definition old.h:149
static SpiEncoder apa102(u32 clock_hz=6000000) FL_NOEXCEPT
Create APA102 encoder configuration.
Definition spi.h:44

References fl::SpiEncoder::apa102(), args, fl::json::array(), fl::json::as_bool(), fl::json::as_int(), fl::json::as_string(), autoResearchChipsetTiming(), autoResearchChipsetTimingLegacy(), autoResearchSetExclusiveDriverByName(), fl::basic_string::c_str(), fl::vector< T >::clear(), fl::CLOCKLESS_ENCODER_WS2812, fl::json::contains(), fl::vector_basic::empty(), fl::NamedTimingConfig::encoder, fl::encoder_for(), fl::json::is_array(), fl::json::is_bool(), fl::json::is_int(), fl::json::is_string(), leds, fl::make_unique(), fl::makeTimingConfig(), fl::move(), mState, fl::NamedTimingConfig::name, fl::json::object(), passed_tests, fl::json::push_back(), fl::vector< T >::push_back(), RGB, fl::RMT, fl::json::set(), fl::json::size(), fl::vector_basic::size(), fl::sstream::str(), fl::NamedTimingConfig::timing, and total_tests.

Referenced by registerFunctions().

+ Here is the call graph for this function:
+ Here is the caller graph for this function: