82#include "platforms/esp/is_esp.h"
84#if defined(FL_IS_ESP8266)
92#include "platforms/esp/8266/fastled_esp8266_uart.h"
95#include <esp8266_peri.h>
96#include <user_interface.h>
97#include "platforms/esp/8266/i2s_ws2812_codec.h"
98#include "platforms/esp/8266/spi_hspi_clock.h"
99#include <core_esp8266_i2s.h>
106constexpr int kMaxLoopbackLeds = 10;
107constexpr int kMaxEdges = kMaxLoopbackLeds * 24 * 2 + 4;
110__attribute__((always_inline))
inline fl::u32 cycles() {
112 __asm__ __volatile__(
"rsr %0,ccount" :
"=a"(c));
116inline fl::u8 patternByte(
int i) {
117 static const fl::u8 kSeed[] = {0x00, 0xFF, 0xAA, 0x55, 0x0F, 0xF0};
118 if (i <
static_cast<int>(
sizeof(kSeed))) {
121 return static_cast<fl::u8>(i * 37 + 0x5A);
125 fl::u32 edges[kMaxEdges];
130inline EdgeCapture& edgeCapture() {
143 const fl::u8* encoded,
int nEncoded, fl::u32 mask, fl::u32 idleCycles,
145 bool level = (GPI & mask) != 0;
146 cap.firstLevel = level;
147 USF(fl::esp8266_uart::kUart) = encoded[0];
148 USF(fl::esp8266_uart::kUart) = encoded[1];
150 fl::u32 lastEdge = cycles();
151 while (cap.count < kMaxEdges) {
152 const bool now = (GPI & mask) != 0;
153 const fl::u32
t = cycles();
155 cap.edges[cap.count++] =
t;
158 if (!now && fed < nEncoded) {
159 USF(fl::esp8266_uart::kUart) = encoded[fed++];
161 }
else if (
t - lastEdge > idleCycles) {
167inline bool isUsableRxPin(
int pin) {
169 return pin == 0 || pin == 4 || pin == 5 || (pin >= 12 && pin <= 15);
172inline fl::json uartWs2812Loopback(
int nLeds,
int rxPin) {
173 if (!isUsableRxPin(rxPin)) {
175 r.
set(
"success",
false);
176 r.
set(
"error",
"rxPin must be one of 0,4,5,12,13,14,15");
179 if (nLeds < 1) nLeds = 1;
180 if (nLeds > kMaxLoopbackLeds) nLeds = kMaxLoopbackLeds;
181 const int nBytes = nLeds * 3;
183 fl::u8 encoded[kMaxLoopbackLeds * 12];
184 for (
int i = 0; i < nBytes; ++i) {
185 fl::esp8266_uart::encodeByte(patternByte(i), &encoded[i * 4]);
189 fl::esp8266_uart::begin(FASTLED_ESP8266_UART_BAUD);
192 const fl::u8 prevMhz = system_get_cpu_freq();
193 system_update_cpu_freq(160);
194 const fl::u32 cyclesPerUs = 160;
195 const fl::u32 idleCycles = 20 * cyclesPerUs;
197 EdgeCapture& cap = edgeCapture();
199 const fl::u32 mask = 1u << rxPin;
201 const fl::u32 savedPs = xt_rsil(15);
202 captureEdges(encoded, nLeds * 12, mask, idleCycles, cap);
204 system_update_cpu_freq(prevMhz);
207 fl::u32 t0hMin = 0xFFFFFFFF, t0hMax = 0, t1hMin = 0xFFFFFFFF, t1hMax = 0;
208 fl::u32 periodMin = 0xFFFFFFFF, periodMax = 0;
209 const fl::u32 splitCycles = (625 * cyclesPerUs) / 1000;
210 fl::u8 decoded[kMaxLoopbackLeds * 3] = {};
212 const int start = cap.firstLevel ? 1 : 0;
213 for (
int e = start; e + 1 < cap.count && bits < nBytes * 8; e += 2) {
214 const fl::u32 high = cap.edges[e + 1] - cap.edges[e];
215 const bool one = high > splitCycles;
217 decoded[bits / 8] |=
static_cast<fl::u8>(0x80 >> (bits % 8));
218 if (high < t1hMin) t1hMin = high;
219 if (high > t1hMax) t1hMax = high;
221 if (high < t0hMin) t0hMin = high;
222 if (high > t0hMax) t0hMax = high;
224 if (e + 2 < cap.count) {
225 const fl::u32 period = cap.edges[e + 2] - cap.edges[e];
226 if (period < periodMin) periodMin = period;
227 if (period > periodMax) periodMax = period;
234 for (
int i = 0; i < nBytes; ++i) {
235 if (decoded[i] != patternByte(i)) {
237 if (firstBad < 0) firstBad = i;
241 auto ns = [cyclesPerUs](fl::u32 c) ->
fl::i64 {
242 return c == 0xFFFFFFFF ? -1 :
static_cast<fl::i64>(c) * 1000 / cyclesPerUs;
245 r.
set(
"success", bits == nBytes * 8 && byteErrors == 0);
248 r.
set(
"edges",
static_cast<fl::i64>(cap.count));
250 r.
set(
"byte_errors",
static_cast<fl::i64>(byteErrors));
251 r.
set(
"first_bad_byte",
static_cast<fl::i64>(firstBad));
253 r.
set(
"expected",
static_cast<fl::i64>(patternByte(firstBad)));
254 r.
set(
"actual",
static_cast<fl::i64>(decoded[firstBad]));
256 r.
set(
"idle_low_before", !cap.firstLevel);
257 r.
set(
"t0h_ns_min", ns(t0hMin));
258 r.
set(
"t0h_ns_max", ns(t0hMax));
259 r.
set(
"t1h_ns_min", ns(t1hMin));
260 r.
set(
"t1h_ns_max", ns(t1hMax));
261 r.
set(
"period_ns_min", ns(periodMin));
262 r.
set(
"period_ns_max", ns(periodMax));
275inline ShowState& showState() {
279inline fl::json uartWs2812Show(
int nLeds) {
280 if (nLeds < 1) nLeds = 1;
281 if (nLeds > 64) nLeds = 64;
282 ShowState& st = showState();
288 st.controller.setLeds(fl::span<CRGB>(
leds, nLeds));
289 for (
int i = 0; i < nLeds; ++i) {
290 leds[i] =
CRGB(patternByte(i * 3), patternByte(i * 3 + 1), patternByte(i * 3 + 2));
292 const fl::u32 t0 =
micros();
293 st.controller.showLeds(255);
294 const fl::u32 elapsed =
micros() - t0;
296 r.
set(
"success",
true);
298 r.
set(
"show_us",
static_cast<fl::i64>(elapsed));
300 r.
set(
"expected_min_us",
static_cast<fl::i64>(nLeds * 30 + FASTLED_ESP8266_UART_RESET_US));
304constexpr int kClocklessPin = FL_PIN_CLOCKLESS_1;
310constexpr int kApaDataPin = FL_PIN_SPI_DATA_1;
311constexpr int kApaClockPin = FL_PIN_SPI_CLOCK_1;
312constexpr int kApaDataRealPin = fl::FastPin<FL_PIN_SPI_DATA_1>::kRealPin;
313constexpr int kApaClockRealPin = fl::FastPin<FL_PIN_SPI_CLOCK_1>::kRealPin;
315struct DriverShowState {
321inline DriverShowState& driverShowState() {
334 DriverShowState& st = driverShowState();
335 if (nLeds < 1) nLeds = 1;
336 if (nLeds > 64) nLeds = 64;
337 if (frames < 1) frames = 1;
338 if (frames > 200) frames = 200;
340 fl::u32 minUs = 0xFFFFFFFF, maxUs = 0;
343 int firstTruncated = -1;
344 for (
int f = 0; f < frames; ++f) {
345 for (
int i = 0; i < nLeds; ++i) {
346 st.leds[i] =
CHSV(
static_cast<fl::u8>(f * 7 + i * 13), 255, 255);
348 const fl::u32 t0 =
micros();
350 const fl::u32 us =
micros() - t0;
351 if (us < minUs) minUs = us;
352 if (us > maxUs) maxUs = us;
353 if (
static_cast<fl::i64>(us) < floorUs) {
354 if (firstTruncated < 0) firstTruncated = f;
358 if (minUsOut !=
nullptr) {
359 *minUsOut =
static_cast<fl::i64>(minUs);
362 r.
set(
"success", truncated == 0);
363 r.
set(
"truncated",
static_cast<fl::i64>(truncated));
364 r.
set(
"first_truncated_frame",
static_cast<fl::i64>(firstTruncated));
368 r.
set(
"wifi_opmode",
static_cast<fl::i64>(wifi_get_opmode()));
370 r.
set(
"frames",
static_cast<fl::i64>(frames));
373 r.
set(
"floor_us", floorUs);
377inline fl::json clocklessShow(
int nLeds,
int frames) {
378 DriverShowState& st = driverShowState();
382 const int n = nLeds < 1 ? 1 : (nLeds > 64 ? 64 : nLeds);
383 return timeFrames(st.clockless, n, frames,
static_cast<fl::i64>(n) * 30);
388inline fl::i64 apa102WireBytes(
int nLeds) {
392 static_cast<fl::size
>(nLeds)));
406constexpr fl::i64 kApa102FrameSlackUs = 400;
413inline fl::u32 hspiAppliedHz() {
414 const fl::u32 reg = SPI1CLK;
415 if (reg & fl::esp8266::kHspiEquSysClk) {
416 return fl::esp8266::kHspiSourceHz;
418 const fl::u32 pre = ((reg >> 18) & 0x1FFFUL) + 1;
419 const fl::u32 n = ((reg >> 12) & 0x3FUL) + 1;
420 return fl::esp8266::kHspiSourceHz / (pre * n);
423inline fl::json apa102RateReport(fl::json r,
fl::i64 minUs,
int nLeds,
int mhz,
425 const fl::i64 bytes = apa102WireBytes(nLeds);
426 const fl::i64 bits = bytes * 8;
427 const fl::i64 expected = hz ? (bits * 1000000 + hz - 1) /
static_cast<fl::i64>(hz) : -1;
428 r.
set(
"success", expected > 0 && minUs >= expected &&
429 minUs <= expected + kApa102FrameSlackUs);
431 r.
set(
"requested_hz",
static_cast<fl::i64>(mhz) * 1000000);
432 r.
set(
"achievable_hz",
static_cast<fl::i64>(hz));
433 r.
set(
"wire_bytes_est", bytes);
434 r.
set(
"expected_us", expected);
439 const fl::u32 reg = SPI1CLK;
440 r.
set(
"reg_equ",
static_cast<fl::i64>((reg >> 31) & 1UL));
441 r.
set(
"gpmux_equ",
static_cast<fl::i64>((GPMUX >> 9) & 1UL));
446inline fl::json apa102Show(
int nLeds,
int frames) {
447 DriverShowState& st = driverShowState();
455 const fl::u32 hz = hspiAppliedHz();
456 const int n = nLeds < 1 ? 1 : (nLeds > 64 ? 64 : nLeds);
460 fl::json r = timeFrames(st.apa102, n, frames, 0, &minUs);
461 return apa102RateReport(r, minUs, n, 6, hz);
464inline fl::json hspiPinError(
const char* what) {
466 e.
set(
"success",
false);
467 e.
set(
"error", what);
477#if defined(FASTLED_ESP8266_RAW_PIN_ORDER)
478constexpr int kBitbangDataPin = 4;
479constexpr int kBitbangClockPin = 16;
482inline fl::json apa102ShowBitbang(
int nLeds,
int frames) {
483#if !defined(FASTLED_ESP8266_RAW_PIN_ORDER)
486 return hspiPinError(
"the bit-bang baseline is only defined in raw pin order");
488 DriverShowState& st = driverShowState();
496 const int n = nLeds < 1 ? 1 : (nLeds > 64 ? 64 : nLeds);
497 fl::json r = timeFrames(c, n, frames, 0);
498 r.
set(
"data_pin",
static_cast<fl::i64>(kBitbangDataPin));
499 r.
set(
"clock_pin",
static_cast<fl::i64>(kBitbangClockPin));
500 r.
set(
"wire_bytes_est", apa102WireBytes(n));
505#if defined(FASTLED_ALL_PINS_HARDWARE_SPI)
506 r.
set(
"generic_bitbang",
false);
508 r.
set(
"generic_bitbang",
true);
524inline fl::json apa102ShowMhz(
int nLeds,
int frames,
int mhz) {
525 DriverShowState& st = driverShowState();
528 case 6: c = apa102At<6>(st.leds);
break;
529 case 10: c = apa102At<10>(st.leds);
break;
530 case 20: c = apa102At<20>(st.leds);
break;
531 case 40: c = apa102At<40>(st.leds);
break;
532 case 80: c = apa102At<80>(st.leds);
break;
535 e.
set(
"success",
false);
536 e.
set(
"error",
"mhz must be 6, 10, 20, 40 or 80");
540 const int n = nLeds < 1 ? 1 : (nLeds > 64 ? 64 : nLeds);
543 const fl::u32 hz = hspiAppliedHz();
545 fl::json r = timeFrames(c, n, frames, 0, &minUs);
546 r = apa102RateReport(r, minUs, n, mhz, hz);
547 r.
set(
"cpu_mhz",
static_cast<fl::i64>(system_get_cpu_freq()));
557constexpr int kHspiLoopBytes = 96;
559struct HspiLoopbackState {
560 fl::u8 got[kHspiLoopBytes];
564 fl::u8 tx[kHspiLoopBytes];
572inline HspiLoopbackState& hspiLoopbackState() {
579__attribute__((always_inline))
inline HspiLoopbackState*& hspiLoopbackActive() {
580 static HspiLoopbackState* s =
nullptr;
584inline fl::u8 hspiPattern(
int i) {
return static_cast<fl::u8>(i * 37 + 11); }
589 HspiLoopbackState& s = *hspiLoopbackActive();
590 const fl::u32 gpi = GPI;
591 if (gpi & s.sckMask) {
593 if (s.bits < kHspiLoopBytes * 8) {
594 if (gpi & s.mosiMask) {
595 s.got[s.bits >> 3] |=
static_cast<fl::u8>(0x80 >> (s.bits & 7));
608inline fl::json apa102HspiLoopbackAt(
int mosiPin,
int sckPin) {
609 HspiLoopbackState& s = hspiLoopbackState();
610 hspiLoopbackActive() = &s;
611 s.mosiMask = 1u << mosiPin;
612 s.sckMask = 1u << sckPin;
616 for (
int i = 0; i < kHspiLoopBytes; ++i) {
618 s.tx[i] = hspiPattern(i);
621 fl::SPIOutput<kApaDataPin, kApaClockPin,
DATA_RATE_KHZ(KHZ)> spi;
627 const fl::u32 gpf13 = GPF13;
628 const fl::u32 gpf14 = GPF14;
629 const fl::u32 gpe = GPE;
633 const fl::u32 t0 =
micros();
634 attachInterrupt(digitalPinToInterrupt(sckPin), hspiSckSample, CHANGE);
635 spi.writeBytes(s.tx, kHspiLoopBytes);
636 const fl::u32 elapsed =
micros() - t0;
639 spi.endTransaction();
640 const int risingAfter = s.rising;
641 const int fallingAfter = s.falling;
642 const int bitsAfter = s.bits;
644 int badBytes = 0, firstBad = -1;
646 for (
int i = 0; i < kHspiLoopBytes; ++i) {
647 if (s.got[i] != hspiPattern(i)) {
652 firstBadGot = s.got[i];
663 pinMode(kApaClockRealPin, OUTPUT);
664 for (
int i = 0; i < 3; ++i) {
670 const int probeEdges = s.rising - risingAfter;
671 detachInterrupt(digitalPinToInterrupt(sckPin));
675 r.
set(
"success", bitsAfter == kHspiLoopBytes * 8 && badBytes == 0);
676 r.
set(
"bytes",
static_cast<fl::i64>(kHspiLoopBytes));
677 r.
set(
"bits",
static_cast<fl::i64>(bitsAfter));
678 r.
set(
"sck_rising_edges",
static_cast<fl::i64>(risingAfter));
679 r.
set(
"sck_falling_edges",
static_cast<fl::i64>(fallingAfter));
686 r.
set(
"probe_edges",
static_cast<fl::i64>(probeEdges));
687 r.
set(
"isr_live_after_transfer", probeEdges == 3);
688 r.
set(
"bad_bytes",
static_cast<fl::i64>(badBytes));
689 r.
set(
"first_bad",
static_cast<fl::i64>(firstBad));
691 r.
set(
"first_bad_got",
static_cast<fl::i64>(firstBadGot));
692 r.
set(
"first_bad_want",
static_cast<fl::i64>(hspiPattern(firstBad)));
695 r.
set(
"elapsed_us",
static_cast<fl::i64>(elapsed));
698 r.
set(
"wifi_opmode",
static_cast<fl::i64>(wifi_get_opmode()));
709inline bool hspiSensePinOk(
int pin,
int other) {
710 const bool usable = pin == 0 || pin == 5 || pin == 12 || pin == 15;
711 return usable && pin != other;
722inline fl::json apa102HspiJumperCheck(
int sensePin,
int txPin) {
723 if (!hspiSensePinOk(sensePin, -1)) {
724 return hspiPinError(
"sensePin must be one of 0, 5, 12, 15");
726 if (txPin != kApaDataRealPin && txPin != kApaClockRealPin) {
727 return hspiPinError(
"txPin must be 13 (MOSI/D7) or 14 (SCK/D5)");
729 const int sckPin = sensePin;
730 const int drivePin = txPin;
731 HspiLoopbackState& s = hspiLoopbackState();
732 hspiLoopbackActive() = &s;
734 s.sckMask = 1u << sckPin;
743 attachInterrupt(digitalPinToInterrupt(sckPin), hspiSckSample, CHANGE);
744 bool seenHigh =
false, seenLow =
false;
745 for (
int i = 0; i < 10; ++i) {
748 if (GPI & s.sckMask) {
753 if ((GPI & s.sckMask) == 0) {
757 detachInterrupt(digitalPinToInterrupt(sckPin));
762 const bool levelFollows = seenHigh && seenLow;
763 const bool isrCounted = s.rising == 10 && s.falling == 10;
767 r.
set(
"success", levelFollows && isrCounted);
768 r.
set(
"level_follows_tx", levelFollows);
769 r.
set(
"isr_counted_edges", isrCounted);
770 r.
set(
"rising",
static_cast<fl::i64>(s.rising));
771 r.
set(
"falling",
static_cast<fl::i64>(s.falling));
772 r.
set(
"sense_pin",
static_cast<fl::i64>(sensePin));
777inline fl::json apa102HspiLoopback(
int khz,
int mosiPin,
int sckPin) {
778 if (!hspiSensePinOk(mosiPin, sckPin) || !hspiSensePinOk(sckPin, mosiPin)) {
780 "sense pins must be two different pins from 0, 5, 12, 15");
786 case 25:
return apa102HspiLoopbackAt<25>(mosiPin, sckPin);
787 case 50:
return apa102HspiLoopbackAt<50>(mosiPin, sckPin);
790 e.
set(
"success",
false);
791 e.
set(
"error",
"khz must be 25 or 50");
804inline fl::json apa102HspiWatch(
int sckPin,
int nBytes,
int nField) {
805 if (!hspiSensePinOk(sckPin, -1)) {
806 return hspiPinError(
"sckPin must be one of 0, 5, 12, 15");
808 const int n = nBytes < 1 ? 1 : (nBytes > 64 ? 64 : nBytes);
809 fl::SPIOutput<kApaDataPin, kApaClockPin,
DATA_RATE_KHZ(25)> spi;
818 const fl::u32 clkBefore = SPI1CLK;
820 const fl::u32 pre1 = ((clkBefore >> 18) & 0x1FFFUL) + 1;
821 const fl::u32 n1 = ((clkBefore >> 12) & 0x3FUL) + 1;
822 const fl::u32 divider = pre1 * n1;
823 const fl::u32 nNew = (
static_cast<fl::u32
>(nField) & 0x3FUL) + 1;
824 const fl::u32 preNew = divider / nNew == 0 ? 1 : divider / nNew;
826 SPI1CLK = ((nNew / 2) & 0x3FUL) | ((nNew - 1) << 12) | ((preNew - 1) << 18);
829 const fl::u32 mask = 1u << sckPin;
832 const fl::u32 bits =
static_cast<fl::u32
>(n) * 8 - 1;
833 const fl::u32 lenMask = (
static_cast<fl::u32
>(SPIMMOSI) << SPILMOSI) |
834 (
static_cast<fl::u32
>(SPIMMISO) << SPILMISO);
835 while (SPI1CMD & SPIBUSY) {}
836 SPI1U1 = (SPI1U1 & ~lenMask) | (bits << SPILMOSI) | (bits << SPILMISO);
837 volatile fl::u32* fifo = &SPI1W0;
838 for (
int i = 0; i < (n + 3) / 4; ++i) {
840 fl::u32 word =
static_cast<fl::u32
>(hspiPattern(b));
841 if (b + 1 < n) word |=
static_cast<fl::u32
>(hspiPattern(b + 1)) << 8;
842 if (b + 2 < n) word |=
static_cast<fl::u32
>(hspiPattern(b + 2)) << 16;
843 if (b + 3 < n) word |=
static_cast<fl::u32
>(hspiPattern(b + 3)) << 24;
846 int transitions = 0, highSamples = 0, samples = 0;
847 fl::u32 last = GPI & mask;
848 const fl::u32 t0 =
micros();
853 __sync_synchronize();
855 while (SPI1CMD & SPIBUSY) {
856 const fl::u32 now = GPI & mask;
866 const fl::u32 elapsed =
micros() - t0;
870 r.
set(
"success", transitions > 0);
874 r.
set(
"sck_pin",
static_cast<fl::i64>(sckPin));
875 r.
set(
"transitions",
static_cast<fl::i64>(transitions));
876 r.
set(
"high_samples",
static_cast<fl::i64>(highSamples));
877 r.
set(
"samples",
static_cast<fl::i64>(samples));
878 r.
set(
"elapsed_us",
static_cast<fl::i64>(elapsed));
882 const fl::u32 clkRan = SPI1CLK;
883 r.
set(
"clk_cnt_l",
static_cast<fl::i64>(clkRan & 0x3FUL));
884 r.
set(
"clk_cnt_h",
static_cast<fl::i64>((clkRan >> 6) & 0x3FUL));
885 r.
set(
"clk_cnt_n",
static_cast<fl::i64>((clkRan >> 12) & 0x3FUL));
886 r.
set(
"clk_pre",
static_cast<fl::i64>((clkRan >> 18) & 0x1FFFUL));
887 r.
set(
"clk_before_pre",
static_cast<fl::i64>((clkBefore >> 18) & 0x1FFFUL));
888 r.
set(
"n_field",
static_cast<fl::i64>(nField));
893inline fl::json wifiOff() {
894 wifi_set_opmode_current(NULL_MODE);
896 r.
set(
"success", wifi_get_opmode() == NULL_MODE);
897 r.
set(
"wifi_opmode",
static_cast<fl::i64>(wifi_get_opmode()));
904inline fl::json uartWs2812InternalLoopback(fl::u32 baud) {
905 namespace u = fl::esp8266_uart;
906 u::begin(baud ? baud : FASTLED_ESP8266_UART_BAUD);
910 USC0(u::kUart) &= ~(1u << UCTXI);
911 USC0(u::kUart) |= (1u << UCLBE);
912 USC0(u::kUart) |= (1u << UCRXRST);
913 USC0(u::kUart) &= ~(1u << UCRXRST);
914 const fl::u8 pixel[3] = {0xA5, 0x0F, 0xC3};
916 for (
int i = 0; i < 3; ++i) {
917 u::encodeByte(pixel[i], &sent[i * 4]);
919 u::write(sent,
sizeof(sent));
922 const fl::u32 got = (USS(u::kUart) >> USRXC) & 0xFF;
925 for (fl::u32 i = 0; i < got && i <
sizeof(sent); ++i) {
926 const fl::u8 c =
static_cast<fl::u8>(USF(u::kUart) & 0x3F);
928 if (c == (sent[i] & 0x3F)) {
932 USC0(u::kUart) &= ~(1u << UCLBE);
933 USC0(u::kUart) |= (1u << UCTXI);
935 r.
set(
"sent",
static_cast<fl::i64>(
sizeof(sent)));
937 r.
set(
"matched",
static_cast<fl::i64>(matched));
939 r.
set(
"success", got ==
sizeof(sent) && matched ==
sizeof(sent));
946inline fl::json jumperCheck(
int rxPin) {
948 if (!isUsableRxPin(rxPin)) {
949 r.
set(
"success",
false);
950 r.
set(
"error",
"rxPin must be one of 0,4,5,12,13,14,15");
955 const fl::u32 mask = 1u << rxPin;
958 const bool seenHigh = (GPI & mask) != 0;
961 const bool seenLow = (GPI & mask) == 0;
963 r.
set(
"rx_high_when_tx_high", seenHigh);
964 r.
set(
"rx_low_when_tx_low", seenLow);
965 r.
set(
"success", seenHigh && seenLow);
975inline int rpcArgInt(
const fl::json&
args, fl::size index,
int fallback) {
976 if (
args.is_array() && index <
args.size() &&
args[index].is_number()) {
979 return static_cast<int>(value.
value());
991 remote.bind(
"resetCause", [](
const fl::json& ) -> fl::json {
995 r.
set(
"crash_count",
static_cast<fl::i64>(wdt.consecutiveCrashCount()));
997 r.
set(
"cont_stack_free",
static_cast<fl::i64>(cont_get_free_stack(g_pcont)));
1000 remote.bind(
"watchdogStall", [](
const fl::json&
args) -> fl::json {
1001 const int ms = rpcArgInt(
args, 0, -1);
1004 e.
set(
"error",
"ms must be >= 0");
1008 while (
fl::millis() - start <
static_cast<fl::u32
>(ms)) {
1020 remote.bind(
"uartWs2812Loopback", [](
const fl::json&
args) -> fl::json {
1021 const int nLeds = rpcArgInt(
args, 0, -1);
1022 const int rxPin = rpcArgInt(
args, 1, -1);
1025 if (nLeds < 0 || nLeds > 80000) {
1027 e.
set(
"error",
"kbaud must be in [0, 80000]");
1030 return uartWs2812InternalLoopback(
static_cast<fl::u32
>(nLeds) * 1000u);
1034 return jumperCheck(rxPin);
1036 return uartWs2812Loopback(nLeds, rxPin);
1038 remote.bind(
"uartWs2812Show", [](
const fl::json&
args) -> fl::json {
1039 return uartWs2812Show(rpcArgInt(
args, 0, 0));
1044 remote.bind(
"wifiOff", [](
const fl::json& ) -> fl::json {
return wifiOff(); });
1045 remote.bind(
"clocklessShow", [](
const fl::json&
args) -> fl::json {
1046 return clocklessShow(rpcArgInt(
args, 0, 0), rpcArgInt(
args, 1, 1));
1048 remote.bind(
"apa102Show", [](
const fl::json&
args) -> fl::json {
1049 return apa102Show(rpcArgInt(
args, 0, 0), rpcArgInt(
args, 1, 1));
1056 remote.bind(
"apa102ShowMhz", [](
const fl::json&
args) -> fl::json {
1057 return apa102ShowMhz(rpcArgInt(
args, 0, 0), 20, rpcArgInt(
args, 1, 6));
1059 remote.bind(
"apa102HspiLoopback", [](
const fl::json&
args) -> fl::json {
1060 return apa102HspiLoopback(rpcArgInt(
args, 0, 0), rpcArgInt(
args, 1, -1),
1061 rpcArgInt(
args, 2, -1));
1063 remote.bind(
"apa102HspiJumperCheck", [](
const fl::json&
args) -> fl::json {
1064 return apa102HspiJumperCheck(rpcArgInt(
args, 0, -1), rpcArgInt(
args, 1, -1));
1066 remote.bind(
"apa102HspiWatch", [](
const fl::json&
args) -> fl::json {
1067 return apa102HspiWatch(rpcArgInt(
args, 0, -1), rpcArgInt(
args, 1, 1),
1068 rpcArgInt(
args, 2, -1));
1075 remote.bind(
"apa102ShowBitbang", [](
const fl::json&
args) -> fl::json {
1076 return apa102ShowBitbang(rpcArgInt(
args, 0, 0), rpcArgInt(
args, 1, 1));
1084 remote.bind(
"i2sLoopback", [](
int nBytes) -> fl::json {
1086 if (nBytes < 1 || nBytes > 300) {
1087 r.
set(
"error",
"nBytes must be in [1, 300]");
1090 const int n = nBytes;
1092 for (
int i = 0; i < n; ++i) {
1093 tx[i] =
static_cast<fl::u8>(i * 53 + 0x1B);
1096 static fl::u16 samples[600];
1097 for (
int i = 0; i < n; ++i) {
1098 fl::esp8266::encodeByte(tx[i], &samples[i * 2]);
1100 const int nSamples = n * 2;
1103 (void)i2s_rxtxdrive_begin(
true,
true,
true,
false);
1104 i2s_set_rate(fl::esp8266::kI2sSlotHz);
1106 for (
int i = 0; i < nSamples; ++i) {
1107 (void)i2s_write_sample_nb(samples[i]);
1109 static fl::u16 got[600];
1112 while (cap < nSamples &&
fl::millis() < deadline) {
1113 if (i2s_rx_available() > 0) {
1114 fl::i16 l = 0, rr = 0;
1115 if (i2s_read_sample(&l, &rr,
false)) {
1118 fl::u16 s =
static_cast<fl::u16
>(l);
1120 s =
static_cast<fl::u16
>(rr);
1127 for (
int i = 0; i < n; ++i) {
1128 if (i * 2 + 1 >= cap) { firstBad = i;
break; }
1129 if (fl::esp8266::decodeByte(got[i * 2], got[i * 2 + 1]) != tx[i]) {
1134 r.
set(
"success", firstBad < 0);
1136 r.
set(
"captured_samples",
static_cast<fl::i64>(cap));
1137 r.
set(
"first_bad",
static_cast<fl::i64>(firstBad));
1138 r.
set(
"s0",
static_cast<fl::i64>(samples[0]));
1141 r.
set(
"dec0",
static_cast<fl::i64>(fl::esp8266::decodeByte(got[0], got[1])));
1152 remote.bind(
"spiLoopback", [](
int nBytes) -> fl::json {
1154 if (nBytes < 1 || nBytes > 64) {
1155 r.
set(
"error",
"nBytes must be in [1, 64]");
1158 const int n = nBytes;
1166 while (SPI1CMD & SPIBUSY) {
1170 GPMUX &= ~(1UL << 9);
1171 SPI1CLK = fl::esp8266::hspiClockFor(1000000).reg_value;
1172 SPI1U = SPIUMOSI | SPIUDUPLEX | SPIUSSE;
1173 SPI1U1 = (7UL << SPILMOSI) | (7UL << SPILMISO);
1177 for (
int i = 0; i < n; ++i) {
1179 while (SPI1CMD & SPIBUSY) {
1183 while (SPI1CMD & SPIBUSY) {
1185 lastGot =
static_cast<fl::u8>(SPI1W0 & 0xFF);
1186 if (lastGot != b && firstBad < 0) {
1190 r.
set(
"success", firstBad < 0);
1192 r.
set(
"first_bad",
static_cast<fl::i64>(firstBad));
1193 r.
set(
"last_got",
static_cast<fl::i64>(lastGot));
1198inline void bindEsp8266Benches(fl::Remote&
remote) {
1199 bindEsp8266WatchdogRpcs(
remote);
1200 bindEsp8266UartRpcs(
remote);
1201 bindEsp8266DriverRpcs(
remote);
1202 bindEsp8266HspiRpcs(
remote);
1203 bindEsp8266HspiBenchRpcs(
remote);
1204 bindEsp8266I2sRpcs(
remote);
1205 bindEsp8266SpiRpcs(
remote);
FL_DISABLE_WARNING_PUSH FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS CFastLED FastLED
Global LED strip management instance.
@ APA102
APA102 LED chipset.
CLEDController * controller
fl::unique_ptr< fl::Remote > remote
static constexpr size_t calculateBytes(size_t num_leds) FL_NO_EXCEPT
Calculate total byte count for APA102 protocol Used for quad-SPI buffer pre-allocation.
::CLEDController & addLeds(::CLEDController *pLed, CRGB *data, int nLedsOrOffset, int nLedsIfOffset=0) FL_NO_EXCEPT
Add a CLEDController instance to the world.
CLEDController & setLeds(CRGB *data, int nLeds) FL_NO_EXCEPT
Set the default array of LEDs to be used by this controller.
VIRTUAL_IF_NOT_AVR void showLeds(fl::u8 brightness) FL_NO_EXCEPT
virtual void init() FL_NO_EXCEPT=0
Initialize the LED controller.
bool has_value() const FL_NO_EXCEPT
static T & instance() FL_NO_EXCEPT
static Watchdog & instance() FL_NO_EXCEPT
void set(const fl::string &key, const json &value) FL_NO_EXCEPT
void push_back(const json &value) FL_NO_EXCEPT
static json object() FL_NO_EXCEPT
static json array() FL_NO_EXCEPT
fl::CLEDController CLEDController
FastLED's Elegant JSON Library: fl::json
void delay(u32 ms, bool run_async=true) FL_NO_EXCEPT
Public delay wrapper that keeps bare Arduino delay() preferred after using fl::delay; while still all...
u32 _retry_cnt
Frame retry counter for ESP8266 clockless drivers (FASTLED_DEBUG_COUNT_FRAME_RETRIES).
fl::u32 millis() FL_NO_EXCEPT
Universal millisecond timer - returns milliseconds since system startup.
fl::u32 micros() FL_NO_EXCEPT
Universal microsecond timer - returns microseconds since system startup.
u32 _frame_cnt
Frame counter for ESP8266 clockless drivers (FASTLED_DEBUG_COUNT_FRAME_RETRIES).
fl::string_view resetCauseName(ResetCause c) FL_NO_EXCEPT
Return a static-lifetime name for c — zero-cost, no allocation.
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.
Unified cross-platform Watchdog Timer API for FastLED.