Skip to content

Commit dbe7647

Browse files
committed
Merge pull request #4484 from blazoncek/parallel-I2S
WWA strip support & parallel I2S for S2/S3 (bumping outputs from 5/4 to 12)
1 parent dc3d463 commit dbe7647

18 files changed

+863
-715
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ wled-update.sh
1515

1616
/build_output/
1717
/node_modules/
18+
/logs/
1819

1920
/wled00/extLibs
2021
/wled00/LittleFS

platformio.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ lib_compat_mode = strict
138138
lib_deps =
139139
fastled/FastLED @ 3.6.0
140140
IRremoteESP8266 @ 2.8.2
141-
makuna/NeoPixelBus @ 2.8.0
141+
makuna/NeoPixelBus @ 2.8.3
142142
#https://github.com/makuna/NeoPixelBus.git#CoreShaderBeta
143143
https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.2.1
144144
# for I2C interface

wled00/FX.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ uint16_t dissolve(uint32_t color) {
612612
for (int j = 0; j <= SEGLEN / 15; j++) {
613613
if (random8() <= SEGMENT.intensity) {
614614
for (size_t times = 0; times < 10; times++) { //attempt to spawn a new pixel 10 times
615-
unsigned i = hw_random16(SEGLEN);
615+
unsigned i = random16(SEGLEN);
616616
if (SEGENV.aux0) { //dissolve to primary/palette
617617
if (pixels[i] == SEGCOLOR(1)) {
618618
pixels[i] = color == SEGCOLOR(0) ? SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0) : color;

wled00/FX.h

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#pragma once
12
/*
23
WS2812FX.h - Library for WS2812 LED effects.
34
Harm Aldick - 2016
@@ -8,12 +9,15 @@
89
Adapted from code originally licensed under the MIT license
910
1011
Modified for WLED
12+
13+
Segment class/struct (c) 2022 Blaz Kristan (@blazoncek)
1114
*/
1215

1316
#ifndef WS2812FX_h
1417
#define WS2812FX_h
1518

1619
#include <vector>
20+
#include "wled.h"
1721

1822
#include "const.h"
1923

@@ -67,18 +71,15 @@
6771
/* each segment uses 82 bytes of SRAM memory, so if you're application fails because of
6872
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
6973
#ifdef ESP8266
70-
#define MAX_NUM_SEGMENTS 16
74+
#define MAX_NUM_SEGMENTS 16
7175
/* How much data bytes all segments combined may allocate */
7276
#define MAX_SEGMENT_DATA 5120
77+
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
78+
#define MAX_NUM_SEGMENTS 20
79+
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*512) // 10k by default (S2 is short on free RAM)
7380
#else
74-
#ifndef MAX_NUM_SEGMENTS
75-
#define MAX_NUM_SEGMENTS 32
76-
#endif
77-
#if defined(ARDUINO_ARCH_ESP32S2)
78-
#define MAX_SEGMENT_DATA MAX_NUM_SEGMENTS*768 // 24k by default (S2 is short on free RAM)
79-
#else
80-
#define MAX_SEGMENT_DATA MAX_NUM_SEGMENTS*1280 // 40k by default
81-
#endif
81+
#define MAX_NUM_SEGMENTS 32 // warning: going beyond 32 may consume too much RAM for stable operation
82+
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*1280) // 40k by default
8283
#endif
8384

8485
/* How much data bytes each segment should max allocate to leave enough space for other segments,
@@ -530,6 +531,9 @@ typedef struct Segment {
530531
inline uint16_t length() const { return width() * height(); } // segment length (count) in physical pixels
531532
inline uint16_t groupLength() const { return grouping + spacing; }
532533
inline uint8_t getLightCapabilities() const { return _capabilities; }
534+
inline void deactivate() { setGeometry(0,0); }
535+
inline Segment &clearName() { if (name) free(name); name = nullptr; return *this; }
536+
inline Segment &setName(const String &name) { return setName(name.c_str()); }
533537

534538
inline static uint16_t getUsedSegmentData() { return _usedSegmentData; }
535539
inline static void addUsedSegmentData(int len) { _usedSegmentData += len; }
@@ -539,14 +543,15 @@ typedef struct Segment {
539543
static void handleRandomPalette();
540544
inline static const CRGBPalette16 &getCurrentPalette() { return Segment::_currentPalette; }
541545

542-
void setUp(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1);
546+
void setGeometry(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1);
543547
Segment &setColor(uint8_t slot, uint32_t c);
544548
Segment &setCCT(uint16_t k);
545549
Segment &setOpacity(uint8_t o);
546550
Segment &setOption(uint8_t n, bool val);
547551
Segment &setMode(uint8_t fx, bool loadDefaults = false);
548552
Segment &setPalette(uint8_t pal);
549-
uint8_t differs(Segment& b) const;
553+
Segment &setName(const char* name);
554+
uint8_t differs(const Segment& b) const;
550555
void refreshLightCapabilities();
551556

552557
// runtime data functions

wled00/FX_fcn.cpp

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ void Segment::handleRandomPalette() {
456456
}
457457

458458
// segId is given when called from network callback, changes are queued if that segment is currently in its effect function
459-
void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y) {
459+
void Segment::setGeometry(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y) {
460460
// return if neither bounds nor grouping have changed
461461
bool boundsUnchanged = (start == i1 && stop == i2);
462462
#ifndef WLED_DISABLE_2D
@@ -601,6 +601,20 @@ Segment &Segment::setPalette(uint8_t pal) {
601601
return *this;
602602
}
603603

604+
Segment &Segment::setName(const char *newName) {
605+
if (newName) {
606+
const int newLen = min(strlen(newName), (size_t)WLED_MAX_SEGNAME_LEN);
607+
if (newLen) {
608+
if (name) name = static_cast<char*>(realloc(name, newLen+1));
609+
else name = static_cast<char*>(malloc(newLen+1));
610+
if (name) strlcpy(name, newName, newLen+1);
611+
name[newLen] = 0;
612+
return *this;
613+
}
614+
}
615+
return clearName();
616+
}
617+
604618
// 2D matrix
605619
unsigned IRAM_ATTR Segment::virtualWidth() const {
606620
unsigned groupLen = groupLength();
@@ -951,7 +965,7 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColor(int i) const
951965
return strip.getPixelColor(i);
952966
}
953967

954-
uint8_t Segment::differs(Segment& b) const {
968+
uint8_t Segment::differs(const Segment& b) const {
955969
uint8_t d = 0;
956970
if (start != b.start) d |= SEG_DIFFERS_BOUNDS;
957971
if (stop != b.stop) d |= SEG_DIFFERS_BOUNDS;
@@ -1192,6 +1206,34 @@ void WS2812FX::finalizeInit() {
11921206

11931207
_hasWhiteChannel = _isOffRefreshRequired = false;
11941208

1209+
unsigned digitalCount = 0;
1210+
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
1211+
// determine if it is sensible to use parallel I2S outputs on ESP32 (i.e. more than 5 outputs = 1 I2S + 4 RMT)
1212+
unsigned maxLedsOnBus = 0;
1213+
for (const auto &bus : busConfigs) {
1214+
if (Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type)) {
1215+
digitalCount++;
1216+
if (bus.count > maxLedsOnBus) maxLedsOnBus = bus.count;
1217+
}
1218+
}
1219+
DEBUG_PRINTF_P(PSTR("Maximum LEDs on a bus: %u\nDigital buses: %u\n"), maxLedsOnBus, digitalCount);
1220+
// we may remove 300 LEDs per bus limit when NeoPixelBus is updated beyond 2.9.0
1221+
if (maxLedsOnBus <= 300 && useParallelI2S) BusManager::useParallelOutput(); // must call before creating buses
1222+
else useParallelI2S = false; // enforce single I2S
1223+
#endif
1224+
1225+
// create buses/outputs
1226+
unsigned mem = 0;
1227+
digitalCount = 0;
1228+
for (const auto &bus : busConfigs) {
1229+
mem += bus.memUsage(Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type) ? digitalCount++ : 0); // includes global buffer
1230+
if (mem <= MAX_LED_MEMORY) {
1231+
if (BusManager::add(bus) == -1) break;
1232+
} else DEBUG_PRINTF_P(PSTR("Out of LED memory! Bus %d (%d) #%u not created."), (int)bus.type, (int)bus.count, digitalCount);
1233+
}
1234+
busConfigs.clear();
1235+
busConfigs.shrink_to_fit();
1236+
11951237
//if busses failed to load, add default (fresh install, FS issue, ...)
11961238
if (BusManager::getNumBusses() == 0) {
11971239
DEBUG_PRINTLN(F("No busses, init default"));
@@ -1207,6 +1249,7 @@ void WS2812FX::finalizeInit() {
12071249

12081250
unsigned prevLen = 0;
12091251
unsigned pinsIndex = 0;
1252+
digitalCount = 0;
12101253
for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
12111254
uint8_t defPin[OUTPUT_MAX_PINS];
12121255
// if we have less types than requested outputs and they do not align, use last known type to set current type
@@ -1271,9 +1314,11 @@ void WS2812FX::finalizeInit() {
12711314
if (Bus::isPWM(dataType) || Bus::isOnOff(dataType)) count = 1;
12721315
prevLen += count;
12731316
BusConfig defCfg = BusConfig(dataType, defPin, start, count, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY, 0, useGlobalLedBuffer);
1317+
mem += defCfg.memUsage(Bus::isDigital(dataType) && !Bus::is2Pin(dataType) ? digitalCount++ : 0);
12741318
if (BusManager::add(defCfg) == -1) break;
12751319
}
12761320
}
1321+
DEBUG_PRINTF_P(PSTR("LED buffer size: %uB/%uB\n"), mem, BusManager::memUsage());
12771322

12781323
_length = 0;
12791324
for (int i=0; i<BusManager::getNumBusses(); i++) {
@@ -1290,6 +1335,7 @@ void WS2812FX::finalizeInit() {
12901335
// This must be done after all buses have been created, as some kinds (parallel I2S) interact
12911336
bus->begin();
12921337
}
1338+
DEBUG_PRINTF_P(PSTR("Heap after buses: %d\n"), ESP.getFreeHeap());
12931339

12941340
Segment::maxWidth = _length;
12951341
Segment::maxHeight = 1;
@@ -1601,7 +1647,7 @@ void WS2812FX::setSegment(uint8_t segId, uint16_t i1, uint16_t i2, uint8_t group
16011647
segId = getSegmentsNum()-1; // segments are added at the end of list
16021648
}
16031649
suspend();
1604-
_segments[segId].setUp(i1, i2, grouping, spacing, offset, startY, stopY);
1650+
_segments[segId].setGeometry(i1, i2, grouping, spacing, offset, startY, stopY);
16051651
resume();
16061652
if (segId > 0 && segId == getSegmentsNum()-1 && i2 <= i1) _segments.pop_back(); // if last segment was deleted remove it from vector
16071653
}

0 commit comments

Comments
 (0)