9#if FL_PLATFORM_HAS_LARGE_MEMORY
12#define DEBUG_PRINT(x) do {} while(0)
13#define DEBUG_PRINTLN(x) do {} while(0)
47#if defined(FL_IS_ESP32)
48#include "platforms/esp/32/drivers/spi/spi_hw_base.h"
54#include "platforms/arm/teensy/teensy4_common/drivers/flexio/flexio_spi_mode.h"
58#include "platforms/arm/teensy/teensy4_common/drivers/objectfled/objectfled_spi_mode.h"
69#if defined(FL_IS_TEENSY_4X)
70struct FlexIoObjectFledState {
74FlexIoObjectFledState& flexIoObjectFledState() {
84 remote.bind(
"dualSpiHostLoopback", [
this](
const fl::json&
args) -> fl::json {
86#if !defined(FL_IS_ESP32) || !FASTLED_ESP32_HAS_SPI3
90 response.set(
"error",
"TwoSpiHostsUnavailable");
93 if (!
args.is_object() || !
args.contains(
"activeHost") ||
94 !
args[
"activeHost"].is_int()) {
96 response.set(
"error",
"ExpectedActiveHost");
99 const int active_host =
static_cast<int>(
args[
"activeHost"].as_int().value());
100 if (active_host != 2 && active_host != 3) {
102 response.set(
"error",
"InvalidActiveHost");
105 const bool dummy_first =
args.contains(
"dummyFirst") &&
106 args[
"dummyFirst"].is_bool() &&
args[
"dummyFirst"].as_bool().value();
107 constexpr int kLedCount = 100;
108 static bool already_run =
false;
111 response.set(
"error",
"ResetBoardBeforeNextOrder");
115 static CRGB active_leds[kLedCount];
116 static CRGB dummy_leds[kLedCount];
117 for (
int i = 0; i < kLedCount; ++i) {
118 active_leds[i] =
CRGB(
static_cast<uint8_t>(i * 19 + 1),
119 static_cast<uint8_t>(i * 13 + 2),
120 static_cast<uint8_t>(i * 7 + 3));
123 const auto register_active = [&]() {
125 active_leds, kLedCount, active_host == 2
128 const auto register_dummy = [&]() {
130 dummy_leds, kLedCount, active_host == 2
144 fl::RunResult diagnostics;
149 "SPI_UNIFIED", &diagnostics);
151 response.set(
"activeHost", active_host);
152 response.set(
"dummyFirst", dummy_first);
153 response.set(
"genericDecodedBytes",
static_cast<int>(captured));
158 fl::EdgeTime edge_samples[256];
159 const size_t edge_count =
mState->rx_channel->getRawEdgeTimes(
160 fl::span<fl::EdgeTime>(edge_samples, 256), 0);
161 response.set(
"rawEdges",
static_cast<int>(edge_count));
162 constexpr size_t kVerifiedLeds = 8;
163 uint8_t first_bytes[kVerifiedLeds * 4] = {};
164 size_t bit_count = 0;
165 for (
size_t i = 0; i < edge_count && bit_count <
sizeof(first_bytes) * 8; ++i) {
166 const size_t bits = (edge_samples[i].
ns + 208) / 416;
167 for (
size_t j = 0; j < bits && bit_count <
sizeof(first_bytes) * 8; ++j, ++bit_count) {
168 if (edge_samples[i].high) {
169 first_bytes[bit_count / 8] |=
170 static_cast<uint8_t>(1u << (7 - bit_count % 8));
174 int wire_mismatches = 0;
175 if (bit_count <
sizeof(first_bytes) * 8) {
176 wire_mismatches =
static_cast<int>(
sizeof(first_bytes) * 8 - bit_count);
178 for (
size_t i = 0; i < kVerifiedLeds; ++i) {
179 const size_t offset = i * 4;
180 wire_mismatches += first_bytes[
offset] != 0xFF;
181 wire_mismatches += first_bytes[
offset + 1] != active_leds[i].r;
182 wire_mismatches += first_bytes[
offset + 2] != active_leds[i].g;
183 wire_mismatches += first_bytes[
offset + 3] != active_leds[i].b;
186 response.set(
"verifiedLeds",
static_cast<int>(kVerifiedLeds));
187 response.set(
"wireBits",
static_cast<int>(bit_count));
188 response.set(
"wireMismatches", wire_mismatches);
189 response.set(
"first0", first_bytes[0]);
190 response.set(
"first1", first_bytes[1]);
191 response.set(
"first2", first_bytes[2]);
192 response.set(
"first3", first_bytes[3]);
193 response.set(
"first4", first_bytes[4]);
194 response.set(
"first5", first_bytes[5]);
195 response.set(
"first6", first_bytes[6]);
196 response.set(
"first7", first_bytes[7]);
197 if (edge_count >= 4) {
198 response.set(
"edge0Ns",
static_cast<int>(edge_samples[0].ns));
199 response.set(
"edge1Ns",
static_cast<int>(edge_samples[1].ns));
200 response.set(
"edge2Ns",
static_cast<int>(edge_samples[2].ns));
201 response.set(
"edge3Ns",
static_cast<int>(edge_samples[3].ns));
202 response.set(
"edge0High", edge_samples[0].high);
203 response.set(
"edge1High", edge_samples[1].high);
207 const bool native_driver_found =
static_cast<bool>(driver);
208 const bool native_driver_enabled = driver &&
210 response.set(
"nativeDriverFound", native_driver_found);
211 response.set(
"nativeDriverEnabled", native_driver_enabled);
212 response.set(
"success", primed && native_driver_enabled && wire_mismatches == 0);
235 remote.bind(
"flexioRxBenchmark", [](
const fl::json&
args) -> fl::json {
237#if !defined(FL_IS_TEENSY_4X)
240 response.set(
"error",
"PlatformNotSupported");
242 "flexioRxBenchmark is Teensy 4.x-only (FLEXIO1 capture).");
246 int frequency_hz = 1000;
247 int duration_ms = 100;
250 if (
args.is_array() &&
args.size() >= 1 &&
args[0].is_object()) {
251 fl::json cfg =
args[0];
252 if (cfg.
contains(
"frequency_hz") && cfg[
"frequency_hz"].is_int()) {
253 frequency_hz =
static_cast<int>(cfg[
"frequency_hz"].
as_int().value());
255 if (cfg.
contains(
"duration_ms") && cfg[
"duration_ms"].is_int()) {
256 duration_ms =
static_cast<int>(cfg[
"duration_ms"].
as_int().value());
258 if (cfg.
contains(
"tx_pin") && cfg[
"tx_pin"].is_int()) {
259 tx_pin =
static_cast<int>(cfg[
"tx_pin"].
as_int().value());
261 if (cfg.
contains(
"rx_pin") && cfg[
"rx_pin"].is_int()) {
262 rx_pin =
static_cast<int>(cfg[
"rx_pin"].
as_int().value());
265 if (frequency_hz < 1 || frequency_hz > 5000000 ||
266 duration_ms < 1 || duration_ms > 5000) {
268 response.set(
"error",
"InvalidArgs");
270 "frequency_hz in [1, 5_000_000]; duration_ms in [1, 5000].");
275 analogWriteFrequency(tx_pin, (
float)frequency_hz);
287 fl::u64 target = expected_edges + expected_edges / 2;
288 if (target < 1024ULL) target = 1024ULL;
289 if (target > 16384ULL) target = 16384ULL;
290 rx_cfg.edge_capacity = (
size_t)target;
291 rx_cfg.start_low =
false;
296 response.set(
"error",
"RxCreateFailed");
298 "Failed to create FlexIO RX channel on pin (no FLEXIO1 mapping).");
301 if (!rx_channel->begin(rx_cfg)) {
304 response.set(
"error",
"RxBeginFailed");
306 "RxChannel::begin() returned false (see device WARN log).");
311 rx_channel->wait((u32)duration_ms);
317 fl::vector<fl::EdgeTime> edges;
318 edges.
assign(rx_cfg.edge_capacity, fl::EdgeTime());
319 size_t edges_captured =
320 rx_channel->getRawEdgeTimes(fl::span<fl::EdgeTime>(edges), 0);
321 edges.
resize(edges_captured);
327 fl::u32 min_ns = 0xFFFFFFFFu;
330 for (
size_t i = 0; i + 1 < edges_captured; i += 2) {
331 const fl::u32 period_ns =
332 (fl::u32)edges[i].ns + (fl::u32)edges[i + 1].ns;
333 if (period_ns == 0)
continue;
336 if (period_ns < min_ns) min_ns = period_ns;
337 if (period_ns > max_ns) max_ns = period_ns;
341 fl::u32 sigma_ns = 0;
343 mean_ns = (fl::u32)(sum_ns / (
fl::u64)periods);
346 periods > 0 ? (sum_sq_ns / (
fl::u64)periods) - (mean64 * mean64)
354 if (min_ns == 0xFFFFFFFFu) min_ns = 0;
357 response.set(
"frequency_hz",
static_cast<int64_t
>(frequency_hz));
358 response.set(
"duration_ms",
static_cast<int64_t
>(duration_ms));
359 response.set(
"tx_pin",
static_cast<int64_t
>(tx_pin));
360 response.set(
"rx_pin",
static_cast<int64_t
>(rx_pin));
361 response.set(
"edges_captured",
static_cast<int64_t
>(edges_captured));
362 response.set(
"periods",
static_cast<int64_t
>(periods));
363 response.set(
"period_mean_ns",
static_cast<int64_t
>(mean_ns));
364 response.set(
"period_sigma_ns",
static_cast<int64_t
>(sigma_ns));
365 response.set(
"period_min_ns",
static_cast<int64_t
>(min_ns));
366 response.set(
"period_max_ns",
static_cast<int64_t
>(max_ns));
398 remote.bind(
"flexioObjectFledTest", [](
const fl::json&
args) -> fl::json {
400#if !defined(FL_IS_TEENSY_4X)
403 response.set(
"error",
"PlatformNotSupported");
405 "flexioObjectFledTest is Teensy 4.x-only (FLEXIO1 RX + "
406 "Teensy-core ObjectFLED driver).");
414 if (
args.is_array() &&
args.size() >= 1 &&
args[0].is_object()) {
415 fl::json cfg =
args[0];
416 if (cfg.
contains(
"test_case") && cfg[
"test_case"].is_int()) {
417 test_case =
static_cast<int>(cfg[
"test_case"].
as_int().value());
419 if (cfg.
contains(
"tx_pin") && cfg[
"tx_pin"].is_int()) {
420 tx_pin =
static_cast<int>(cfg[
"tx_pin"].
as_int().value());
422 if (cfg.
contains(
"rx_pin") && cfg[
"rx_pin"].is_int()) {
423 rx_pin =
static_cast<int>(cfg[
"rx_pin"].
as_int().value());
425 if (cfg.
contains(
"capture_ms") && cfg[
"capture_ms"].is_int()) {
426 capture_ms =
static_cast<int>(cfg[
"capture_ms"].
as_int().value());
429 if (test_case < 0 || test_case > 4 ||
430 capture_ms < 1 || capture_ms > 5000) {
432 response.set(
"error",
"InvalidArgs");
434 "test_case in [0,4]; capture_ms in [1, 5000].");
440 CRGB* leds_buf = flexIoObjectFledState().leds;
459 leds_buf[0] =
CRGB(0xFF, 0xFF, 0xFF);
463 for (
int i = 0; i < 100; ++i) {
465 : (i % 3 == 1) ?
CRGB::Green
472 fl::vector<u8> expected;
473 expected.
reserve((
size_t)num_leds * 3);
474 for (
int i = 0; i < num_leds; ++i) {
482 const size_t expected_edges = (
size_t)num_leds * 24u * 2u;
483 size_t edge_capacity = expected_edges + expected_edges / 4u + 64u;
484 if (edge_capacity > 16384u) edge_capacity = 16384u;
487 rx_cfg.edge_capacity = edge_capacity;
488 rx_cfg.start_low =
true;
490 if (!rx_channel || !rx_channel->begin(rx_cfg)) {
492 response.set(
"error",
"RxBeginFailed");
494 "Failed to bring up FlexIO RX on the requested pin "
495 "(check kFlexIo1Pins[] mapping).");
500 fl::ChannelOptions opts;
505 fl::NamedTimingConfig timing_cfg(resolved_timing,
"WS2812B-V5",
507 fl::ChannelConfig tx_cfg(
510 fl::span<CRGB>(leds_buf, (
size_t)num_leds),
516 response.set(
"error",
"TxAddFailed");
518 "FastLED.add() rejected the ObjectFLED ChannelConfig.");
531 const u32 ws2812_tx_us =
532 (u32)num_leds * 24u * 13u / 10u + 100u;
533 const u32 min_capture_ms = (ws2812_tx_us + 999u) / 1000u + 10u;
534 const u32 effective_capture_ms = ((u32)capture_ms > min_capture_ms)
537 rx_channel->wait(effective_capture_ms);
540 const fl::ChipsetTiming ws2812_timing =
542 fl::ChipsetTiming4Phase rx_timing =
544 fl::vector<u8> decoded;
547 rx_channel->decode(rx_timing, fl::span<u8>(decoded));
550 u32 decoded_bytes = 0u;
552 decoded_bytes = decode_result.value();
554 const size_t cmp_n = (decoded_bytes < expected.
size())
558 size_t mismatched = 0;
559 for (
size_t i = 0; i < cmp_n; ++i) {
560 if (decoded[i] == expected[i]) ++matched;
else ++mismatched;
562 if (decoded_bytes < expected.
size()) {
563 mismatched += expected.size() - decoded_bytes;
567 fl::vector<fl::EdgeTime> edges;
568 edges.
assign(edge_capacity, fl::EdgeTime());
569 const size_t edges_captured =
570 rx_channel->getRawEdgeTimes(fl::span<fl::EdgeTime>(edges), 0);
577 response.set(
"success", mismatched == 0 && decoded_bytes == expected.
size());
578 response.set(
"test_case",
static_cast<int64_t
>(test_case));
579 response.set(
"tx_pin",
static_cast<int64_t
>(tx_pin));
580 response.set(
"rx_pin",
static_cast<int64_t
>(rx_pin));
581 response.set(
"num_leds",
static_cast<int64_t
>(num_leds));
582 response.set(
"expected_bytes",
static_cast<int64_t
>(expected.
size()));
583 response.set(
"decoded_bytes",
static_cast<int64_t
>(decoded_bytes));
584 response.set(
"matched",
static_cast<int64_t
>(matched));
585 response.set(
"mismatched",
static_cast<int64_t
>(mismatched));
586 response.set(
"edges_captured",
static_cast<int64_t
>(edges_captured));
615 remote.bind(
"flexioRxLoopbackPing", [](
const fl::json&
args) -> fl::json {
617#if !defined(FL_IS_TEENSY_4X)
620 response.set(
"error",
"PlatformNotSupported");
622 "flexioRxLoopbackPing is Teensy 4.x-only (FLEXIO1 RX).");
629 if (
args.is_array() &&
args.size() >= 1 &&
args[0].is_object()) {
630 const fl::json &cfg =
args[0];
631 if (cfg.
contains(
"tx_pin") && cfg[
"tx_pin"].is_int()) {
632 tx_pin =
static_cast<int>(cfg[
"tx_pin"].
as_int().value());
634 if (cfg.
contains(
"rx_pin") && cfg[
"rx_pin"].is_int()) {
635 rx_pin =
static_cast<int>(cfg[
"rx_pin"].
as_int().value());
637 if (cfg.
contains(
"toggle_us") && cfg[
"toggle_us"].is_int()) {
638 toggle_us =
static_cast<int>(cfg[
"toggle_us"].
as_int().value());
640 if (cfg.
contains(
"edges") && cfg[
"edges"].is_int()) {
641 edges =
static_cast<int>(cfg[
"edges"].
as_int().value());
644 if (edges < 2 || edges > 64) {
646 response.set(
"error",
"InvalidEdges");
647 response.set(
"message",
"edges must be in [2, 64]");
653 rx_cfg.edge_capacity = 1024;
654 rx_cfg.start_low =
true;
656 if (!rx_channel || !rx_channel->begin(rx_cfg)) {
658 response.set(
"error",
"RxBeginFailed");
660 "FlexIO RX begin() failed (kFlexIo1Pins[] mapping?).");
672 for (
int i = 0; i < edges; ++i) {
683 constexpr uintptr_t kFLEXIO1_BASE_DIAG = 0x401AC000u;
685 volatile uint32_t *shftstat = (
volatile uint32_t *)(kFLEXIO1_BASE_DIAG + 0x010);
686 volatile uint32_t *timstat = (
volatile uint32_t *)(kFLEXIO1_BASE_DIAG + 0x018);
687 volatile uint32_t *shftbuf = (
volatile uint32_t *)(kFLEXIO1_BASE_DIAG + 0x200);
688 volatile uint32_t *shftbufbis = (
volatile uint32_t *)(kFLEXIO1_BASE_DIAG + 0x280);
689 volatile uint32_t *shftbufbys = (
volatile uint32_t *)(kFLEXIO1_BASE_DIAG + 0x300);
691 flexio_diag.
set(
"pin",
static_cast<int64_t
>(*pin));
692 flexio_diag.
set(
"shiftstat",
static_cast<int64_t
>(*shftstat));
693 flexio_diag.
set(
"timstat",
static_cast<int64_t
>(*timstat));
694 flexio_diag.
set(
"shiftbuf0",
static_cast<int64_t
>(*shftbuf));
695 flexio_diag.
set(
"shiftbufbis0",
static_cast<int64_t
>(*shftbufbis));
696 flexio_diag.
set(
"shiftbufbys0",
static_cast<int64_t
>(*shftbufbys));
697 response.set(
"flexio1", flexio_diag);
704 rx_channel->wait(50);
707 fl::vector<fl::EdgeTime> captured_edges;
708 captured_edges.
assign(64, fl::EdgeTime());
710 rx_channel->getRawEdgeTimes(fl::span<fl::EdgeTime>(captured_edges), 0);
716 response.set(
"tx_pin",
static_cast<int64_t
>(tx_pin));
717 response.set(
"rx_pin",
static_cast<int64_t
>(rx_pin));
718 response.set(
"toggle_us",
static_cast<int64_t
>(toggle_us));
719 response.set(
"edges",
static_cast<int64_t
>(edges));
720 response.set(
"edges_captured",
static_cast<int64_t
>(edge_count));
726 const size_t to_dump = (edge_count < 8u) ? edge_count : 8u;
727 for (
size_t i = 0; i < to_dump; ++i) {
729 e.
set(
"high", captured_edges[i].high);
730 e.
set(
"ns",
static_cast<int64_t
>(captured_edges[i].ns));
733 response.set(
"first_edges", edges_arr);
738 remote.bind(
"legacyAsyncProbe", [](
const fl::json&
args) -> fl::json {
741 remote.bind(
"legacyAsyncErrorProbe", [](
const fl::json&
args) -> fl::json {
753 remote.bind(
"timingDriftTest", [](
const fl::json&
args) -> fl::json {
761 if (
args.is_object()) {
763 }
else if (
args.is_array() &&
args.size() >= 1 &&
args[0].is_object()) {
767 if (config.
contains(
"pin") && config[
"pin"].is_int()) {
768 pin =
static_cast<int>(config[
"pin"].
as_int().value());
770 if (config.
contains(
"numLeds") && config[
"numLeds"].is_int()) {
771 num_leds =
static_cast<int>(config[
"numLeds"].
as_int().value());
773 if (config.
contains(
"iterations") && config[
"iterations"].is_int()) {
774 iterations =
static_cast<int>(config[
"iterations"].
as_int().value());
780 response.set(
"success", r.valid_pin);
782 response.set(
"error",
"InvalidPin");
784 "pin is outside LegacyClocklessProxy range");
789 response.set(
"num_leds", r.num_leds);
790 response.set(
"iterations", r.iterations);
791 response.set(
"cpu_mhz",
static_cast<int64_t
>(r.cpu_mhz));
792 response.set(
"show_count",
static_cast<int64_t
>(r.show_count));
793 response.set(
"show_min_us",
static_cast<int64_t
>(r.show_min_us));
794 response.set(
"show_max_us",
static_cast<int64_t
>(r.show_max_us));
795 response.set(
"show_total_us",
static_cast<int64_t
>(r.show_total_us));
798 for (
int i = 0; i < r.iterations; ++i) {
799 iter_ms_arr.
push_back(
static_cast<int64_t
>(r.iter_ms[i]));
801 response.set(
"iter_ms", iter_ms_arr);
830 remote.bind(
"flexioSpiSelfTest", [](
const fl::json&
args) -> fl::json {
832#if !defined(FL_IS_TEENSY_4X)
835 response.set(
"error",
"PlatformNotSupported");
837 "flexioSpiSelfTest is Teensy 4.x-only (FlexIO2 SPI master mode).");
849 int clock_hz = 1000000;
851 if (
args.is_object()) {
852 const fl::json& cfg =
args;
853 if (cfg.
contains(
"mosi_pin") && cfg[
"mosi_pin"].is_int()) {
854 mosi_pin =
static_cast<int>(cfg[
"mosi_pin"].
as_int().value());
856 if (cfg.
contains(
"sclk_pin") && cfg[
"sclk_pin"].is_int()) {
857 sclk_pin =
static_cast<int>(cfg[
"sclk_pin"].
as_int().value());
859 if (cfg.
contains(
"clock_hz") && cfg[
"clock_hz"].is_int()) {
860 clock_hz =
static_cast<int>(cfg[
"clock_hz"].
as_int().value());
862 if (cfg.
contains(
"num_bytes") && cfg[
"num_bytes"].is_int()) {
863 num_bytes =
static_cast<int>(cfg[
"num_bytes"].
as_int().value());
869 response.set(
"clock_hz",
static_cast<int64_t
>(clock_hz));
870 response.set(
"num_bytes", num_bytes);
877 if (num_bytes <= 0 || num_bytes > 64) {
879 response.set(
"error",
"InvalidArgs");
880 response.set(
"message",
"num_bytes must be in [1, 64].");
883 if (mosi_pin < 0 || mosi_pin > 255 ||
884 sclk_pin < 0 || sclk_pin > 255) {
886 response.set(
"error",
"InvalidArgs");
887 response.set(
"message",
"mosi_pin / sclk_pin must be in [0, 255].");
892 response.set(
"error",
"InvalidArgs");
893 response.set(
"message",
"clock_hz must be > 0.");
898 fl::FlexIOSPIPinInfo pin_info{};
899 bool lookup_ok = fl::flexio_spi_lookup_pins(
900 static_cast<fl::u8>(mosi_pin),
901 static_cast<fl::u8>(sclk_pin),
903 response.set(
"lookup_ok", lookup_ok);
906 response.set(
"error",
"PinNotRoutable");
908 "(MOSI, SCLK) pair is not FlexIO2-routable; both pins "
909 "must be in {6, 7, 8, 9, 10, 11, 12, 13, 32}.");
913 static_cast<int64_t
>(pin_info.mosi_flexio_pin));
915 static_cast<int64_t
>(pin_info.sclk_flexio_pin));
918 bool init_ok = fl::flexio_spi_init(pin_info,
919 static_cast<fl::u32
>(clock_hz));
923 response.set(
"error",
"InitFailed");
924 response.set(
"message",
"flexio_spi_init returned false.");
932 for (
int i = 0; i < num_bytes; ++i) {
934 case 0: buffer[i] = 0xA5;
break;
935 case 1: buffer[i] = 0x5A;
break;
936 case 2: buffer[i] = 0xFF;
break;
937 case 3: buffer[i] = 0x00;
break;
941 const fl::u32 show_start_ms =
millis();
942 bool show_ok = fl::flexio_spi_show(buffer,
943 static_cast<fl::u32
>(num_bytes));
946 fl::flexio_spi_deinit();
948 response.set(
"error",
"ShowFailed");
949 response.set(
"message",
"flexio_spi_show returned false.");
954 fl::flexio_spi_wait();
955 const fl::u32 wait_ms =
millis() - show_start_ms;
956 response.set(
"wait_ms",
static_cast<int64_t
>(wait_ms));
959 fl::flexio_spi_deinit();
984 remote.bind(
"objectfledSpiSelfTest", [](
const fl::json&
args) -> fl::json {
986#if !defined(FL_IS_TEENSY_4X)
989 response.set(
"error",
"PlatformNotSupported");
991 "objectfledSpiSelfTest is Teensy 4.x-only (FlexPWM2 + eDMA + GPIO6).");
999 int clock_hz = 1000000;
1001 if (
args.is_object()) {
1002 const fl::json& cfg =
args;
1003 if (cfg.
contains(
"mosi_pin") && cfg[
"mosi_pin"].is_int()) {
1004 mosi_pin =
static_cast<int>(cfg[
"mosi_pin"].
as_int().value());
1006 if (cfg.
contains(
"sclk_pin") && cfg[
"sclk_pin"].is_int()) {
1007 sclk_pin =
static_cast<int>(cfg[
"sclk_pin"].
as_int().value());
1009 if (cfg.
contains(
"clock_hz") && cfg[
"clock_hz"].is_int()) {
1010 clock_hz =
static_cast<int>(cfg[
"clock_hz"].
as_int().value());
1012 if (cfg.
contains(
"num_bytes") && cfg[
"num_bytes"].is_int()) {
1013 num_bytes =
static_cast<int>(cfg[
"num_bytes"].
as_int().value());
1017 response.set(
"mosi_pin", mosi_pin);
1018 response.set(
"sclk_pin", sclk_pin);
1019 response.set(
"clock_hz",
static_cast<int64_t
>(clock_hz));
1020 response.set(
"num_bytes", num_bytes);
1023 if (num_bytes <= 0 || num_bytes > 64) {
1025 response.set(
"error",
"InvalidArgs");
1026 response.set(
"message",
"num_bytes must be in [1, 64].");
1029 if (mosi_pin < 0 || mosi_pin > 255 ||
1030 sclk_pin < 0 || sclk_pin > 255) {
1032 response.set(
"error",
"InvalidArgs");
1033 response.set(
"message",
"mosi_pin / sclk_pin must be in [0, 255].");
1036 if (clock_hz <= 0) {
1038 response.set(
"error",
"InvalidArgs");
1039 response.set(
"message",
"clock_hz must be > 0.");
1044 fl::ObjectFLEDSPIPinInfo pin_info{};
1045 bool lookup_ok = fl::objectfled_spi_lookup_pins(
1046 static_cast<fl::u8>(mosi_pin),
1047 static_cast<fl::u8>(sclk_pin),
1049 response.set(
"lookup_ok", lookup_ok);
1052 response.set(
"error",
"PinNotRoutable");
1054 "(MOSI, SCLK) pair is not on GPIO6 or otherwise invalid; "
1055 "both pins must be GPIO6-resident (T4.x: 0, 1, 14-27, "
1056 "T4.1 adds 38-41).");
1059 response.set(
"mosi_bit",
static_cast<int64_t
>(pin_info.mosi_bit));
1060 response.set(
"sclk_bit",
static_cast<int64_t
>(pin_info.sclk_bit));
1063 bool init_ok = fl::objectfled_spi_init(pin_info,
1064 static_cast<fl::u32
>(clock_hz));
1068 response.set(
"error",
"InitFailed");
1069 response.set(
"message",
"objectfled_spi_init returned false.");
1075 for (
int i = 0; i < num_bytes; ++i) {
1077 case 0: buffer[i] = 0xA5;
break;
1078 case 1: buffer[i] = 0x5A;
break;
1079 case 2: buffer[i] = 0xFF;
break;
1080 case 3: buffer[i] = 0x00;
break;
1084 const fl::u32 show_start_ms =
millis();
1085 bool show_ok = fl::objectfled_spi_show(
1086 fl::span<const fl::u8>(buffer,
1087 static_cast<fl::size
>(num_bytes)));
1090 fl::objectfled_spi_deinit();
1092 response.set(
"error",
"ShowFailed");
1093 response.set(
"message",
"objectfled_spi_show returned false.");
1098 fl::objectfled_spi_wait();
1099 const fl::u32 wait_ms =
millis() - show_start_ms;
1100 response.set(
"wait_ms",
static_cast<int64_t
>(wait_ms));
1103 fl::ObjectFLEDSPIDiagnostics diag{};
1104 fl::objectfled_spi_read_diagnostics(&diag);
1105 response.set(
"dma_channel",
static_cast<int64_t
>(diag.dma_channel));
1106 response.set(
"dma_erq",
static_cast<int64_t
>(diag.dma_erq));
1107 response.set(
"dma_int",
static_cast<int64_t
>(diag.dma_int));
1108 response.set(
"dma_err",
static_cast<int64_t
>(diag.dma_err));
1109 response.set(
"dma_es",
static_cast<int64_t
>(diag.dma_es));
1110 response.set(
"dma_citer",
static_cast<int64_t
>(diag.dma_citer));
1111 response.set(
"dma_biter",
static_cast<int64_t
>(diag.dma_biter));
1112 response.set(
"tmr3_cntr0",
static_cast<int64_t
>(diag.tmr3_cntr0));
1113 response.set(
"tmr3_csctrl0",
static_cast<int64_t
>(diag.tmr3_csctrl0));
1114 response.set(
"tmr3_sctrl0",
static_cast<int64_t
>(diag.tmr3_sctrl0));
1115 response.set(
"tmr3_ctrl0",
static_cast<int64_t
>(diag.tmr3_ctrl0));
1116 response.set(
"tmr3_enbl",
static_cast<int64_t
>(diag.tmr3_enbl));
1117 response.set(
"xbar1_ctrl1",
static_cast<int64_t
>(diag.xbar1_ctrl1));
1120 fl::objectfled_spi_deinit();
Full PARLIO encode bench for ESP32-P4 (post-byte-LUT, #2526 landed).
PARLIO ISR-streaming functional validation (#2548 deep-dive follow-up).
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)
Issue #2994 repro: compounded per-sequence timing drift vs 3.10.3.
Wave8 expansion micro-benchmark for #2526.
FL_DISABLE_WARNING_PUSH FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS CFastLED FastLED
Global LED strip management instance.
@ CHANNELS
Remove all channels from controller list.
@ APA102
APA102 LED chipset.
fl::unique_ptr< fl::Remote > remote
fl::net::ble — BLE GATT transport layer for JSON-RPC
SPI encoder configuration for clocked LED chipsets.
fl::shared_ptr< AutoResearchState > mState
void bindDriverMethods(fl::Remote &remote)
void clear(bool writeData=false) FL_NO_EXCEPT
Clear the leds, wiping the local array of data.
static void add(fl::ChannelPtr channel) FL_NO_EXCEPT
Add a Channel-based LED controller (from ChannelPtr)
void show(fl::u8 scale) FL_NO_EXCEPT
Update all our controllers with the current led colors, using the passed in brightness.
void setBrightness(fl::u8 scale) FL_NO_EXCEPT
Set the global brightness scaling.
void wait() FL_NO_EXCEPT
Wait for all channel bus transmissions to complete.
::CLEDController & addLeds(::CLEDController *pLed, CRGB *data, int nLedsOrOffset, int nLedsIfOffset=0) FL_NO_EXCEPT
Add a CLEDController instance to the world.
static ChannelManager & registry() FL_NO_EXCEPT
Get the singleton WITHOUT registering platform default drivers.
fl::shared_ptr< IChannelDriver > findDriverByName(const fl::string &name) const FL_NO_EXCEPT
Silent counterpart to getDriverByName().
bool isDriverEnabled(const char *name) const FL_NO_EXCEPT
Check if a driver is enabled by name.
JSON-RPC server with scheduling support.
static RxChannelPtr create(const RxChannelConfig &config) FL_NO_EXCEPT
static T & instance() FL_NO_EXCEPT
const char * c_str() const FL_NO_EXCEPT
fl::optional< i64 > as_int() const FL_NO_EXCEPT
void set(const fl::string &key, const json &value) FL_NO_EXCEPT
bool is_null() const FL_NO_EXCEPT
void push_back(const json &value) FL_NO_EXCEPT
bool contains(size_t idx) const FL_NO_EXCEPT
static json object() FL_NO_EXCEPT
static json array() FL_NO_EXCEPT
static string from_literal(const char *literal) 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 assign(InputIt first, InputIt last) FL_NO_EXCEPT
fl::UISlider offset("Offset", 0.0f, 0.0f, 1.0f, 0.01f)
Task executor — runs registered task runners and manages the run loop.
Platform-abstracted heap memory query functions.
FastLED's Elegant JSON Library: fl::json
DriftResult run(int pin, int num_leds, int iterations)
fl::json legacyAsyncProbe(const fl::json &args)
Call after the regular driver test has registered the selected backend.
fl::json legacyAsyncErrorProbe(const fl::json &args)
constexpr ChipsetTiming to_runtime_timing() FL_NO_EXCEPT
Convert enum-based timing type to runtime ChipsetTiming struct.
constexpr ChipsetTimingConfig makeTimingConfig() FL_NO_EXCEPT
Convert compile-time CHIPSET type to runtime timing config.
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.
@ FLEXIO
Teensy 4.x-only FlexIO capture backend (FLEXIO1 — FLEXIO2 is owned by the WS2812 TX driver)....
void analogWrite(int pin, u16 val) FL_NO_EXCEPT
Write analog value to pin (PWM)
constexpr ClocklessEncoder encoder_for() FL_NO_EXCEPT
Extract the encoder selector from a compile-time TIMING type.
void digitalWrite(int pin, PinValue val) FL_NO_EXCEPT
Write digital value to pin.
void pinMode(int pin, PinMode mode) FL_NO_EXCEPT
Set pin mode (input, output, pull-up, pull-down)
void delayMicroseconds(u32 us) FL_NO_EXCEPT
Delay for a given number of microseconds.
Promise-based fluent API for FastLED - standalone async primitives.
Umbrella header for SIMD subsystem.
@ 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>
@ Black
<div style='background:#000000;width:4em;height:4em;'></div>
u32 ns
Duration in nanoseconds (31 bits, max ~2.1s)
int captureWaitResult
RxWaitResult value, or -1 before wait.
int rawEdgesAfterWait
Raw edges visible after RX wait/decode.
fl::string rxBeginError
Machine-readable reason when RX begin() fails.