mirror of
https://github.com/wled/WLED.git
synced 2026-01-04 22:37:56 +00:00
Compare commits
9 Commits
copilot/re
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e95450b318 | ||
|
|
b556da8b36 | ||
|
|
5cfb6f984b | ||
|
|
60e1698ed2 | ||
|
|
979e3fd1f7 | ||
|
|
600b9b4d8e | ||
|
|
787d8a7342 | ||
|
|
46e77ea203 | ||
|
|
fa868568af |
5
.github/FUNDING.yml
vendored
5
.github/FUNDING.yml
vendored
@@ -1,3 +1,2 @@
|
||||
github: [Aircoookie,blazoncek,DedeHai,lost-hope,willmmiles]
|
||||
custom: ['https://paypal.me/Aircoookie','https://paypal.me/blazoncek']
|
||||
thanks_dev: u/gh/netmindz
|
||||
github: [DedeHai,lost-hope,willmmiles,netmindz]
|
||||
custom: ['https://paypal.me/Aircoookie']
|
||||
|
||||
@@ -545,6 +545,7 @@ lib_deps = ${env:esp32dev.lib_deps}
|
||||
# ------------------------------------------------------------------------------
|
||||
# Hub75 examples
|
||||
# ------------------------------------------------------------------------------
|
||||
# Note: some panels may experience ghosting with default full brightness. use -D WLED_HUB75_MAX_BRIGHTNESS=239 or lower to fix it.
|
||||
|
||||
[env:esp32dev_hub75]
|
||||
board = esp32dev
|
||||
@@ -591,7 +592,7 @@ build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_8M_qspi\"
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1 ;; -DARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
|
||||
-DBOARD_HAS_PSRAM
|
||||
-DLOLIN_WIFI_FIX ; seems to work much better with this
|
||||
-DLOLIN_WIFI_FIX ; seems to work much better with this (sets lower TX power)
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
-D WLED_ENABLE_HUB75MATRIX -D NO_GFX
|
||||
-D S3_LCD_DIV_NUM=20 ;; Attempt to fix wifi performance issue when panel active with S3 chips
|
||||
@@ -622,7 +623,7 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=
|
||||
${Speed_Flags.build_flags} ;; optional: -O2 -> optimize for speed instead of size
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1 ;; -DARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
|
||||
-DBOARD_HAS_PSRAM
|
||||
-DLOLIN_WIFI_FIX ; seems to work much better with this
|
||||
-DLOLIN_WIFI_FIX ; seems to work much better with this (sets lower TX power)
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
-D WLED_ENABLE_HUB75MATRIX -D NO_GFX
|
||||
-D S3_LCD_DIV_NUM=20 ;; Attempt to fix wifi performance issue when panel active with S3 chips
|
||||
|
||||
@@ -284,7 +284,6 @@ void HttpPullLightControl::handleResponse(String& responseStr) {
|
||||
if (!requestJSONBufferLock(myLockId)) {
|
||||
DEBUG_PRINT(F("ERROR: Can not request JSON Buffer Lock, number: "));
|
||||
DEBUG_PRINTLN(myLockId);
|
||||
releaseJSONBufferLock(); // Just release in any case, maybe there was already a buffer lock
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -401,19 +401,19 @@ void RotaryEncoderUIUsermod::sortModesAndPalettes() {
|
||||
re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
|
||||
|
||||
DEBUG_PRINT(F("Sorting palettes: ")); DEBUG_PRINT(getPaletteCount()); DEBUG_PRINT('/'); DEBUG_PRINTLN(customPalettes.size());
|
||||
palettes_qstrings = re_findModeStrings(JSON_palette_names, getPaletteCount() + 1); // +1 for default palette
|
||||
palettes_alpha_indexes = re_initIndexArray(getPaletteCount());
|
||||
palettes_qstrings = re_findModeStrings(JSON_palette_names, getPaletteCount()); // allocates memory for all palette names
|
||||
palettes_alpha_indexes = re_initIndexArray(getPaletteCount()); // allocates memory for all palette indexes
|
||||
if (customPalettes.size()) {
|
||||
for (int i=0; i<customPalettes.size(); i++) {
|
||||
palettes_alpha_indexes[getPaletteCount()-customPalettes.size()+i] = 255-i;
|
||||
palettes_qstrings[getPaletteCount()-customPalettes.size()+i] = PSTR("~Custom~");
|
||||
palettes_alpha_indexes[FIXED_PALETTE_COUNT+i] = 255-i;
|
||||
palettes_qstrings[FIXED_PALETTE_COUNT+i] = PSTR("~Custom~");
|
||||
}
|
||||
}
|
||||
// How many palette names start with '*' and should not be sorted?
|
||||
// (Also skipping the first one, 'Default').
|
||||
int skipPaletteCount = 1;
|
||||
while (pgm_read_byte_near(palettes_qstrings[skipPaletteCount]) == '*') skipPaletteCount++;
|
||||
re_sortModes(palettes_qstrings, palettes_alpha_indexes, getPaletteCount()-customPalettes.size(), skipPaletteCount);
|
||||
int skipPaletteCount = 1; // could use DYNAMIC_PALETTE_COUNT instead
|
||||
while (pgm_read_byte_near(palettes_qstrings[skipPaletteCount]) == '*') skipPaletteCount++; // legacy code
|
||||
re_sortModes(palettes_qstrings, palettes_alpha_indexes, FIXED_PALETTE_COUNT, skipPaletteCount); // only sort fixed palettes (skip dynamic)
|
||||
}
|
||||
|
||||
byte *RotaryEncoderUIUsermod::re_initIndexArray(int numModes) {
|
||||
|
||||
@@ -10276,7 +10276,7 @@ uint16_t mode_particleBalance(void) {
|
||||
if (SEGMENT.check3) // random, use perlin noise
|
||||
xgravity = ((int16_t)perlin8(SEGENV.aux0) - 128);
|
||||
else // sinusoidal
|
||||
xgravity = (int16_t)cos8(SEGENV.aux0) - 128;//((int32_t)(SEGMENT.custom3 << 2) * cos8(SEGENV.aux0)
|
||||
xgravity = (int16_t)cos8_t(SEGENV.aux0) - 128;//((int32_t)(SEGMENT.custom3 << 2) * cos8(SEGENV.aux0)
|
||||
// scale the force
|
||||
xgravity = (xgravity * ((SEGMENT.custom3+1) << 2)) / 128; // xgravity: -127 to +127
|
||||
PartSys->applyForce(xgravity);
|
||||
|
||||
@@ -230,7 +230,7 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
|
||||
// then come the custom palettes (255,254,...) growing downwards from 255 (255 being 1st custom palette)
|
||||
// palette 0 is a varying palette depending on effect and may be replaced by segment's color if so
|
||||
// instructed in color_from_palette()
|
||||
if (pal > FIXED_PALETTE_COUNT && pal <= 255-customPalettes.size()) pal = 0; // out of bounds palette
|
||||
if (pal >= FIXED_PALETTE_COUNT && pal <= 255-customPalettes.size()) pal = 0; // out of bounds palette
|
||||
//default palette. Differs depending on effect
|
||||
if (pal == 0) pal = _default_palette; // _default_palette is set in setMode()
|
||||
switch (pal) {
|
||||
@@ -268,11 +268,11 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
|
||||
default: //progmem palettes
|
||||
if (pal > 255 - customPalettes.size()) {
|
||||
targetPalette = customPalettes[255-pal]; // we checked bounds above
|
||||
} else if (pal < DYNAMIC_PALETTE_COUNT+FASTLED_PALETTE_COUNT+1) { // palette 6 - 12, fastled palettes
|
||||
targetPalette = *fastledPalettes[pal-DYNAMIC_PALETTE_COUNT-1];
|
||||
} else if (pal < DYNAMIC_PALETTE_COUNT + FASTLED_PALETTE_COUNT) { // palette 6 - 12, fastled palettes
|
||||
targetPalette = *fastledPalettes[pal - DYNAMIC_PALETTE_COUNT];
|
||||
} else {
|
||||
byte tcp[72];
|
||||
memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[pal-(DYNAMIC_PALETTE_COUNT+FASTLED_PALETTE_COUNT)-1])), 72);
|
||||
memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[pal - (DYNAMIC_PALETTE_COUNT + FASTLED_PALETTE_COUNT)])), sizeof(tcp));
|
||||
targetPalette.loadDynamicGradientPalette(tcp);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -21,12 +21,9 @@
|
||||
#ifdef ESP8266
|
||||
#include "core_esp8266_waveform.h"
|
||||
#endif
|
||||
#include "const.h"
|
||||
#include "colors.h"
|
||||
#include "pin_manager.h"
|
||||
#include "bus_manager.h"
|
||||
#include "bus_wrapper.h"
|
||||
#include <bits/unique_ptr.h>
|
||||
#include "wled.h"
|
||||
|
||||
extern char cmDNS[];
|
||||
extern bool cctICused;
|
||||
@@ -34,17 +31,18 @@ extern bool useParallelI2S;
|
||||
|
||||
// functions to get/set bits in an array - based on functions created by Brandon for GOL
|
||||
// toDo : make this a class that's completely defined in a header file
|
||||
// note: these functions are automatically inline by the compiler
|
||||
bool getBitFromArray(const uint8_t* byteArray, size_t position) { // get bit value
|
||||
size_t byteIndex = position / 8;
|
||||
unsigned bitIndex = position % 8;
|
||||
size_t byteIndex = position >> 3; // divide by 8
|
||||
unsigned bitIndex = position & 0x07; // modulo 8
|
||||
uint8_t byteValue = byteArray[byteIndex];
|
||||
return (byteValue >> bitIndex) & 1;
|
||||
}
|
||||
|
||||
void setBitInArray(uint8_t* byteArray, size_t position, bool value) { // set bit - with error handling for nullptr
|
||||
//if (byteArray == nullptr) return;
|
||||
size_t byteIndex = position / 8;
|
||||
unsigned bitIndex = position % 8;
|
||||
size_t byteIndex = position >> 3; // divide by 8
|
||||
unsigned bitIndex = position & 0x07; // modulo 8
|
||||
if (value)
|
||||
byteArray[byteIndex] |= (1 << bitIndex);
|
||||
else
|
||||
@@ -52,7 +50,7 @@ void setBitInArray(uint8_t* byteArray, size_t position, bool value) { // set bi
|
||||
}
|
||||
|
||||
size_t getBitArrayBytes(size_t num_bits) { // number of bytes needed for an array with num_bits bits
|
||||
return (num_bits + 7) / 8;
|
||||
return (num_bits + 7) >> 3;
|
||||
}
|
||||
|
||||
void setBitArray(uint8_t* byteArray, size_t numBits, bool value) { // set all bits to same value
|
||||
@@ -62,45 +60,6 @@ void setBitArray(uint8_t* byteArray, size_t numBits, bool value) { // set all b
|
||||
else memset(byteArray, 0x00, len);
|
||||
}
|
||||
|
||||
//colors.cpp
|
||||
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
|
||||
|
||||
//udp.cpp
|
||||
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, const byte *buffer, uint8_t bri=255, bool isRGBW=false);
|
||||
|
||||
//util.cpp
|
||||
// memory allocation wrappers
|
||||
extern "C" {
|
||||
// prefer DRAM over PSRAM (if available) in d_ alloc functions
|
||||
void *d_malloc(size_t);
|
||||
void *d_calloc(size_t, size_t);
|
||||
void *d_realloc_malloc(void *ptr, size_t size);
|
||||
#ifndef ESP8266
|
||||
inline void d_free(void *ptr) { heap_caps_free(ptr); }
|
||||
#else
|
||||
inline void d_free(void *ptr) { free(ptr); }
|
||||
#endif
|
||||
#if defined(BOARD_HAS_PSRAM)
|
||||
// prefer PSRAM over DRAM in p_ alloc functions
|
||||
void *p_malloc(size_t);
|
||||
void *p_calloc(size_t, size_t);
|
||||
void *p_realloc_malloc(void *ptr, size_t size);
|
||||
inline void p_free(void *ptr) { heap_caps_free(ptr); }
|
||||
#else
|
||||
#define p_malloc d_malloc
|
||||
#define p_calloc d_calloc
|
||||
#define p_free d_free
|
||||
#endif
|
||||
}
|
||||
|
||||
//color mangling macros
|
||||
#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b))))
|
||||
#define R(c) (byte((c) >> 16))
|
||||
#define G(c) (byte((c) >> 8))
|
||||
#define B(c) (byte(c))
|
||||
#define W(c) (byte((c) >> 24))
|
||||
|
||||
|
||||
static ColorOrderMap _colorOrderMap = {};
|
||||
|
||||
bool ColorOrderMap::add(uint16_t start, uint16_t len, uint8_t colorOrder) {
|
||||
@@ -800,6 +759,13 @@ BusHub75Matrix::BusHub75Matrix(const BusConfig &bc) : Bus(bc.type, bc.start, bc.
|
||||
_valid = false;
|
||||
_hasRgb = true;
|
||||
_hasWhite = false;
|
||||
virtualDisp = nullptr; // todo: this should be solved properly, can cause memory leak (if omitted here, nothing seems to work)
|
||||
// aliases for easier reading
|
||||
uint8_t panelWidth = bc.pins[0];
|
||||
uint8_t panelHeight = bc.pins[1];
|
||||
uint8_t chainLength = bc.pins[2];
|
||||
_rows = bc.pins[3];
|
||||
_cols = bc.pins[4];
|
||||
|
||||
mxconfig.double_buff = false; // Use our own memory-optimised buffer rather than the driver's own double-buffer
|
||||
|
||||
@@ -808,38 +774,25 @@ BusHub75Matrix::BusHub75Matrix(const BusConfig &bc) : Bus(bc.type, bc.start, bc.
|
||||
|
||||
// mxconfig.latch_blanking = 3;
|
||||
// mxconfig.i2sspeed = HUB75_I2S_CFG::HZ_10M; // experimental - 5MHZ should be enugh, but colours looks slightly better at 10MHz
|
||||
//mxconfig.min_refresh_rate = 90;
|
||||
//mxconfig.min_refresh_rate = 120;
|
||||
mxconfig.clkphase = bc.reversed;
|
||||
// mxconfig.min_refresh_rate = 90;
|
||||
// mxconfig.min_refresh_rate = 120;
|
||||
|
||||
virtualDisp = nullptr;
|
||||
mxconfig.clkphase = bc.reversed;
|
||||
// allow chain length up to 4, limit to prevent bad data from preventing boot due to low memory
|
||||
mxconfig.chain_length = max((uint8_t) 1, min(chainLength, (uint8_t) 4));
|
||||
|
||||
if (mxconfig.mx_height >= 64 && (mxconfig.chain_length > 1)) {
|
||||
DEBUGBUS_PRINTLN(F("WARNING, only single panel can be used of 64 pixel boards due to memory"));
|
||||
mxconfig.chain_length = 1;
|
||||
}
|
||||
|
||||
if (bc.type == TYPE_HUB75MATRIX_HS) {
|
||||
mxconfig.mx_width = min((uint8_t) 64, bc.pins[0]);
|
||||
mxconfig.mx_height = min((uint8_t) 64, bc.pins[1]);
|
||||
// Disable chains of panels for now, incomplete UI changes
|
||||
// if(bc.pins[2] > 1 && bc.pins[3] != 0 && bc.pins[4] != 0 && bc.pins[3] != 255 && bc.pins[4] != 255) {
|
||||
// virtualDisp = new VirtualMatrixPanel((*display), bc.pins[3], bc.pins[4], mxconfig.mx_width, mxconfig.mx_height, CHAIN_BOTTOM_LEFT_UP);
|
||||
// }
|
||||
mxconfig.mx_width = min((uint8_t) 64, panelWidth); // TODO: UI limit is 128, this limits to 64
|
||||
mxconfig.mx_height = min((uint8_t) 64, panelHeight);
|
||||
} else if (bc.type == TYPE_HUB75MATRIX_QS) {
|
||||
mxconfig.mx_width = min((uint8_t) 64, bc.pins[0]) * 2;
|
||||
mxconfig.mx_height = min((uint8_t) 64, bc.pins[1]) / 2;
|
||||
virtualDisp = new VirtualMatrixPanel((*display), 1, 1, bc.pins[0], bc.pins[1]);
|
||||
virtualDisp->setRotation(0);
|
||||
switch(bc.pins[1]) {
|
||||
case 16:
|
||||
virtualDisp->setPhysicalPanelScanRate(FOUR_SCAN_16PX_HIGH);
|
||||
break;
|
||||
case 32:
|
||||
virtualDisp->setPhysicalPanelScanRate(FOUR_SCAN_32PX_HIGH);
|
||||
break;
|
||||
case 64:
|
||||
virtualDisp->setPhysicalPanelScanRate(FOUR_SCAN_64PX_HIGH);
|
||||
break;
|
||||
default:
|
||||
DEBUGBUS_PRINTLN("Unsupported height");
|
||||
return;
|
||||
}
|
||||
_isVirtual = true;
|
||||
mxconfig.mx_width = min((uint8_t) 64, panelWidth) * 2;
|
||||
mxconfig.mx_height = min((uint8_t) 64, panelHeight) / 2;
|
||||
} else {
|
||||
DEBUGBUS_PRINTLN("Unknown type");
|
||||
return;
|
||||
@@ -853,12 +806,6 @@ BusHub75Matrix::BusHub75Matrix(const BusConfig &bc) : Bus(bc.type, bc.start, bc.
|
||||
} else mxconfig.setPixelColorDepthBits(8);
|
||||
#endif
|
||||
|
||||
mxconfig.chain_length = max((uint8_t) 1, min(bc.pins[2], (uint8_t) 4)); // prevent bad data preventing boot due to low memory
|
||||
|
||||
if(mxconfig.mx_height >= 64 && (mxconfig.chain_length > 1)) {
|
||||
DEBUGBUS_PRINTLN("WARNING, only single panel can be used of 64 pixel boards due to memory");
|
||||
mxconfig.chain_length = 1;
|
||||
}
|
||||
|
||||
|
||||
// HUB75_I2S_CFG::i2s_pins _pins={R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, LAT_PIN, OE_PIN, CLK_PIN};
|
||||
@@ -915,9 +862,9 @@ BusHub75Matrix::BusHub75Matrix(const BusConfig &bc) : Bus(bc.type, bc.start, bc.
|
||||
return;
|
||||
}
|
||||
|
||||
if(bc.colorOrder == COL_ORDER_RGB) {
|
||||
if (bc.colorOrder == COL_ORDER_RGB) {
|
||||
DEBUGBUS_PRINTLN("MatrixPanel_I2S_DMA = Default color order (RGB)");
|
||||
} else if(bc.colorOrder == COL_ORDER_BGR) {
|
||||
} else if (bc.colorOrder == COL_ORDER_BGR) {
|
||||
DEBUGBUS_PRINTLN("MatrixPanel_I2S_DMA = color order BGR");
|
||||
int8_t tmpPin;
|
||||
tmpPin = mxconfig.gpio.r1;
|
||||
@@ -944,21 +891,27 @@ BusHub75Matrix::BusHub75Matrix(const BusConfig &bc) : Bus(bc.type, bc.start, bc.
|
||||
return;
|
||||
}
|
||||
|
||||
this->_len = (display->width() * display->height());
|
||||
this->_len = (display->width() * display->height()); // note: this returns correct number of pixels but incorrect dimensions if using virtual display (updated below)
|
||||
|
||||
DEBUGBUS_PRINTF("Length: %u\n", _len);
|
||||
if(this->_len >= MAX_LEDS) {
|
||||
if (this->_len >= MAX_LEDS) {
|
||||
DEBUGBUS_PRINTLN("MatrixPanel_I2S_DMA Too many LEDS - playing safe");
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUGBUS_PRINTLN("MatrixPanel_I2S_DMA created");
|
||||
// let's adjust default brightness
|
||||
display->setBrightness8(25); // range is 0-255, 0 - 0%, 255 - 100%
|
||||
|
||||
// as noted in HUB75_I2S_DMA library, some panels can show ghosting if set higher than 239, so let users override at compile time
|
||||
#ifndef WLED_HUB75_MAX_BRIGHTNESS
|
||||
#define WLED_HUB75_MAX_BRIGHTNESS 255
|
||||
#endif
|
||||
// let's adjust default brightness (128), brightness scaling is handled by WLED
|
||||
//display->setBrightness8(WLED_HUB75_MAX_BRIGHTNESS); // range is 0-255, 0 - 0%, 255 - 100%
|
||||
|
||||
delay(24); // experimental
|
||||
DEBUGBUS_PRINT(F("heap usage: ")); DEBUGBUS_PRINTLN(lastHeap - ESP.getFreeHeap());
|
||||
// Allocate memory and start DMA display
|
||||
if( not display->begin() ) {
|
||||
if (!display->begin() ) {
|
||||
DEBUGBUS_PRINTLN("****** MatrixPanel_I2S_DMA !KABOOM! I2S memory allocation failed ***********");
|
||||
DEBUGBUS_PRINT(F("heap usage: ")); DEBUGBUS_PRINTLN(lastHeap - ESP.getFreeHeap());
|
||||
return;
|
||||
@@ -971,10 +924,10 @@ BusHub75Matrix::BusHub75Matrix(const BusConfig &bc) : Bus(bc.type, bc.start, bc.
|
||||
display->clearScreen(); // initially clear the screen buffer
|
||||
DEBUGBUS_PRINTLN("MatrixPanel_I2S_DMA clear ok");
|
||||
|
||||
if (_ledBuffer) free(_ledBuffer); // should not happen
|
||||
if (_ledsDirty) free(_ledsDirty); // should not happen
|
||||
if (_ledBuffer) d_free(_ledBuffer); // should not happen
|
||||
if (_ledsDirty) d_free(_ledsDirty); // should not happen
|
||||
DEBUGBUS_PRINTLN("MatrixPanel_I2S_DMA allocate memory");
|
||||
_ledsDirty = (byte*) malloc(getBitArrayBytes(_len)); // create LEDs dirty bits
|
||||
_ledsDirty = (byte*) d_malloc(getBitArrayBytes(_len)); // create LEDs dirty bits
|
||||
DEBUGBUS_PRINTLN("MatrixPanel_I2S_DMA allocate memory ok");
|
||||
|
||||
if (_ledsDirty == nullptr) {
|
||||
@@ -988,17 +941,50 @@ BusHub75Matrix::BusHub75Matrix(const BusConfig &bc) : Bus(bc.type, bc.start, bc.
|
||||
setBitArray(_ledsDirty, _len, false); // reset dirty bits
|
||||
|
||||
if (mxconfig.double_buff == false) {
|
||||
_ledBuffer = (CRGB*) calloc(_len, sizeof(CRGB)); // create LEDs buffer (initialized to BLACK)
|
||||
// create LEDs buffer (initialized to BLACK), prefer DRAM if enough heap is available (faster in case global _pixels buffer is in PSRAM as not both will fit the cache)
|
||||
_ledBuffer = static_cast<CRGB*>(allocate_buffer(_len * sizeof(CRGB), BFRALLOC_PREFER_DRAM | BFRALLOC_CLEAR));
|
||||
}
|
||||
}
|
||||
|
||||
PANEL_CHAIN_TYPE chainType = CHAIN_NONE; // default for quarter-scan panels that do not use chaining
|
||||
// chained panels with cols and rows define need the virtual display driver, so do quarter-scan panels
|
||||
if (chainLength > 1 && (_rows > 1 || _cols > 1) || bc.type == TYPE_HUB75MATRIX_QS) {
|
||||
_isVirtual = true;
|
||||
chainType = CHAIN_BOTTOM_LEFT_UP; // TODO: is there any need to support other chaining types?
|
||||
DEBUGBUS_PRINTF_P(PSTR("Using virtual matrix: %ux%u panels of %ux%u pixels\n"), _cols, _rows, mxconfig.mx_width, mxconfig.mx_height);
|
||||
}
|
||||
else {
|
||||
_isVirtual = false;
|
||||
}
|
||||
|
||||
if (_isVirtual) {
|
||||
virtualDisp = new VirtualMatrixPanel((*display), _rows, _cols, mxconfig.mx_width, mxconfig.mx_height, chainType);
|
||||
virtualDisp->setRotation(0);
|
||||
if (bc.type == TYPE_HUB75MATRIX_QS) {
|
||||
switch(panelHeight) {
|
||||
case 16:
|
||||
virtualDisp->setPhysicalPanelScanRate(FOUR_SCAN_16PX_HIGH);
|
||||
break;
|
||||
case 32:
|
||||
virtualDisp->setPhysicalPanelScanRate(FOUR_SCAN_32PX_HIGH);
|
||||
break;
|
||||
case 64:
|
||||
virtualDisp->setPhysicalPanelScanRate(FOUR_SCAN_64PX_HIGH);
|
||||
break;
|
||||
default:
|
||||
DEBUGBUS_PRINTLN("Unsupported height");
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_valid) {
|
||||
_panelWidth = virtualDisp ? virtualDisp->width() : display->width(); // cache width - it will never change
|
||||
}
|
||||
|
||||
DEBUGBUS_PRINT(F("MatrixPanel_I2S_DMA "));
|
||||
DEBUGBUS_PRINTF("%sstarted, width=%u, %u pixels.\n", _valid? "":"not ", _panelWidth, _len);
|
||||
DEBUGBUS_PRINTF_P(PSTR("%sstarted, width=%u, %u pixels.\n"), _valid? "":"not ", _panelWidth, _len);
|
||||
|
||||
if (_ledBuffer != nullptr) DEBUGBUS_PRINTLN(F("MatrixPanel_I2S_DMA LEDS buffer enabled."));
|
||||
if (_ledsDirty != nullptr) DEBUGBUS_PRINTLN(F("MatrixPanel_I2S_DMA LEDS dirty bit optimization enabled."));
|
||||
@@ -1009,8 +995,8 @@ BusHub75Matrix::BusHub75Matrix(const BusConfig &bc) : Bus(bc.type, bc.start, bc.
|
||||
}
|
||||
}
|
||||
|
||||
void __attribute__((hot)) BusHub75Matrix::setPixelColor(unsigned pix, uint32_t c) {
|
||||
if (!_valid || pix >= _len) return;
|
||||
void IRAM_ATTR BusHub75Matrix::setPixelColor(unsigned pix, uint32_t c) {
|
||||
if (!_valid) return; // note: no need to check pix >= _len as that is checked in containsPixel()
|
||||
// if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
|
||||
|
||||
if (_ledBuffer) {
|
||||
@@ -1028,8 +1014,8 @@ void __attribute__((hot)) BusHub75Matrix::setPixelColor(unsigned pix, uint32_t c
|
||||
uint8_t g = G(c);
|
||||
uint8_t b = B(c);
|
||||
|
||||
if(virtualDisp != nullptr) {
|
||||
int x = pix % _panelWidth;
|
||||
if (virtualDisp != nullptr) {
|
||||
int x = pix % _panelWidth; // TODO: check if using & and shift would be faster here, it limits to power-of-2 widths though
|
||||
int y = pix / _panelWidth;
|
||||
virtualDisp->drawPixelRGB888(int16_t(x), int16_t(y), r, g, b);
|
||||
} else {
|
||||
@@ -1041,41 +1027,35 @@ void __attribute__((hot)) BusHub75Matrix::setPixelColor(unsigned pix, uint32_t c
|
||||
}
|
||||
|
||||
uint32_t BusHub75Matrix::getPixelColor(unsigned pix) const {
|
||||
if (!_valid || pix >= _len) return IS_BLACK;
|
||||
if (!_valid) return IS_BLACK; // note: no need to check pix >= _len as that is checked in containsPixel()
|
||||
if (_ledBuffer)
|
||||
return uint32_t(_ledBuffer[pix].scale8(_bri)) & 0x00FFFFFF; // scale8() is needed to mimic NeoPixelBus, which returns scaled-down colours
|
||||
return uint32_t(_ledBuffer[pix]);
|
||||
else
|
||||
return getBitFromArray(_ledsDirty, pix) ? IS_DARKGREY: IS_BLACK; // just a hack - we only know if the pixel is black or not
|
||||
}
|
||||
|
||||
void BusHub75Matrix::setBrightness(uint8_t b) {
|
||||
_bri = b;
|
||||
if (display) display->setBrightness(_bri);
|
||||
display->setBrightness(_bri);
|
||||
}
|
||||
|
||||
void BusHub75Matrix::show(void) {
|
||||
if (!_valid) return;
|
||||
display->setBrightness(_bri);
|
||||
|
||||
if (_ledBuffer) {
|
||||
// write out buffered LEDs
|
||||
bool isVirtualDisp = (virtualDisp != nullptr);
|
||||
unsigned height = isVirtualDisp ? virtualDisp->height() : display->height();
|
||||
unsigned height = _isVirtual ? virtualDisp->height() : display->height();
|
||||
unsigned width = _panelWidth;
|
||||
|
||||
//while(!previousBufferFree) delay(1); // experimental - Wait before we allow any writing to the buffer. Stop flicker.
|
||||
|
||||
size_t pix = 0; // running pixel index
|
||||
for (int y=0; y<height; y++) for (int x=0; x<width; x++) {
|
||||
if (getBitFromArray(_ledsDirty, pix) == true) { // only repaint the "dirty" pixels
|
||||
uint32_t c = uint32_t(_ledBuffer[pix]) & 0x00FFFFFF; // get RGB color, removing FastLED "alpha" component
|
||||
uint8_t r = R(c);
|
||||
uint8_t g = G(c);
|
||||
uint8_t b = B(c);
|
||||
if (isVirtualDisp) virtualDisp->drawPixelRGB888(int16_t(x), int16_t(y), r, g, b);
|
||||
else display->drawPixelRGB888(int16_t(x), int16_t(y), r, g, b);
|
||||
CRGB c = _ledBuffer[pix];
|
||||
//c.nscale8_video(_bri); // apply brightness
|
||||
if (_isVirtual) virtualDisp->drawPixelRGB888(int16_t(x), int16_t(y), c.r, c.g, c.b);
|
||||
else display->drawPixelRGB888(int16_t(x), int16_t(y), c.r, c.g, c.b);
|
||||
}
|
||||
pix ++;
|
||||
pix++;
|
||||
}
|
||||
setBitArray(_ledsDirty, _len, false); // buffer shown - reset all dirty bits
|
||||
}
|
||||
@@ -1084,16 +1064,18 @@ void BusHub75Matrix::show(void) {
|
||||
void BusHub75Matrix::cleanup() {
|
||||
if (display && _valid) display->stopDMAoutput(); // terminate DMA driver (display goes black)
|
||||
_valid = false;
|
||||
delay(30); // give some time to finish DMA
|
||||
_panelWidth = 0;
|
||||
deallocatePins();
|
||||
DEBUGBUS_PRINTLN("HUB75 output ended.");
|
||||
|
||||
//if (virtualDisp != nullptr) delete virtualDisp; // warning: deleting object of polymorphic class type 'VirtualMatrixPanel' which has non-virtual destructor might cause undefined behavior
|
||||
delete display;
|
||||
DEBUGBUS_PRINTLN(F("HUB75 output ended."));
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S3 // on ESP32-S3 deleting display/virtualDisp does not work and leads to crash (DMA issues), request reboot from user instead
|
||||
if (virtualDisp != nullptr) delete virtualDisp; // note: in MM there is a warning to not do this but if using "NO_GFX" this is safe
|
||||
if (display != nullptr) delete display;
|
||||
display = nullptr;
|
||||
virtualDisp = nullptr;
|
||||
if (_ledBuffer != nullptr) free(_ledBuffer); _ledBuffer = nullptr;
|
||||
if (_ledsDirty != nullptr) free(_ledsDirty); _ledsDirty = nullptr;
|
||||
virtualDisp = nullptr; // note: when not using "NO_GFX" this causes a memory leak
|
||||
#endif
|
||||
if (_ledBuffer != nullptr) d_free(_ledBuffer); _ledBuffer = nullptr;
|
||||
if (_ledsDirty != nullptr) d_free(_ledsDirty); _ledsDirty = nullptr;
|
||||
}
|
||||
|
||||
void BusHub75Matrix::deallocatePins() {
|
||||
@@ -1114,8 +1096,10 @@ size_t BusHub75Matrix::getPins(uint8_t* pinArray) const {
|
||||
pinArray[0] = mxconfig.mx_width;
|
||||
pinArray[1] = mxconfig.mx_height;
|
||||
pinArray[2] = mxconfig.chain_length;
|
||||
pinArray[3] = _rows;
|
||||
pinArray[4] = _cols;
|
||||
}
|
||||
return 3;
|
||||
return 5;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -166,7 +166,7 @@ class Bus {
|
||||
inline bool containsPixel(uint16_t pix) const { return pix >= _start && pix < _start + _len; }
|
||||
|
||||
static inline std::vector<LEDType> getLEDTypes() { return {{TYPE_NONE, "", PSTR("None")}}; } // not used. just for reference for derived classes
|
||||
static constexpr size_t getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : isHub75(type) ? 3 : is2Pin(type) + 1; } // credit @PaoloTK
|
||||
static constexpr size_t getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : isHub75(type) ? 5 : is2Pin(type) + 1; } // credit @PaoloTK
|
||||
static constexpr size_t getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); }
|
||||
static constexpr bool hasRGB(uint8_t type) {
|
||||
return !((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_ANALOG_1CH || type == TYPE_ANALOG_2CH || type == TYPE_ONOFF);
|
||||
@@ -395,12 +395,15 @@ class BusHub75Matrix : public Bus {
|
||||
VirtualMatrixPanel *virtualDisp = nullptr;
|
||||
HUB75_I2S_CFG mxconfig;
|
||||
unsigned _panelWidth = 0;
|
||||
CRGB *_ledBuffer = nullptr;
|
||||
uint8_t _rows = 1; // panels per row
|
||||
uint8_t _cols = 1; // panels per column
|
||||
bool _isVirtual = false; // note: this is not strictly needed but there are padding bytes here anyway
|
||||
CRGB *_ledBuffer = nullptr; // note: using uint32_t buffer is only 2% faster and not worth the extra RAM
|
||||
byte *_ledsDirty = nullptr;
|
||||
// workaround for missing constants on include path for non-MM
|
||||
uint32_t IS_BLACK = 0x000000;
|
||||
uint32_t IS_DARKGREY = 0x333333;
|
||||
const int PIN_COUNT = 14;
|
||||
static constexpr uint32_t IS_BLACK = 0x000000u;
|
||||
static constexpr uint32_t IS_DARKGREY = 0x333333u;
|
||||
static constexpr int PIN_COUNT = 14;
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -451,7 +454,7 @@ struct BusConfig {
|
||||
//validates start and length and extends total if needed
|
||||
bool adjustBounds(uint16_t& total) {
|
||||
if (!count) count = 1;
|
||||
if (!Bus::isVirtual(type) && count > MAX_LEDS_PER_BUS) count = MAX_LEDS_PER_BUS;
|
||||
if (count > MAX_LEDS_PER_BUS) count = MAX_LEDS_PER_BUS;
|
||||
if (start >= MAX_LEDS) return false;
|
||||
//limit length of strip if it would exceed total permissible LEDs
|
||||
if (start + count > MAX_LEDS) count = MAX_LEDS - start;
|
||||
|
||||
@@ -63,8 +63,8 @@ uint32_t WLED_O2_ATTR color_add(uint32_t c1, uint32_t c2, bool preserveCR) //121
|
||||
* if using "video" method the resulting color will never become black unless it is already black
|
||||
*/
|
||||
uint32_t IRAM_ATTR color_fade(uint32_t c1, uint8_t amount, bool video) {
|
||||
if (c1 == 0 || amount == 0) return 0; // black or no change
|
||||
if (amount == 255) return c1;
|
||||
if (c1 == BLACK || amount == 0) return 0; // black or full fade
|
||||
if (amount == 255) return c1; // no change
|
||||
uint32_t addRemains = 0;
|
||||
|
||||
if (!video) amount++; // add one for correct scaling using bitshifts
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
* Readability defines and their associated numerical values + compile-time constants
|
||||
*/
|
||||
|
||||
constexpr size_t FASTLED_PALETTE_COUNT = 7; // = sizeof(fastledPalettes) / sizeof(fastledPalettes[0]);
|
||||
constexpr size_t GRADIENT_PALETTE_COUNT = 59; // = sizeof(gGradientPalettes) / sizeof(gGradientPalettes[0]);
|
||||
constexpr size_t DYNAMIC_PALETTE_COUNT = 5; // 1-5 are dynamic palettes (1=random,2=primary,3=primary+secondary,4=primary+secondary+tertiary,5=primary+secondary(+tertiary if not black)
|
||||
constexpr size_t FASTLED_PALETTE_COUNT = 7; // 6-12 = sizeof(fastledPalettes) / sizeof(fastledPalettes[0]);
|
||||
constexpr size_t GRADIENT_PALETTE_COUNT = 59; // 13-72 = sizeof(gGradientPalettes) / sizeof(gGradientPalettes[0]);
|
||||
constexpr size_t DYNAMIC_PALETTE_COUNT = 6; // 0- 5 = dynamic palettes (0=default(virtual),1=random,2=primary,3=primary+secondary,4=primary+secondary+tertiary,5=primary+secondary(+tertiary if not black)
|
||||
constexpr size_t FIXED_PALETTE_COUNT = DYNAMIC_PALETTE_COUNT + FASTLED_PALETTE_COUNT + GRADIENT_PALETTE_COUNT; // total number of fixed palettes
|
||||
#ifndef ESP8266
|
||||
#define WLED_MAX_CUSTOM_PALETTES (255 - FIXED_PALETTE_COUNT) // allow up to 255 total palettes, user is warned about stability issues when adding more than 10
|
||||
|
||||
@@ -122,6 +122,20 @@
|
||||
d.Sf.data.value = '';
|
||||
e.preventDefault();
|
||||
if (!pinsOK()) {e.stopPropagation();return false;} // Prevent form submission and contact with server
|
||||
// validate HUB75 panel config
|
||||
let LTs = d.Sf.querySelectorAll("#mLC select[name^=LT]");
|
||||
for (let i=0; i<LTs.length; i++) {
|
||||
let n = chrID(i);
|
||||
let t = parseInt(LTs[i].value);
|
||||
if (isHub75(t)) {
|
||||
let p = parseInt(d.Sf["L2"+n].value)||1, r = parseInt(d.Sf["L3"+n].value)||1, c = parseInt(d.Sf["L4"+n].value)||1, h = parseInt(d.Sf["L1"+n].value)||1;
|
||||
if (r*c !== p) {alert(`HUB75 error: panels≠rows×cols`); e.stopPropagation(); return false;}
|
||||
if (h >= 64 && p > 1) {alert(`HUB75 error: height >= 64, only single panel allowed`); e.stopPropagation(); return false;}
|
||||
if(isS3()) {
|
||||
alert("HUB75 changes require a reboot"); // TODO: only throw this if panel config changed?
|
||||
}
|
||||
}
|
||||
};
|
||||
if (bquot > 200) {var msg = "Too many LEDs! Can't handle that!"; alert(msg); e.stopPropagation(); return false;}
|
||||
else {
|
||||
if (bquot > 80) {var msg = "Memory usage is high, reboot recommended!\n\rSet transitions to 0 to save memory.";
|
||||
@@ -256,17 +270,19 @@
|
||||
p0d = "IP address:";
|
||||
break;
|
||||
case 'V': // virtual/non-GPIO based
|
||||
p0d = "Config:"
|
||||
p0d = "Config:";
|
||||
break;
|
||||
case 'H': // HUB75
|
||||
p0d = "Panel size (width x height), Panel count:"
|
||||
p0d = "Panel (width x height):";
|
||||
gId("p2d"+n).innerHTML = "<br>No. of Panels:";
|
||||
gId("p3d"+n).innerText = "rows x cols:";
|
||||
break;
|
||||
}
|
||||
gId("p0d"+n).innerText = p0d;
|
||||
gId("p1d"+n).innerText = p1d;
|
||||
gId("off"+n).innerText = off;
|
||||
// secondary pins show/hide (type string length is equivalent to number of pins used; except for network and on/off)
|
||||
let pins = Math.max(gT(t).t.length,1) + 3*isNet(t) + 2*isHub75(t); // fixes network pins to 4
|
||||
let pins = Math.max(gT(t).t.length,1) + 3*isNet(t) + 4*isHub75(t); // fixes network pins to 4
|
||||
for (let p=1; p<5; p++) {
|
||||
var LK = d.Sf["L"+p+n];
|
||||
if (!LK) continue;
|
||||
@@ -330,29 +346,33 @@
|
||||
}
|
||||
// do we have a led count field
|
||||
if (nm=="LC") {
|
||||
if (!isHub75(t)) {
|
||||
LC.max = isAna(t) ? 1 : (isDig(t) ? maxPB : 16384); // set max value
|
||||
} else {
|
||||
LC.min = undefined;
|
||||
LC.max = undefined;
|
||||
}
|
||||
let c = parseInt(LC.value,10); //get LED count
|
||||
if (!customStarts || !startsDirty[toNum(n)]) gId("ls"+n).value = sLC; //update start value
|
||||
gId("ls"+n).disabled = !customStarts; //enable/disable field editing
|
||||
if (c) {
|
||||
let s = parseInt(gId("ls"+n).value); //start value
|
||||
if (s+c > sLC) sLC = s+c; //update total count
|
||||
if (c > maxLC) maxLC = c; //max per output
|
||||
if (!isVir(t)) sPC += c; //virtual out busses do not count towards physical LEDs
|
||||
if (isDig(t)) {
|
||||
if (c > maxLC) maxLC = c; //max per output
|
||||
sDI += c; // summarize digital LED count
|
||||
let maPL = parseInt(d.Sf["LA"+n].value);
|
||||
if (maPL == 255) maPL = 12; // wacky WS2815 mode (255 == 12mA per LED)
|
||||
if (maPL == 255) maPL = 12;
|
||||
busMA += maPL*c; // summarize maximum bus current (calculated)
|
||||
}
|
||||
} // increase led count
|
||||
return;
|
||||
}
|
||||
// do we have led pins for digital leds
|
||||
if (nm=="L0" || nm=="L1") {
|
||||
if (!isHub75(t)) {
|
||||
d.Sf["LC"+n].max = maxPB; // update max led count value
|
||||
}
|
||||
else {
|
||||
d.Sf["LC"+n].min = undefined;
|
||||
d.Sf["LC"+n].max = undefined;
|
||||
}
|
||||
}
|
||||
// ignore IP address (stored in pins for virtual busses)
|
||||
if (nm.search(/^L[0-3]/) == 0) { // pin fields
|
||||
if (isVir(t)) {
|
||||
@@ -372,10 +392,11 @@
|
||||
LC.style.color="#fff";
|
||||
return; // do not check conflicts
|
||||
}
|
||||
else if (isHub75(t) && nm=="L2") {
|
||||
// Chain length aka Panel Count
|
||||
else if (isHub75(t) && (nm=="L2" || nm=="L3" || nm=="L4")) {
|
||||
// chain length aka panel count (L2), cols(L3), rows(L4)
|
||||
LC.max = 4;
|
||||
LC.min = 1;
|
||||
if (LC.value === "") LC.value = 1; // default to 1
|
||||
LC.style.color="#fff";
|
||||
return; // do not check conflicts
|
||||
}
|
||||
|
||||
@@ -945,26 +945,25 @@ void serializePalettes(JsonObject root, int page)
|
||||
{
|
||||
byte tcp[72];
|
||||
#ifdef ESP8266
|
||||
int itemPerPage = 5;
|
||||
constexpr int itemPerPage = 5;
|
||||
#else
|
||||
int itemPerPage = 8;
|
||||
constexpr int itemPerPage = 8;
|
||||
#endif
|
||||
|
||||
int customPalettesCount = customPalettes.size();
|
||||
int palettesCount = getPaletteCount() - customPalettesCount; // palettesCount is number of palettes, not palette index
|
||||
const int customPalettesCount = customPalettes.size();
|
||||
const int palettesCount = FIXED_PALETTE_COUNT; // palettesCount is number of palettes, not palette index
|
||||
|
||||
int maxPage = (palettesCount + customPalettesCount -1) / itemPerPage;
|
||||
const int maxPage = (palettesCount + customPalettesCount) / itemPerPage;
|
||||
if (page > maxPage) page = maxPage;
|
||||
|
||||
int start = itemPerPage * page;
|
||||
int end = start + itemPerPage;
|
||||
if (end > palettesCount + customPalettesCount) end = palettesCount + customPalettesCount;
|
||||
const int start = itemPerPage * page;
|
||||
int end = min(start + itemPerPage, palettesCount + customPalettesCount);
|
||||
|
||||
root[F("m")] = maxPage; // inform caller how many pages there are
|
||||
JsonObject palettes = root.createNestedObject("p");
|
||||
|
||||
for (int i = start; i <= end; i++) {
|
||||
JsonArray curPalette = palettes.createNestedArray(String(i<=palettesCount ? i : 255 - (i - (palettesCount + 1))));
|
||||
for (int i = start; i < end; i++) {
|
||||
JsonArray curPalette = palettes.createNestedArray(String(i >= palettesCount ? 255 - i + palettesCount : i));
|
||||
switch (i) {
|
||||
case 0: //default palette
|
||||
setPaletteColors(curPalette, PartyColors_p);
|
||||
@@ -993,12 +992,12 @@ void serializePalettes(JsonObject root, int page)
|
||||
curPalette.add("c1");
|
||||
break;
|
||||
default:
|
||||
if (i > palettesCount)
|
||||
setPaletteColors(curPalette, customPalettes[i - (palettesCount + 1)]);
|
||||
else if (i < 13) // palette 6 - 12, fastled palettes
|
||||
setPaletteColors(curPalette, *fastledPalettes[i-6]);
|
||||
if (i >= palettesCount) // custom palettes
|
||||
setPaletteColors(curPalette, customPalettes[i - palettesCount]);
|
||||
else if (i < DYNAMIC_PALETTE_COUNT + FASTLED_PALETTE_COUNT) // palette 6 - 12, fastled palettes
|
||||
setPaletteColors(curPalette, *fastledPalettes[i - DYNAMIC_PALETTE_COUNT]);
|
||||
else {
|
||||
memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[i - 13])), 72);
|
||||
memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[i - (DYNAMIC_PALETTE_COUNT + FASTLED_PALETTE_COUNT)])), sizeof(tcp));
|
||||
setPaletteColors(curPalette, tcp);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -89,7 +89,8 @@ int16_t loadPlaylist(JsonObject playlistObj, byte presetId) {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
for (int i = it; i < playlistLen; i++) playlistEntries[i].dur = playlistEntries[it -1].dur;
|
||||
if (it > 0) // should never happen but just in case
|
||||
for (int i = it; i < playlistLen; i++) playlistEntries[i].dur = playlistEntries[it -1].dur;
|
||||
|
||||
it = 0;
|
||||
JsonArray tr = playlistObj[F("transition")];
|
||||
|
||||
@@ -279,7 +279,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
int offset = i < 10 ? '0' : 'A' - 10;
|
||||
char bt[4] = "BT"; bt[2] = offset+i; bt[3] = 0; // button pin (use A,B,C,... if WLED_MAX_BUTTONS>10)
|
||||
char be[4] = "BE"; be[2] = offset+i; be[3] = 0; // button type (use A,B,C,... if WLED_MAX_BUTTONS>10)
|
||||
int hw_btn_pin = request->arg(bt).toInt();
|
||||
int hw_btn_pin = request->hasArg(bt) ? request->arg(bt).toInt() : -1;
|
||||
if (i >= buttons.size()) buttons.emplace_back(hw_btn_pin, request->arg(be).toInt()); // add button to vector
|
||||
else {
|
||||
buttons[i].pin = hw_btn_pin;
|
||||
|
||||
@@ -334,8 +334,8 @@ static void parseNotifyPacket(const uint8_t *udpIn) {
|
||||
selseg.custom2 = udpIn[30+ofs];
|
||||
selseg.custom3 = udpIn[31+ofs] & 0x1F;
|
||||
selseg.check1 = (udpIn[31+ofs]>>5) & 0x1;
|
||||
selseg.check1 = (udpIn[31+ofs]>>6) & 0x1;
|
||||
selseg.check1 = (udpIn[31+ofs]>>7) & 0x1;
|
||||
selseg.check2 = (udpIn[31+ofs]>>6) & 0x1;
|
||||
selseg.check3 = (udpIn[31+ofs]>>7) & 0x1;
|
||||
}
|
||||
}
|
||||
if (receiveSegmentBounds) {
|
||||
|
||||
@@ -213,7 +213,7 @@ void releaseJSONBufferLock()
|
||||
|
||||
|
||||
// extracts effect mode (or palette) name from names serialized string
|
||||
// caller must provide large enough buffer for name (including SR extensions)!
|
||||
// caller must provide large enough buffer for name (including SR extensions)! maxLen is (buffersize - 1)
|
||||
uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen)
|
||||
{
|
||||
if (src == JSON_mode_names || src == nullptr) {
|
||||
@@ -235,7 +235,7 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe
|
||||
|
||||
if (src == JSON_palette_names && mode > 255-customPalettes.size()) {
|
||||
snprintf_P(dest, maxLen, PSTR("~ Custom %d ~"), 255-mode);
|
||||
dest[maxLen-1] = '\0';
|
||||
dest[maxLen] = '\0';
|
||||
return strlen(dest);
|
||||
}
|
||||
|
||||
@@ -336,7 +336,7 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL
|
||||
case 0: strncpy_P(dest, PSTR("FX Speed"), maxLen); break;
|
||||
case 1: strncpy_P(dest, PSTR("FX Intensity"), maxLen); break;
|
||||
}
|
||||
dest[maxLen] = '\0'; // strncpy does not necessarily null terminate string
|
||||
dest[maxLen-1] = '\0'; // strncpy does not necessarily null terminate string
|
||||
}
|
||||
}
|
||||
return strlen(dest);
|
||||
|
||||
Reference in New Issue
Block a user