Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WWA strip support & parallel I2S for S2/S3 (bumping outputs from 5/4 to 12) #4484

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ wled-update.sh

/build_output/
/node_modules/
/logs/

/wled00/extLibs
/wled00/LittleFS
Expand Down
2 changes: 1 addition & 1 deletion platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ lib_compat_mode = strict
lib_deps =
fastled/FastLED @ 3.6.0
IRremoteESP8266 @ 2.8.2
makuna/NeoPixelBus @ 2.8.0
makuna/NeoPixelBus @ 2.8.3
#https://github.com/makuna/NeoPixelBus.git#CoreShaderBeta
https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.2.1
# for I2C interface
Expand Down
24 changes: 13 additions & 11 deletions wled00/FX.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#pragma once
/*
WS2812FX.h - Library for WS2812 LED effects.
Harm Aldick - 2016
Expand All @@ -8,12 +9,15 @@
Adapted from code originally licensed under the MIT license

Modified for WLED

Segment class/struct (c) 2022 Blaz Kristan (@blazoncek)
*/

#ifndef WS2812FX_h
#define WS2812FX_h

#include <vector>
#include "wled.h"

#include "const.h"
#include "bus_manager.h"
Expand Down Expand Up @@ -71,18 +75,15 @@ extern byte realtimeMode; // used in getMappedPixelIndex()
/* each segment uses 82 bytes of SRAM memory, so if you're application fails because of
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
#ifdef ESP8266
#define MAX_NUM_SEGMENTS 16
#define MAX_NUM_SEGMENTS 16
/* How much data bytes all segments combined may allocate */
#define MAX_SEGMENT_DATA 5120
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
#define MAX_NUM_SEGMENTS 20
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*512) // 10k by default (S2 is short on free RAM)
#else
#ifndef MAX_NUM_SEGMENTS
#define MAX_NUM_SEGMENTS 32
#endif
#if defined(ARDUINO_ARCH_ESP32S2)
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*768) // 24k by default (S2 is short on free RAM)
#else
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*1280) // 40k by default
#endif
#define MAX_NUM_SEGMENTS 32 // warning: going beyond 32 may consume too much RAM for stable operation
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*1280) // 40k by default
#endif

/* How much data bytes each segment should max allocate to leave enough space for other segments,
Expand Down Expand Up @@ -543,6 +544,8 @@ typedef struct Segment {
inline uint16_t groupLength() const { return grouping + spacing; }
inline uint8_t getLightCapabilities() const { return _capabilities; }
inline void deactivate() { setGeometry(0,0); }
inline Segment &clearName() { if (name) free(name); name = nullptr; return *this; }
inline Segment &setName(const String &name) { return setName(name.c_str()); }

inline static unsigned getUsedSegmentData() { return Segment::_usedSegmentData; }
inline static void addUsedSegmentData(int len) { Segment::_usedSegmentData += len; }
Expand All @@ -565,6 +568,7 @@ typedef struct Segment {
Segment &setOption(uint8_t n, bool val);
Segment &setMode(uint8_t fx, bool loadDefaults = false);
Segment &setPalette(uint8_t pal);
Segment &setName(const char* name);
uint8_t differs(const Segment& b) const;
void refreshLightCapabilities();

Expand Down Expand Up @@ -736,7 +740,6 @@ class WS2812FX { // 96 bytes
WS2812FX() :
paletteFade(0),
paletteBlend(0),
cctBlending(0),
now(millis()),
timebase(0),
isMatrix(false),
Expand Down Expand Up @@ -839,7 +842,6 @@ class WS2812FX { // 96 bytes

uint8_t
paletteBlend,
cctBlending,
getActiveSegmentsNum() const,
getFirstSelectedSegId() const,
getLastActiveSegmentId() const,
Expand Down
44 changes: 44 additions & 0 deletions wled00/FX_fcn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,19 @@ Segment &Segment::setPalette(uint8_t pal) {
return *this;
}

Segment &Segment::setName(const char *newName) {
willmmiles marked this conversation as resolved.
Show resolved Hide resolved
if (newName) {
const int newLen = min(strlen(newName), (size_t)WLED_MAX_SEGNAME_LEN);
if (newLen) {
if (name) name = static_cast<char*>(realloc(name, newLen+1));
else name = static_cast<char*>(malloc(newLen+1));
if (name) strlcpy(name, newName, newLen);
blazoncek marked this conversation as resolved.
Show resolved Hide resolved
return *this;
}
}
return clearName();
}

// 2D matrix
unsigned Segment::virtualWidth() const {
unsigned groupLen = groupLength();
Expand Down Expand Up @@ -1220,6 +1233,33 @@ void WS2812FX::finalizeInit() {

_hasWhiteChannel = _isOffRefreshRequired = false;

unsigned digitalCount = 0;
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
// determine if it is sensible to use parallel I2S outputs on ESP32 (i.e. more than 5 outputs = 1 I2S + 4 RMT)
unsigned maxLedsOnBus = 0;
for (const auto &bus : busConfigs) {
if (Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type)) {
digitalCount++;
if (bus.count > maxLedsOnBus) maxLedsOnBus = bus.count;
}
}
DEBUG_PRINTF_P(PSTR("Maximum LEDs on a bus: %u\nDigital buses: %u\n"), maxLedsOnBus, digitalCount);
// we may remove 300 LEDs per bus limit when NeoPixelBus is updated beyond 2.9.0
if (maxLedsOnBus <= 300 && useParallelI2S) BusManager::useParallelOutput(); // must call before creating buses
else useParallelI2S = false; // enforce single I2S
willmmiles marked this conversation as resolved.
Show resolved Hide resolved
#endif

// create buses/outputs
unsigned mem = 0;
digitalCount = 0;
for (const auto &bus : busConfigs) {
mem += bus.memUsage(Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type) ? digitalCount++ : 0); // includes global buffer
if (mem <= MAX_LED_MEMORY) BusManager::add(bus);
else DEBUG_PRINTF_P(PSTR("Out of LED memory! Bus %d (%d) #%u not created."), (int)bus.type, (int)bus.count, digitalCount);
}
busConfigs.clear();
busConfigs.shrink_to_fit();

//if busses failed to load, add default (fresh install, FS issue, ...)
if (BusManager::getNumBusses() == 0) {
DEBUG_PRINTLN(F("No busses, init default"));
Expand All @@ -1235,6 +1275,7 @@ void WS2812FX::finalizeInit() {

unsigned prevLen = 0;
unsigned pinsIndex = 0;
digitalCount = 0;
for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
uint8_t defPin[OUTPUT_MAX_PINS];
// if we have less types than requested outputs and they do not align, use last known type to set current type
Expand Down Expand Up @@ -1299,9 +1340,11 @@ void WS2812FX::finalizeInit() {
if (Bus::isPWM(dataType) || Bus::isOnOff(dataType)) count = 1;
prevLen += count;
BusConfig defCfg = BusConfig(dataType, defPin, start, count, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY, 0, useGlobalLedBuffer);
mem += defCfg.memUsage(Bus::isDigital(dataType) && !Bus::is2Pin(dataType) ? digitalCount++ : 0);
if (BusManager::add(defCfg) == -1) break;
}
}
DEBUG_PRINTF_P(PSTR("LED buffer size: %uB/%uB\n"), mem, BusManager::memUsage());

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

Segment::maxWidth = _length;
Segment::maxHeight = 1;
Expand Down
Loading
Loading