Merge branch 'master' into dev

(mostly ignored index.js)
This commit is contained in:
Blaž Kristan 2021-09-22 06:58:49 +02:00
commit 6db2240f8a
19 changed files with 909 additions and 854 deletions

View File

@ -2,6 +2,19 @@
### Builds after release 0.12.0
#### Build 2109220
- Version bump to 0.13.0-b3 "Toki"
- Added segment names (PR #2184)
- Improved Police and other effects (PR #2184)
- Reverted PR #1902 (Live color correction - will be implemented as usermod) (PR #2175)
- Added transitions for segment on/off
- Improved number of sparks/stars in Fireworks effect with low number of segments
- Fixed segment name edit pencil disappearing with request
- Fixed color transition active even if the segment is off
- Disallowed file upload with OTA lock active
- Fixed analog invert option missing (PR #2219)
#### Build 2109100
- Added an auto create segments per bus setting

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "wled",
"version": "0.13.0-bl2",
"version": "0.13.0-bl3",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -2629,7 +2629,7 @@ typedef struct Spark {
*/
uint16_t WS2812FX::mode_popcorn(void) {
//allocate segment data
uint16_t maxNumPopcorn = 22; // max 22 on 16 segment ESP8266
uint16_t maxNumPopcorn = 21; // max 21 on 16 segment ESP8266
uint16_t dataSize = sizeof(spark) * maxNumPopcorn;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -2688,7 +2688,7 @@ uint16_t WS2812FX::candle(bool multi)
if (multi)
{
//allocate segment data
uint16_t dataSize = (SEGLEN -1) *3; // max length of segment on 16 segment ESP8266 is 75 pixels
uint16_t dataSize = (SEGLEN -1) *3; //max. 1365 pixels (ESP8266)
if (!SEGENV.allocateData(dataSize)) return candle(false); //allocation failed
}
@ -2776,13 +2776,11 @@ uint16_t WS2812FX::mode_candle_multi()
/ Speed sets frequency of new starbursts, intensity is the intensity of the burst
*/
#ifdef ESP8266
#define STARBURST_MAX_FRAG 4
#define STARBURST_MAX_STARS 6
#define STARBURST_MAX_FRAG 8 //52 bytes / star
#else
#define STARBURST_MAX_FRAG 10
#define STARBURST_MAX_STARS 11
#define STARBURST_MAX_FRAG 10 //60 bytes / star
#endif
//each needs 18+STARBURST_MAX_FRAG*4 bytes
//each needs 20+STARBURST_MAX_FRAG*4 bytes
typedef struct particle {
CRGB color;
uint32_t birth =0;
@ -2793,7 +2791,14 @@ typedef struct particle {
} star;
uint16_t WS2812FX::mode_starburst(void) {
uint8_t numStars = min(1 + (SEGLEN >> 3), STARBURST_MAX_STARS); // 11 * 58 * 32 = 19k (ESP32), 6 * 34 * 16 = 3.2k (ESP8266)
uint16_t maxData = FAIR_DATA_PER_SEG; //ESP8266: 256 ESP32: 640
uint8_t segs = getActiveSegmentsNum();
if (segs <= (MAX_NUM_SEGMENTS /2)) maxData *= 2; //ESP8266: 512 if <= 8 segs ESP32: 1280 if <= 16 segs
if (segs <= (MAX_NUM_SEGMENTS /4)) maxData *= 2; //ESP8266: 1024 if <= 4 segs ESP32: 2560 if <= 8 segs
uint16_t maxStars = maxData / sizeof(star); //ESP8266: max. 4/9/19 stars/seg, ESP32: max. 10/21/42 stars/seg
uint8_t numStars = 1 + (SEGLEN >> 3);
if (numStars > maxStars) numStars = maxStars;
uint16_t dataSize = sizeof(star) * numStars;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -2895,24 +2900,29 @@ uint16_t WS2812FX::mode_starburst(void) {
return FRAMETIME;
}
#undef STARBURST_MAX_FRAG
#undef STARBURST_MAX_STARS
/*
* Exploding fireworks effect
* adapted from: http://www.anirama.com/1000leds/1d-fireworks/
*/
#ifdef ESP8266
#define MAX_SPARKS 20 // number of fragments (11 bytes per fragment)
#else
#define MAX_SPARKS 58 // number of fragments
#endif
uint16_t WS2812FX::mode_exploding_fireworks(void)
{
//allocate segment data
uint16_t numSparks = min(2 + (SEGLEN >> 1), MAX_SPARKS); // max 58 for 32 segment ESP32, 20 for 16 segment ESP8266
uint16_t maxData = FAIR_DATA_PER_SEG; //ESP8266: 256 ESP32: 640
uint8_t segs = getActiveSegmentsNum();
if (segs <= (MAX_NUM_SEGMENTS /2)) maxData *= 2; //ESP8266: 512 if <= 8 segs ESP32: 1280 if <= 16 segs
if (segs <= (MAX_NUM_SEGMENTS /4)) maxData *= 2; //ESP8266: 1024 if <= 4 segs ESP32: 2560 if <= 8 segs
int maxSparks = maxData / sizeof(spark); //ESP8266: max. 21/42/85 sparks/seg, ESP32: max. 53/106/213 sparks/seg
uint16_t numSparks = min(2 + (SEGLEN >> 1), maxSparks);
uint16_t dataSize = sizeof(spark) * numSparks;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
if (dataSize != SEGENV.aux1) { //reset to flare if sparks were reallocated
SEGENV.aux0 = 0;
SEGENV.aux1 = dataSize;
}
fill(BLACK);
bool actuallyReverse = SEGMENT.getOption(SEG_OPTION_REVERSED);

View File

@ -57,7 +57,7 @@
/* How many color transitions can run at once */
#define MAX_NUM_TRANSITIONS 8
/* How much data bytes all segments combined may allocate */
#define MAX_SEGMENT_DATA 3584
#define MAX_SEGMENT_DATA 4096
#else
#ifndef MAX_NUM_SEGMENTS
#define MAX_NUM_SEGMENTS 32
@ -66,6 +66,10 @@
#define MAX_SEGMENT_DATA 20480
#endif
/* How much data bytes each segment should max allocate to leave enough space for other segments,
assuming each segment uses the same amount of data. 256 for ESP8266, 640 for ESP32. */
#define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / MAX_NUM_SEGMENTS)
#define LED_SKIP_AMOUNT 1
#define MIN_SHOW_DELAY 15
@ -243,7 +247,7 @@ class WS2812FX {
// segment parameters
public:
typedef struct Segment { // 25 (28 in memory?) bytes
typedef struct Segment { // 29 (32 in memory?) bytes
uint16_t start;
uint16_t stop; //segment invalid if stop == 0
uint16_t offset;
@ -274,22 +278,24 @@ class WS2812FX {
}*/
void setOption(uint8_t n, bool val, uint8_t segn = 255)
{
//bool prevOn = false;
//if (n == SEG_OPTION_ON) prevOn = getOption(SEG_OPTION_ON);
bool prevOn = false;
if (n == SEG_OPTION_ON) {
prevOn = getOption(SEG_OPTION_ON);
if (!val && prevOn) { //fade off
ColorTransition::startTransition(opacity, colors[0], instance->_transitionDur, segn, 0);
}
}
if (val) {
options |= 0x01 << n;
} else
{
options &= ~(0x01 << n);
}
//transitions on segment on/off don't work correctly at this point
/*if (n == SEG_OPTION_ON && segn < MAX_NUM_SEGMENTS && getOption(SEG_OPTION_ON) != prevOn) {
if (getOption(SEG_OPTION_ON)) {
ColorTransition::startTransition(0, colors[0], instance->_transitionDur, segn, 0);
} else {
ColorTransition::startTransition(opacity, colors[0], instance->_transitionDur, segn, 0);
}
}*/
if (n == SEG_OPTION_ON && val && !prevOn) { //fade on
ColorTransition::startTransition(0, colors[0], instance->_transitionDur, segn, 0);
}
}
bool getOption(uint8_t n)
{
@ -409,6 +415,7 @@ class WS2812FX {
static void startTransition(uint8_t oldBri, uint32_t oldCol, uint16_t dur, uint8_t segn, uint8_t slot) {
if (segn >= MAX_NUM_SEGMENTS || slot >= NUM_COLORS || dur == 0) return;
if (instance->_brightness == 0) return; //do not need transitions if master bri is off
if (!instance->_segments[segn].getOption(SEG_OPTION_ON)) return; //not if segment is off either
uint8_t tIndex = 0xFF; //none found
uint16_t tProgression = 0;
uint8_t s = segn + (slot << 6); //merge slot and segment into one byte
@ -437,7 +444,8 @@ class WS2812FX {
ColorTransition& t = instance->transitions[tIndex];
if (t.segment == s) //this is an active transition on the same segment+color
{
t.briOld = t.currentBri();
bool wasTurningOff = (oldBri == 0);
t.briOld = t.currentBri(wasTurningOff);
t.colorOld = t.currentColor(oldCol);
} else {
t.briOld = oldBri;
@ -469,10 +477,11 @@ class WS2812FX {
uint32_t currentColor(uint32_t colorNew) {
return instance->color_blend(colorOld, colorNew, progress(true), true);
}
uint8_t currentBri() {
uint8_t currentBri(bool turningOff = false) {
uint8_t segn = segment & 0x3F;
if (segn >= MAX_NUM_SEGMENTS) return 0;
uint8_t briNew = instance->_segments[segn].opacity;
if (!instance->_segments[segn].getOption(SEG_OPTION_ON) || turningOff) briNew = 0;
uint32_t prog = progress() + 1;
return ((briNew * prog) + (briOld * (0x10000 - prog))) >> 16;
}
@ -656,6 +665,7 @@ class WS2812FX {
getModeCount(void),
getPaletteCount(void),
getMaxSegments(void),
getActiveSegmentsNum(void),
//getFirstSelectedSegment(void),
getMainSegmentId(void),
gamma8(uint8_t),

View File

@ -510,6 +510,15 @@ uint8_t WS2812FX::getMainSegmentId(void) {
return 0;
}
uint8_t WS2812FX::getActiveSegmentsNum(void) {
uint8_t c = 0;
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
{
if (_segments[i].isActive()) c++;
}
return c;
}
uint32_t WS2812FX::getColor(void) {
return _segments[getMainSegmentId()].colors[0];
}
@ -548,15 +557,6 @@ uint32_t WS2812FX::getLastShow(void) {
return _lastShow;
}
// there is no longer any need for these two
//uint8_t WS2812FX::getColorOrder(void) {
// return COL_ORDER_GRB;
//}
//
//void WS2812FX::setColorOrder(uint8_t co) {
// //bus->SetColorOrder(co);
//}
void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing) {
if (n >= MAX_NUM_SEGMENTS) return;
Segment& seg = _segments[n];
@ -1035,9 +1035,10 @@ uint32_t WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8
//load custom mapping table from JSON file
void WS2812FX::deserializeMap(uint8_t n) {
String fileName = String(F("/ledmap"));
if (n) fileName += String(n);
fileName += String(F(".json"));
char fileName[32];
strcpy_P(fileName, PSTR("/ledmap"));
if (n) sprintf(fileName +7, "%d", n);
strcat(fileName, ".json");
bool isFile = WLED_FS.exists(fileName);
if (!isFile) {
@ -1054,7 +1055,7 @@ void WS2812FX::deserializeMap(uint8_t n) {
DEBUG_PRINT(F("Reading LED map from "));
DEBUG_PRINTLN(fileName);
if (!readObjectFromFile(fileName.c_str(), nullptr, &doc)) return; //if file does not exist just exit
if (!readObjectFromFile(fileName, nullptr, &doc)) return; //if file does not exist just exit
// erase old custom ledmap
if (customMappingTable != nullptr) {

View File

@ -110,7 +110,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (length==0 || length+lC > MAX_LEDS) continue; // zero length or we reached max. number of LEDs, just stop
uint8_t colorOrder = (int)elm[F("order")];
uint8_t skipFirst = elm[F("skip")];
uint16_t start = elm[F("start")] | 0;
uint16_t start = elm["start"] | 0;
if (start > lC+length) continue; // something is very wrong :)
uint8_t ledType = elm["type"] | TYPE_WS2812_RGB;
bool reversed = elm["rev"];
@ -411,7 +411,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
JsonObject dmx = doc["dmx"];
CJSON(DMXChannels, dmx[F("chan")]);
CJSON(DMXGap,dmx[F("gap")]);
CJSON(DMXStart, dmx[F("start")]);
CJSON(DMXStart, dmx["start"]);
CJSON(DMXStartLED,dmx[F("start-led")]);
JsonArray dmx_fixmap = dmx[F("fixmap")];
@ -549,7 +549,7 @@ void serializeConfig() {
Bus *bus = busses.getBus(s);
if (!bus || bus->getLength()==0) break;
JsonObject ins = hw_led_ins.createNestedObject();
ins[F("start")] = bus->getStart();
ins["start"] = bus->getStart();
ins[F("len")] = bus->getLength();
JsonArray ins_pin = ins.createNestedArray("pin");
uint8_t pins[5];
@ -753,7 +753,7 @@ void serializeConfig() {
JsonObject dmx = doc.createNestedObject("dmx");
dmx[F("chan")] = DMXChannels;
dmx[F("gap")] = DMXGap;
dmx[F("start")] = DMXStart;
dmx["start"] = DMXStart;
dmx[F("start-led")] = DMXStartLED;
JsonArray dmx_fixmap = dmx.createNestedArray(F("fixmap"));

File diff suppressed because one or more lines are too long

View File

@ -1568,6 +1568,12 @@ function tglSegn(s)
(window.getComputedStyle(d.gId(`seg${s}t`)).display === "none") ? "inline":"none";
}
function tglSegn(s)
{
d.getElementById(`seg${s}t`).style.display =
(window.getComputedStyle(d.getElementById(`seg${s}t`)).display === "none") ? "inline":"none";
}
function selSegEx(s)
{
var obj = {"seg":[]};

View File

@ -330,7 +330,7 @@ ${i+1}:
}
function uploadFile(name) {
var req = new XMLHttpRequest();
req.addEventListener('load', function(){showToast(this.responseText)});
req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400)});
req.addEventListener('error', function(e){showToast(e.stack,true);});
req.open("POST", "/upload");
var formData = new FormData();

View File

@ -37,7 +37,7 @@
}
function uploadFile(fO,name) {
var req = new XMLHttpRequest();
req.addEventListener('load', function(){showToast(this.responseText)});
req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400)});
req.addEventListener('error', function(e){showToast(e.stack,true);});
req.open("POST", "/upload");
var formData = new FormData();

View File

@ -202,7 +202,7 @@
}
function uploadFile(fO,name) {
var req = new XMLHttpRequest();
req.addEventListener('load', function(){showToast(this.responseText)});
req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400)});
req.addEventListener('error', function(e){showToast(e.stack,true);});
req.open("POST", "/upload");
var formData = new FormData();

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
//WS2812FX::Segment prev;
//prev = seg; //make a backup so we can tell if something changed
uint16_t start = elem[F("start")] | seg.start;
uint16_t start = elem["start"] | seg.start;
int stop = elem["stop"] | -1;
if (stop < 0) {
uint16_t len = elem[F("len")];
@ -70,7 +70,9 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
seg.setOption(SEG_OPTION_ON, 1, id);
}
seg.setOption(SEG_OPTION_ON, elem["on"] | seg.getOption(SEG_OPTION_ON), id);
bool on = elem["on"] | seg.getOption(SEG_OPTION_ON);
if (elem["on"].is<const char*>() && elem["on"].as<const char*>()[0] == 't') on = !on;
seg.setOption(SEG_OPTION_ON, on, id);
JsonArray colarr = elem["col"];
if (!colarr.isNull())
@ -358,7 +360,7 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
{
root["id"] = id;
if (segmentBounds) {
root[F("start")] = seg.start;
root["start"] = seg.start;
root["stop"] = seg.stop;
}
if (!forPreset) root[F("len")] = seg.stop - seg.start;

View File

@ -2,14 +2,18 @@
IPAddress NetworkClass::localIP()
{
IPAddress localIP;
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
if (ETH.localIP()[0] != 0) {
return ETH.localIP();
localIP = ETH.localIP();
if (localIP[0] != 0) {
return localIP;
}
#endif
if (WiFi.localIP()[0] != 0) {
return WiFi.localIP();
localIP = WiFi.localIP();
if (localIP[0] != 0) {
return localIP;
}
return INADDR_NONE;
}

View File

@ -124,6 +124,8 @@ void sendTPM2Ack() {
void handleNotifications()
{
IPAddress localIP;
//send second notification if enabled
if(udpConnected && notificationTwoRequired && millis()-notificationSentTime > 250){
notify(notificationSentCallMode,true);
@ -179,9 +181,10 @@ void handleNotifications()
if (!(receiveNotifications || receiveDirect)) return;
localIP = Network.localIP();
//notifier and UDP realtime
if (!packetSize || packetSize > UDP_IN_MAXSIZE) return;
if (!isSupp && notifierUdp.remoteIP() == Network.localIP()) return; //don't process broadcasts we send ourselves
if (!isSupp && notifierUdp.remoteIP() == localIP) return; //don't process broadcasts we send ourselves
uint8_t udpIn[packetSize +1];
uint16_t len;
@ -190,7 +193,7 @@ void handleNotifications()
// WLED nodes info notifications
if (isSupp && udpIn[0] == 255 && udpIn[1] == 1 && len >= 40) {
if (!nodeListEnabled || notifier2Udp.remoteIP() == Network.localIP()) return;
if (!nodeListEnabled || notifier2Udp.remoteIP() == localIP) return;
uint8_t unit = udpIn[39];
NodesMap::iterator it = Nodes.find(unit);

View File

@ -686,13 +686,14 @@ void WLED::initConnection()
void WLED::initInterfaces()
{
IPAddress ipAddress = Network.localIP();
DEBUG_PRINTLN(F("Init STA interfaces"));
#ifndef WLED_DISABLE_HUESYNC
if (hueIP[0] == 0) {
hueIP[0] = Network.localIP()[0];
hueIP[1] = Network.localIP()[1];
hueIP[2] = Network.localIP()[2];
hueIP[0] = ipAddress[0];
hueIP[1] = ipAddress[1];
hueIP[2] = ipAddress[2];
}
#endif

View File

@ -3,12 +3,12 @@
/*
Main sketch, global variable declarations
@title WLED project sketch
@version 0.13.0-bl2
@version 0.13.0-bl3
@author Christian Schwinne
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2109191
#define VERSION 2109220
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG

View File

@ -19,6 +19,10 @@ bool isIp(String str) {
}
void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final){
if (otaLock) {
if (final) request->send(500, "text/plain", F("Please unlock OTA in security settings!"));
return;
}
if (!index) {
request->_tempFile = WLED_FS.open(filename, "w");
DEBUG_PRINT("Uploading ");