FastLED 3.10.8
Loading...
Searching...
No Matches
AutoResearchParlioStream.h
Go to the documentation of this file.
1
24
25#pragma once
26
27#include "FastLED.h"
30#include "fl/stl/int.h"
31#include "fl/stl/span.h"
32#include "fl/stl/singleton.h"
33#include "fl/stl/vector.h"
34
35#if defined(ESP32) && FASTLED_ESP32_HAS_PARLIO
36#include "platforms/esp/32/drivers/parlio/parlio_engine.h" // ok platform headers - AutoResearch is the canonical ESP32 PARLIO live-driver scratch sketch and needs the deep driver header for streaming diagnostics
37#endif
38
39#if defined(ARDUINO_ARCH_ESP32) || defined(ESP_PLATFORM)
40#include <Arduino.h> // micros()
41#endif
42
43namespace autoresearch {
44namespace parlio_stream {
45
46constexpr int kMaxIterations = 16; // result struct iter timing array fixed size
47constexpr int kMaxLanes = 16;
48constexpr int kMaxLeds = 256;
49
53
57
58inline bool isFastLedOutputPinValid(int pin) {
59 if (pin < 0 || pin >= 64) {
60 return false;
61 }
62#if defined(_FL_VALID_PIN_MASK)
63 return (_FL_VALID_PIN_MASK & (1ULL << pin)) != 0;
64#else
65 return true;
66#endif
67}
68
70 bool channels_ok; // true if all PARLIO channels created cleanly
71 bool completed; // true if every iteration completed within timeout
72 int base_tx_pin; // first PARLIO TX pin used
73 bool explicit_tx_pins; // true when caller supplied a non-contiguous pin map
74 int lanes; // PARLIO lane count tested
75 int leds_per_lane; // LEDs per lane
76 int tx_pins[kMaxLanes]; // effective pin per lane
77 int iterations; // number of show() iterations run
78 uint32_t per_iter_us[kMaxIterations]; // per-iteration show()+wait() total time
79 uint32_t per_iter_show_us[kMaxIterations]; // per-iter show() return time (pre-encode + submit)
80 uint32_t per_iter_wait_us[kMaxIterations]; // per-iter wait() time (TX completion)
81 uint32_t steady_avg_us; // average total of iters 1..N-1 (skips iter 0 setup)
82 uint32_t steady_avg_show_us; // average show() time of iters 1..N-1
83 uint32_t steady_avg_wait_us; // average wait() time of iters 1..N-1
84 uint32_t tx_done_count; // PARLIO txDone ISR calls from final metrics
85 uint32_t worker_isr_count; // PARLIO worker ISR calls from final metrics
86 uint32_t underrun_count; // ring-empty-with-bytes-pending events
87 uint32_t ring_count; // final PARLIO ring occupancy
88 uint32_t bytes_total; // final engine total source bytes
89 uint32_t bytes_transmitted; // final engine transmitted source bytes
90 int failed_iter; // index of first iter that exceeded timeout, or -1
91 uint32_t timeout_ms; // effective timeout passed in
92 bool ring_error; // true if PARLIO flagged ring accounting/buffer error
93 bool hardware_idle; // true if hardware went idle before stream completed
94};
95
105 int num_lanes,
106 int num_leds,
107 int iterations,
108 uint32_t timeout_ms,
109 const int* tx_pins = nullptr) {
110 ValidateResult r{};
111 r.channels_ok = false;
112 r.completed = false;
113 r.explicit_tx_pins = tx_pins != nullptr;
114 r.base_tx_pin = base_tx_pin;
115 r.lanes = num_lanes;
116 r.leds_per_lane = num_leds;
117 r.iterations = iterations;
118 r.timeout_ms = timeout_ms;
119 r.failed_iter = -1;
120 for (int lane = 0; lane < kMaxLanes; ++lane) {
121 r.tx_pins[lane] = -1;
122 }
123
124 if (iterations < 1) iterations = 1;
125 if (iterations > kMaxIterations) iterations = kMaxIterations;
126 r.iterations = iterations;
127 if (!tx_pins && base_tx_pin < 0) return r;
128 if (num_lanes < 1 || num_lanes > 16) return r;
129 if (num_leds < 1) return r;
130 if (tx_pins) {
131 base_tx_pin = tx_pins[0];
132 r.base_tx_pin = base_tx_pin;
133 for (int lane = 0; lane < num_lanes; ++lane) {
134 if (tx_pins[lane] < 0) return r;
135 }
136 }
137
138#if defined(ARDUINO_ARCH_ESP32) || defined(ESP_PLATFORM)
139 // Reuse a single static heap-resident LED buffer sized for the canonical
140 // worst case (16 lanes × 256 LEDs). Test callers must stay within these
141 // bounds. Static to avoid repeated allocation across RPC calls.
142 if (num_leds > kMaxLeds) return r;
144 for (int lane = 0; lane < num_lanes; ++lane) {
145 for (int i = 0; i < num_leds; ++i) {
146 leds[lane][i] = CRGB(0xF0, 0x0F, 0xAA); // Pattern A (mixed bits)
147 }
148 }
149
152
153 fl::ChannelOptions parlio_opts;
154 parlio_opts.mBus = fl::Bus::FLEX_IO;
155 parlio_opts.mBusWhich = 0;
156
158
160 for (int lane = 0; lane < num_lanes; ++lane) {
161 const int tx_pin = tx_pins ? tx_pins[lane] : (base_tx_pin + lane);
162 r.tx_pins[lane] = tx_pin;
163 if (!isFastLedOutputPinValid(tx_pin)) {
165 return r;
166 }
168 tx_pin,
169 timing,
170 fl::span<CRGB>(leds[lane], num_leds),
171 RGB,
172 parlio_opts);
173 auto ch = FastLED.add(cfg);
174 if (!ch) {
176 return r;
177 }
178 channels.push_back(ch);
179 }
180 r.channels_ok = true;
181
182 bool ok = true;
183 uint32_t steady_total = 0;
184 uint32_t steady_show_total = 0;
185 uint32_t steady_wait_total = 0;
186 for (int iter = 0; iter < iterations; ++iter) {
187 const uint32_t t0 = micros();
188 FastLED.show();
189 const uint32_t t_show = micros();
190 FastLED.wait(timeout_ms);
191 const uint32_t t1 = micros();
192 const uint32_t show_us = t_show - t0;
193 const uint32_t wait_us = t1 - t_show;
194 const uint32_t dt = t1 - t0;
195 r.per_iter_us[iter] = dt;
196 r.per_iter_show_us[iter] = show_us;
197 r.per_iter_wait_us[iter] = wait_us;
198 if (dt > timeout_ms * 1000u) {
199 r.failed_iter = iter;
200 ok = false;
201 break;
202 }
203 if (iter > 0) {
204 steady_total += dt;
205 steady_show_total += show_us;
206 steady_wait_total += wait_us;
207 }
208 }
209
211
212#if defined(ESP32) && FASTLED_ESP32_HAS_PARLIO
213 {
214 auto metrics = fl::detail::ParlioEngine::getInstance().getDebugMetrics();
215 r.tx_done_count = metrics.mTxDoneCount;
216 r.worker_isr_count = metrics.mWorkerIsrCount;
217 r.underrun_count = metrics.mUnderrunCount;
218 r.ring_count = metrics.mRingCount;
219 r.bytes_total = metrics.mBytesTotal;
220 r.bytes_transmitted = metrics.mBytesTransmitted;
221 r.ring_error = metrics.mRingError;
222 r.hardware_idle = metrics.mHardwareIdle;
223 }
224#endif
225
226 r.completed = ok;
227 if (ok && iterations > 1) {
228 r.steady_avg_us = steady_total / (iterations - 1);
229 r.steady_avg_show_us = steady_show_total / (iterations - 1);
230 r.steady_avg_wait_us = steady_wait_total / (iterations - 1);
231 } else if (ok) {
232 r.steady_avg_us = r.per_iter_us[0];
233 r.steady_avg_show_us = r.per_iter_show_us[0];
234 r.steady_avg_wait_us = r.per_iter_wait_us[0];
235 }
236#else
237 (void)base_tx_pin;
238 (void)timeout_ms;
239#endif
240 return r;
241}
242
243} // namespace parlio_stream
244} // namespace autoresearch
CRGB leds[1]
FL_DISABLE_WARNING_PUSH FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS CFastLED FastLED
Global LED strip management instance.
Runtime chipset timing configuration for clockless LED drivers.
static T & instance() FL_NO_EXCEPT
Definition singleton.h:81
void push_back(const T &value) FL_NO_EXCEPT
Definition vector.h:624
constexpr EOrder RGB
Definition eorder.h:17
@ CHANNELS
Remove all channels from controller list.
Definition FastLED.h:668
fl::CRGB CRGB
Definition crgb.h:25
Centralized LED chipset timing definitions with nanosecond precision.
ValidateResult validateParlioStreaming(int base_tx_pin, int num_lanes, int num_leds, int iterations, uint32_t timeout_ms, const int *tx_pins=nullptr)
Run the PARLIO streaming functional test.
constexpr ChipsetTimingConfig makeTimingConfig() FL_NO_EXCEPT
Convert compile-time CHIPSET type to runtime timing config.
@ FLEX_IO
Definition bus.h:36
fl::u32 uint32_t
Definition stdint.h:218
Configuration for a single LED channel.
Definition config.h:167
Optional channel configuration parameters All fields have sensible defaults and can be overridden as ...
Definition options.h:92
Runtime bit-period timing for a clockless chipset.