4#if FL_PLATFORM_HAS_LARGE_MEMORY
7#define DEBUG_PRINT(x) do {} while(0)
8#define DEBUG_PRINTLN(x) do {} while(0)
51 if (!
args.is_object()) {
53 response.set(
"error",
"InvalidArgs");
54 response.set(
"message",
"Expected {drivers: [{driver, laneSizes}, ...]}");
58 fl::json config =
args;
61 if (!config.
contains(
"drivers") || !config[
"drivers"].is_array()) {
63 response.set(
"error",
"MissingDrivers");
64 response.set(
"message",
"Required field 'drivers' (array of {driver, laneSizes}) missing");
68 fl::json drivers_json = config[
"drivers"];
69 if (drivers_json.
size() < 2) {
71 response.set(
"error",
"TooFewDrivers");
72 response.set(
"message",
"Parallel test requires at least 2 drivers");
77 fl::string pattern =
"MSB_LSB_A";
78 if (config.
contains(
"pattern") && config[
"pattern"].is_string()) {
79 pattern = config[
"pattern"].
as_string().value();
83 if (config.
contains(
"iterations") && config[
"iterations"].is_int()) {
84 iterations =
static_cast<int>(config[
"iterations"].
as_int().value());
85 if (iterations < 1) iterations = 1;
88 fl::string timing_name =
"WS2812B-V5";
89 if (config.
contains(
"timing") && config[
"timing"].is_string()) {
90 timing_name = config[
"timing"].
as_string().value();
94 fl::ChipsetTimingConfig resolved_timing;
96 if (timing_name ==
"UCS7604-800KHZ") {
103 fl::NamedTimingConfig timing_config(resolved_timing, timing_name.
c_str(), resolved_encoder);
108 fl::vector<int> lane_sizes;
111 fl::vector<DriverEntry> driver_entries;
113 int next_pin =
mState->pin_tx;
114 for (fl::size i = 0; i < drivers_json.
size(); i++) {
115 if (!drivers_json[i].is_object()) {
117 response.set(
"error",
"InvalidDriverEntry");
119 msg <<
"drivers[" << i <<
"] must be an object {driver, laneSizes}";
124 fl::json entry = drivers_json[i];
127 if (!entry.
contains(
"driver") || !entry[
"driver"].is_string()) {
129 response.set(
"error",
"MissingDriverName");
131 msg <<
"drivers[" << i <<
"] missing 'driver' (string) field";
135 fl::string driver_name = entry[
"driver"].
as_string().value();
138 bool driver_found =
false;
139 for (fl::size j = 0; j <
mState->drivers_available.size(); j++) {
140 if (
mState->drivers_available[j].name == driver_name) {
147 response.set(
"error",
"UnknownDriver");
149 msg <<
"Driver '" << driver_name.
c_str() <<
"' not available";
155 if (!entry.
contains(
"laneSizes") || !entry[
"laneSizes"].is_array()) {
157 response.set(
"error",
"MissingLaneSizes");
159 msg <<
"drivers[" << i <<
"] missing 'laneSizes' (array) field";
164 fl::json lane_sizes_json = entry[
"laneSizes"];
165 fl::vector<int> lane_sizes;
166 for (fl::size li = 0; li < lane_sizes_json.
size(); li++) {
167 if (!lane_sizes_json[li].is_int()) {
169 response.set(
"error",
"InvalidLaneSizeType");
172 int size =
static_cast<int>(lane_sizes_json[li].
as_int().value());
175 response.set(
"error",
"InvalidLaneSize");
182 int pin_tx = next_pin;
183 if (entry.
contains(
"pinTx") && entry[
"pinTx"].is_int()) {
184 pin_tx =
static_cast<int>(entry[
"pinTx"].
as_int().value());
188 de.name = driver_name;
189 de.lane_sizes = lane_sizes;
194 next_pin = pin_tx + (int)lane_sizes.
size();
202 for (fl::size i = 0; i <
mState->drivers_available.size(); i++) {
205 for (fl::size i = 0; i < driver_entries.
size(); i++) {
210 fl::vector<fl::unique_ptr<fl::vector<CRGB>>> all_led_arrays;
211 fl::vector<fl::ChannelPtr> all_channels;
213 for (fl::size di = 0; di < driver_entries.
size(); di++) {
214 const auto& de = driver_entries[di];
215 for (fl::size li = 0; li < de.lane_sizes.size(); li++) {
221 fl::ChannelOptions opts;
223 const fl::string& n = de.name;
229 else if (n ==
"LCD_RGB") { opts.mBus =
fl::Bus::FLEX_IO; opts.mBusWhich = 1; }
233 else if (n ==
"UART0") { opts.mBus =
fl::Bus::UART; opts.mBusWhich = 0; }
234 else if (n ==
"UART1") { opts.mBus =
fl::Bus::UART; opts.mBusWhich = 1; }
235 else if (n ==
"FLEX_IO") { opts.mBus =
fl::Bus::FLEX_IO; opts.mBusWhich = 1; }
246 fl::ChannelConfig channel_config(
248 timing_config.timing,
249 fl::span<CRGB>(
leds->data(),
leds->size()),
259 response.set(
"error",
"ChannelCreationFailed");
261 msg <<
"Failed to create channel for driver '" << de.name.c_str()
275 for (fl::size di = 0; di < driver_entries.
size(); di++) {
276 const auto& de = driver_entries[di];
277 for (fl::size li = 0; li < de.lane_sizes.size(); li++) {
278 auto&
leds = *all_led_arrays[array_idx];
279 for (
int led = 0; led < de.lane_sizes[li]; led++) {
292 bool show_success =
true;
294 for (
int iter = 0; iter < iterations; iter++) {
302 bool rx_validation_passed =
true;
303 bool rx_validation_attempted =
false;
304 fl::vector<fl::RunResult> run_results;
305 const bool is_rp_pio_pair = driver_entries.
size() == 2 &&
306 ((driver_entries[0].name ==
"PIO0" && driver_entries[1].name ==
"PIO1") ||
307 (driver_entries[0].name ==
"PIO1" && driver_entries[1].name ==
"PIO0"));
313 if (
mState->rx_channel && driver_entries.
size() > 0 && !is_rp_pio_pair) {
314 const auto& primary_driver = driver_entries[0];
317 if (primary_driver.pin_tx ==
mState->pin_tx) {
318 rx_validation_attempted =
true;
321 fl::vector<fl::ChannelConfig> tx_configs;
322 int led_array_offset = 0;
323 for (fl::size li = 0; li < primary_driver.lane_sizes.size(); li++) {
325 primary_driver.pin_tx + (
int)li,
326 timing_config.timing,
327 fl::span<CRGB>(all_led_arrays[led_array_offset + li]->data(),
328 all_led_arrays[led_array_offset + li]->size()),
333 fl::AutoResearchConfig autoresearch_config(
334 timing_config.timing,
337 primary_driver.name.c_str(),
340 primary_driver.lane_sizes[0],
342 timing_config.encoder
350 val_show_duration_ms, &run_results);
362 const int primary_tx_pin = driver_entries.
empty()
364 : driver_entries[0].pin_tx;
365 int capture_evidence_bytes = 0;
366 int capture_evidence_raw_edges = 0;
367 for (fl::size ri = 0; ri < run_results.
size(); ri++) {
368 const auto& rr = run_results[ri];
369 if (rr.capturedBytes > 0) {
370 capture_evidence_bytes += rr.capturedBytes;
372 if (rr.rawEdgesAfterWait > capture_evidence_raw_edges) {
373 capture_evidence_raw_edges = rr.rawEdgesAfterWait;
376 if (rx_validation_attempted) {
378 if (rx_validation_passed) {
385 response.set(
"passed", show_success && rx_validation_passed);
389 response.set(
"requestedTxPin",
static_cast<int64_t
>(primary_tx_pin));
390 response.set(
"requestedRxPin",
static_cast<int64_t
>(
mState->pin_rx));
391 response.set(
"actualTxPin",
static_cast<int64_t
>(primary_tx_pin));
392 response.set(
"actualRxPin",
static_cast<int64_t
>(
mState->pin_rx));
393 fl::string capture_backend =
mState->rx_channel
394 ?
mState->rx_channel->getEngineName()
395 : fl::string(
"none");
397 response.set(
"captureEvidenceExpected", rx_validation_attempted);
398 response.set(
"captureEvidenceBytes",
399 static_cast<int64_t
>(capture_evidence_bytes));
400 response.set(
"captureEvidenceRawEdges",
401 static_cast<int64_t
>(capture_evidence_raw_edges));
402 response.set(
"duration_ms",
static_cast<int64_t
>(duration_ms));
403 response.set(
"show_duration_us",
static_cast<int64_t
>(show_duration_us));
404 response.set(
"show_success", show_success);
405 response.set(
"concurrent_resource_test", is_rp_pio_pair);
406 response.set(
"iterations",
static_cast<int64_t
>(iterations));
407 response.set(
"rx_validation_attempted", rx_validation_attempted);
408 response.set(
"rx_validation_passed", rx_validation_passed);
412 for (fl::size i = 0; i < driver_entries.
size(); i++) {
414 drv.
set(
"driver", driver_entries[i].name.c_str());
415 drv.
set(
"pinTx",
static_cast<int64_t
>(driver_entries[i].pin_tx));
417 for (
int s : driver_entries[i].lane_sizes) {
418 sizes.
push_back(
static_cast<int64_t
>(s));
420 drv.
set(
"laneSizes", sizes);
421 drv.
set(
"laneCount",
static_cast<int64_t
>(driver_entries[i].lane_sizes.size()));
424 response.set(
"drivers", drivers_tested);
Full PARLIO encode bench for ESP32-P4 (post-byte-LUT, #2526 landed).
PARLIO ISR-streaming functional validation (#2548 deep-dive follow-up).
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)
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.
fl::net::ble — BLE GATT transport layer for JSON-RPC
SPI encoder configuration for clocked LED chipsets.
fl::json runParallelTestImpl(const fl::json &args)
fl::shared_ptr< AutoResearchState > mState
void setDriverEnabled(const char *name, bool enabled)
Enable or disable a channel driver by name at runtime.
void show(fl::u8 scale)
Update all our controllers with the current led colors, using the passed in brightness.
static void add(fl::ChannelPtr channel)
Add a Channel-based LED controller (from ChannelPtr)
void wait()
Wait for all channel bus transmissions to complete.
void clear(bool writeData=false)
Clear the leds, wiping the local array of data.
const char * c_str() const FL_NO_EXCEPT
size_t size() const FL_NO_EXCEPT
fl::optional< fl::string > as_string() 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
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
string str() const FL_NO_EXCEPT
fl::size size() const FL_NO_EXCEPT
bool empty() const FL_NO_EXCEPT
void push_back(const T &value) FL_NO_EXCEPT
Task executor — runs registered task runners and manages the run loop.
@ CHANNELS
Remove all channels from controller list.
Platform-abstracted heap memory query functions.
FastLED's Elegant JSON Library: fl::json
constexpr remove_reference< T >::type && move(T &&t) FL_NO_EXCEPT
ClocklessEncoder
Identifies which encoder to use for clockless chipsets in the Channel API.
@ CLOCKLESS_ENCODER_WS2812
Default, no preamble (WS2812 and compatible)
constexpr ChipsetTimingConfig makeTimingConfig() FL_NO_EXCEPT
Convert compile-time CHIPSET type to runtime timing config.
fl::enable_if<!fl::is_array< T >::value, unique_ptr< T > >::type make_unique(Args &&... args) FL_NO_EXCEPT
fl::u32 millis()
Universal millisecond timer - returns milliseconds since system startup.
fl::u32 micros()
Universal microsecond timer - returns microseconds since system startup.
constexpr ClocklessEncoder encoder_for() FL_NO_EXCEPT
Extract the encoder selector from a compile-time TIMING type.
@ RMT
RMT-based receiver (ESP32)
Promise-based fluent API for FastLED - standalone async primitives.
Umbrella header for SIMD subsystem.