Merge branch '4ld-font' into color-order-mapping

This commit is contained in:
Blaz Kristan 2022-01-22 23:20:47 +01:00
commit 6f7f67df5a
18 changed files with 2259 additions and 1887 deletions

View File

@ -45,6 +45,8 @@ class MultiRelay : public Usermod {
// status of initialisation // status of initialisation
bool initDone = false; bool initDone = false;
bool HAautodiscovery = false;
uint16_t periodicBroadcastSec = 60; uint16_t periodicBroadcastSec = 60;
unsigned long lastBroadcast = 0; unsigned long lastBroadcast = 0;
@ -57,13 +59,14 @@ class MultiRelay : public Usermod {
static const char _external[]; static const char _external[];
static const char _button[]; static const char _button[];
static const char _broadcast[]; static const char _broadcast[];
static const char _HAautodiscovery[];
void publishMqtt(const char* state, int relay) { void publishMqtt(int relay) {
//Check if MQTT Connected, otherwise it will crash the 8266 //Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED){ if (WLED_MQTT_CONNECTED){
char subuf[64]; char subuf[64];
sprintf_P(subuf, PSTR("%s/relay/%d"), mqttDeviceTopic, relay); sprintf_P(subuf, PSTR("%s/relay/%d"), mqttDeviceTopic, relay);
mqtt->publish(subuf, 0, false, state); mqtt->publish(subuf, 0, false, _relay[relay].state ? "on" : "off");
} }
} }
@ -78,7 +81,7 @@ class MultiRelay : public Usermod {
if (!_relay[i].external) toggleRelay(i); if (!_relay[i].external) toggleRelay(i);
_relay[i].active = false; _relay[i].active = false;
} else if (periodicBroadcastSec && now - lastBroadcast > (periodicBroadcastSec*1000)) { } else if (periodicBroadcastSec && now - lastBroadcast > (periodicBroadcastSec*1000)) {
if (_relay[i].pin>=0) publishMqtt(_relay[i].state ? "on" : "off", i); if (_relay[i].pin>=0) publishMqtt(i);
} }
activeRelays = activeRelays || _relay[i].active; activeRelays = activeRelays || _relay[i].active;
} }
@ -112,7 +115,7 @@ class MultiRelay : public Usermod {
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
int value = getValue(p->value(), ',', i); int value = getValue(p->value(), ',', i);
if (value==-1) { if (value==-1) {
error = F("There must be as much arugments as relays"); error = F("There must be as many arguments as relays");
} else { } else {
// Switch // Switch
if (_relay[i].external) switchRelay(i, (bool)value); if (_relay[i].external) switchRelay(i, (bool)value);
@ -125,7 +128,7 @@ class MultiRelay : public Usermod {
for (int i=0;i<MULTI_RELAY_MAX_RELAYS;i++) { for (int i=0;i<MULTI_RELAY_MAX_RELAYS;i++) {
int value = getValue(p->value(), ',', i); int value = getValue(p->value(), ',', i);
if (value==-1) { if (value==-1) {
error = F("There must be as mutch arugments as relays"); error = F("There must be as many arguments as relays");
} else { } else {
// Toggle // Toggle
if (value && _relay[i].external) toggleRelay(i); if (value && _relay[i].external) toggleRelay(i);
@ -206,7 +209,7 @@ class MultiRelay : public Usermod {
_relay[relay].state = mode; _relay[relay].state = mode;
pinMode(_relay[relay].pin, OUTPUT); pinMode(_relay[relay].pin, OUTPUT);
digitalWrite(_relay[relay].pin, mode ? !_relay[relay].mode : _relay[relay].mode); digitalWrite(_relay[relay].pin, mode ? !_relay[relay].mode : _relay[relay].mode);
publishMqtt(mode ? "on" : "off", relay); publishMqtt(relay);
} }
/** /**
@ -259,6 +262,50 @@ class MultiRelay : public Usermod {
strcpy(subuf, mqttDeviceTopic); strcpy(subuf, mqttDeviceTopic);
strcat_P(subuf, PSTR("/relay/#")); strcat_P(subuf, PSTR("/relay/#"));
mqtt->subscribe(subuf, 0); mqtt->subscribe(subuf, 0);
if (HAautodiscovery) publishHomeAssistantAutodiscovery();
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].pin<0) continue;
publishMqtt(i); //publish current state
}
}
}
void publishHomeAssistantAutodiscovery() {
for (uint8_t i = 0; i < MULTI_RELAY_MAX_RELAYS; i++) {
char uid[16], json_str[1024], buf[128];
size_t payload_size;
sprintf_P(uid, PSTR("%s_sw%d"), escapedMac.c_str(), i);
if (_relay[i].pin >= 0 && _relay[i].external) {
StaticJsonDocument<1024> json;
sprintf_P(buf, PSTR("%s Switch %d"), serverDescription, i); //max length: 33 + 8 + 3 = 44
json[F("name")] = buf;
sprintf_P(buf, PSTR("%s/relay/%d"), mqttDeviceTopic, i); //max length: 33 + 7 + 3 = 43
json["~"] = buf;
strcat_P(buf, PSTR("/command"));
mqtt->subscribe(buf, 0);
json[F("stat_t")] = "~";
json[F("cmd_t")] = F("~/command");
json[F("pl_off")] = F("off");
json[F("pl_on")] = F("on");
json[F("uniq_id")] = uid;
strcpy(buf, mqttDeviceTopic); //max length: 33 + 7 = 40
strcat_P(buf, PSTR("/status"));
json[F("avty_t")] = buf;
json[F("pl_avail")] = F("online");
json[F("pl_not_avail")] = F("offline");
//TODO: dev
payload_size = serializeJson(json, json_str);
} else {
//Unpublish disabled or internal relays
json_str[0] = 0;
payload_size = 0;
}
sprintf_P(buf, PSTR("homeassistant/switch/%s/config"), uid);
mqtt->publish(buf, 0, true, json_str, payload_size);
} }
} }
@ -320,13 +367,18 @@ class MultiRelay : public Usermod {
*/ */
bool handleButton(uint8_t b) { bool handleButton(uint8_t b) {
yield(); yield();
if (buttonType[b] == BTN_TYPE_NONE || buttonType[b] == BTN_TYPE_RESERVED || buttonType[b] == BTN_TYPE_PIR_SENSOR || buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) { if (!enabled
|| buttonType[b] == BTN_TYPE_NONE
|| buttonType[b] == BTN_TYPE_RESERVED
|| buttonType[b] == BTN_TYPE_PIR_SENSOR
|| buttonType[b] == BTN_TYPE_ANALOG
|| buttonType[b] == BTN_TYPE_ANALOG_INVERTED) {
return false; return false;
} }
bool handled = false; bool handled = false;
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].button == b) { if (_relay[i].button == b && _relay[i].external) {
handled = true; handled = true;
} }
} }
@ -362,6 +414,8 @@ class MultiRelay : public Usermod {
buttonPressedBefore[b] = true; buttonPressedBefore[b] = true;
if (now - buttonPressedTime[b] > 600) { //long press if (now - buttonPressedTime[b] > 600) { //long press
//longPressAction(b); //not exposed
//handled = false; //use if you want to pass to default behaviour
buttonLongPressed[b] = true; buttonLongPressed[b] = true;
} }
@ -378,7 +432,8 @@ class MultiRelay : public Usermod {
if (!buttonLongPressed[b]) { //short press if (!buttonLongPressed[b]) { //short press
// if this is second release within 350ms it is a double press (buttonWaitTime!=0) // if this is second release within 350ms it is a double press (buttonWaitTime!=0)
if (doublePress) { if (doublePress) {
//doublePressAction(b); //doublePressAction(b); //not exposed
//handled = false; //use if you want to pass to default behaviour
} else { } else {
buttonWaitTime[b] = now; buttonWaitTime[b] = now;
} }
@ -386,9 +441,10 @@ class MultiRelay : public Usermod {
buttonPressedBefore[b] = false; buttonPressedBefore[b] = false;
buttonLongPressed[b] = false; buttonLongPressed[b] = false;
} }
// if 450ms elapsed since last press/release it is a short press // if 350ms elapsed since last press/release it is a short press
if (buttonWaitTime[b] && now - buttonWaitTime[b] > 350 && !buttonPressedBefore[b]) { if (buttonWaitTime[b] && now - buttonWaitTime[b] > 350 && !buttonPressedBefore[b]) {
buttonWaitTime[b] = 0; buttonWaitTime[b] = 0;
//shortPressAction(b); //not exposed
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].pin>=0 && _relay[i].button == b) { if (_relay[i].pin>=0 && _relay[i].button == b) {
toggleRelay(i); toggleRelay(i);
@ -494,6 +550,7 @@ class MultiRelay : public Usermod {
relay[FPSTR(_external)] = _relay[i].external; relay[FPSTR(_external)] = _relay[i].external;
relay[FPSTR(_button)] = _relay[i].button; relay[FPSTR(_button)] = _relay[i].button;
} }
top[FPSTR(_HAautodiscovery)] = HAautodiscovery;
DEBUG_PRINTLN(F("MultiRelay config saved.")); DEBUG_PRINTLN(F("MultiRelay config saved."));
} }
@ -516,6 +573,7 @@ class MultiRelay : public Usermod {
enabled = top[FPSTR(_enabled)] | enabled; enabled = top[FPSTR(_enabled)] | enabled;
periodicBroadcastSec = top[FPSTR(_broadcast)] | periodicBroadcastSec; periodicBroadcastSec = top[FPSTR(_broadcast)] | periodicBroadcastSec;
periodicBroadcastSec = min(900,max(0,(int)periodicBroadcastSec)); periodicBroadcastSec = min(900,max(0,(int)periodicBroadcastSec));
HAautodiscovery = top[FPSTR(_HAautodiscovery)] | HAautodiscovery;
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
String parName = FPSTR(_relay_str); parName += '-'; parName += i; String parName = FPSTR(_relay_str); parName += '-'; parName += i;
@ -583,3 +641,4 @@ const char MultiRelay::_activeHigh[] PROGMEM = "active-high";
const char MultiRelay::_external[] PROGMEM = "external"; const char MultiRelay::_external[] PROGMEM = "external";
const char MultiRelay::_button[] PROGMEM = "button"; const char MultiRelay::_button[] PROGMEM = "button";
const char MultiRelay::_broadcast[] PROGMEM = "broadcast-sec"; const char MultiRelay::_broadcast[] PROGMEM = "broadcast-sec";
const char MultiRelay::_HAautodiscovery[] PROGMEM = "HA-autodiscovery";

View File

@ -2,9 +2,10 @@
//WLED custom fonts, curtesy of @Benji (https://github.com/Proto-molecule) //WLED custom fonts, curtesy of @Benji (https://github.com/Proto-molecule)
/* /*
Fontname: wled_logo_akemi_4x4 Fontname: wled_logo_akemi_4x4
Copyright: Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 3/3 Glyphs: 3/3
BBX Build Mode: 3 BBX Build Mode: 3
* this logo ...WLED/images/wled_logo_akemi.png * this logo ...WLED/images/wled_logo_akemi.png
@ -28,7 +29,7 @@ const uint8_t u8x8_wled_logo_akemi_4x4[388] U8X8_FONT_SECTION("u8x8_wled_logo_ak
/* /*
Fontname: wled_logo_akemi_5x5 Fontname: wled_logo_akemi_5x5
Copyright: Created with Fony 1.4.7 Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 3/3 Glyphs: 3/3
BBX Build Mode: 3 BBX Build Mode: 3
* this logo ...WLED/images/wled_logo_akemi.png * this logo ...WLED/images/wled_logo_akemi.png
@ -59,7 +60,7 @@ const uint8_t u8x8_wled_logo_akemi_5x5[604] U8X8_FONT_SECTION("u8x8_wled_logo_ak
/* /*
Fontname: wled_logo_2x2 Fontname: wled_logo_2x2
Copyright: Created with Fony 1.4.7 Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 4/4 Glyphs: 4/4
BBX Build Mode: 3 BBX Build Mode: 3
* this logo https://cdn.discordapp.com/attachments/706623245935444088/927361780613799956/wled_scaled.png * this logo https://cdn.discordapp.com/attachments/706623245935444088/927361780613799956/wled_scaled.png
@ -105,7 +106,7 @@ const uint8_t u8x8_wled_logo_4x4[517] U8X8_FONT_SECTION("u8x8_wled_logo_4x4") =
/* /*
Fontname: 4LineDisplay_WLED_icons_1x Fontname: 4LineDisplay_WLED_icons_1x
Copyright: Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 13/13 Glyphs: 13/13
BBX Build Mode: 3 BBX Build Mode: 3
* 1 = sun * 1 = sun
@ -133,6 +134,33 @@ const uint8_t u8x8_4LineDisplay_WLED_icons_1x1[172] U8X8_FONT_SECTION("u8x8_4Lin
"\311\1\2\0\0~<<\30\30\0"; "\311\1\2\0\0~<<\30\30\0";
/*
Fontname: 4LineDisplay_WLED_icons_2x1
Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 11/11
BBX Build Mode: 3
* 1 = sun
* 2 = skip forward
* 3 = fire
* 4 = custom palette
* 5 = puzzle piece
* 6 = moon
* 7 = brush
* 8 = contrast
* 9 = power-standby
* 10 = star
* 11 = heart
* 12 = Akemi
*/
const uint8_t u8x8_4LineDisplay_WLED_icons_2x1[196] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_2x1") =
"\1\14\2\1\20\20BB\30\30<\275\275<\30\30BB\20\20\377~<<\70\30\20\0\377~<<"
"\70\30\20\0\60p\370\374\77>\236\214\300\340\370\360\360\340\0\0\34<v\326\336\375\375\377\277\275=>"
"\66\66<\34\374\374\374\374~\77\77~\374\374\374\374 pp \30<~~\377\370\360\360\340\340\340\340"
"@@ \0\200\300\340\360\360p`\10\34\34\16\6\6\3\0\0\70|~\376\376\377\377\377\201\201\203\202"
"\302Fl\70\70xL\204\200\200\217\217\200\200\204Lx\70\0\0\10\10\30\330x|\77\77|x\330\30"
"\10\10\0\0\14\36\37\77\77\177~\374\374~\177\77\77\37\36\14\24\64 \60>\26\367\33\375\36>\60"
" \64\24";
/* /*
Fontname: 4LineDisplay_WLED_icons_2x Fontname: 4LineDisplay_WLED_icons_2x
@ -167,9 +195,59 @@ const uint8_t u8x8_4LineDisplay_WLED_icons_2x2[389] U8X8_FONT_SECTION("u8x8_4Lin
"\7\3\1\0\200\200\0\0\0\360\370\374<\334\330\360\0\0\200\200\2\2\14\30\24\37\6~\7\177\7\37" "\7\3\1\0\200\200\0\0\0\360\370\374<\334\330\360\0\0\200\200\2\2\14\30\24\37\6~\7\177\7\37"
"\24\30\16\2"; "\24\30\16\2";
/*
Fontname: 4LineDisplay_WLED_icons_3x
Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 11/11
BBX Build Mode: 3
* 1 = sun
* 2 = skip forward
* 3 = fire
* 4 = custom palette
* 5 = puzzle piece
* 6 = moon
* 7 = brush
* 8 = contrast
* 9 = power-standby
* 10 = star
* 11 = heart
* 12 = Akemi
*/
const uint8_t u8x8_4LineDisplay_WLED_icons_3x3[868] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_3x3") =
"\1\14\3\3\0\0\34\34\34\0\200\300\300\340\347\347\347\340\300\300\200\0\34\34\34\0\0\0\34\34\34\0"
"\0>\377\377\377\377\377\377\377\377\377\377\377>\0\0\34\34\34\0\0\0\16\16\16\0\0\1\1\3ss"
"s\3\1\1\0\0\34\34\34\0\0\0\370\360\340\300\300\200\0\0\0\0\0\0\370\360\340\300\300\200\0\0"
"\0\0\0\0\377\377\377\377\377\377\377\376~<\70\20\377\377\377\377\377\377\377\376~<\70\20\37\17\17\7"
"\3\1\1\0\0\0\0\0\37\17\17\7\3\1\1\0\0\0\0\0\0\0\0\0\0\300\361\376\374\370\360\300"
"\0\0\0\0\0\0\0\0\0\0\0\0\300\370\374\376\377\377\377\377\377\177\77\17\6\0\200\342\374\370\360\340"
"\200\0\0\0\1\17\37\77\177\377\7\3\0\200\360\370\374\376\377\377\377\377\377\377\77\0\0\0\0\200\340\360"
"\370\370\374\316\206\206\317\377\377\377\317\206\206\316\374\374\370\360\340\200<\377\377\371\360py\377\377\377\377\377"
"\377\377\377\377\377\377\363\341\341\363\377\177\0\1\7\17\34\70x|\377\377\377\377\367\363c\3\3\3\3\1"
"\1\1\0\0\300\300\300\300\300\300\300\316\377\377\377\316\300\300\300\300\300\300\0\0\0\0\0\0\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\300\300\340\340\340\300\377\377\377\377\377\377\377\307\3\3\3\307"
"\377\377\377\377\377\377\1\1\3\3\3\1\0\300\340\370\374\374\376\377\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0>\377\377\377\377\377\377\377\377\374\360\340\300\300\200\200\0\0\0\0\0\0\200\200\0\1\7\17"
"\37\37\77\177\177\177\177\377\377\377\177\177\177\77\77\37\17\7\3\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\200\200\300\340\340\360\370\374|>\17\6\0\0\0\0\0\340\340\360\360\360\342\303\7\17\37\77\37\7\3\1"
"\0\0\0\0\0\200\340\360\377\377\377\377\177\77\37\17\0\0\0\0\0\0\0\0\0\0\0\0\0\200\340\360"
"\370\374\374\376\376\376\377\377\7\7\7\6\16\16\34\70\360\340\300\0|\377\377\377\377\377\377\377\377\377\377\377"
"\0\0\0\0\0\0\0\0\0\377\377\377\0\3\7\17\37\77\177\177\377\377\377\377\340\340\340\340pp\70<"
"\37\17\3\0\0\0\200\300\340\340\300\0\0\377\377\377\0\0\300\340\340\300\200\0\0\0\0\0\370\376\377\17"
"\3\0\0\0\0\17\17\17\0\0\0\0\0\3\17\377\376\370\0\0\0\7\17\37<xp\340\340\340\340\340"
"\340\340\340px<\37\17\3\0\0\0\0\0\0\0\0\0\0\0\0\0\300\370\376\370\200\0\0\0\0\0"
"\0\0\0\0\0\2\6\16\36\36>~\376\376\377\377\377\377\377\376\376~>\36\16\6\6\2\0\0\0\0"
"\0\300x<\37\17\17\7\3\7\17\17\37<x\300\0\0\0\0\200\300\340\360\360\370\370\370\360\360\340\300"
"\200\300\340\360\360\370\370\370\360\360\340\200\17\37\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\177\77\37\17\0\0\0\0\0\1\3\7\17\37\77\177\177\77\37\17\7\3\1\0\0\0\0\0\0\0\0\0"
"\0\0\0\200\300\340\360\370\370\370p`\300\200\0\0\0\0\0\0&,f\300\0\0\300\377\377\357\357\357"
"\363\370\377\377\377\377\300\0\0\306l&\0\0\0\1\7\16\14\6\7\1\177\177\0\177\177\1\7\6\14\16"
"\7\1\0";
/* /*
Fontname: 4LineDisplay_WLED_icons_4x Fontname: 4LineDisplay_WLED_icons_4x
Copyright: Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 11/11 Glyphs: 11/11
BBX Build Mode: 3 BBX Build Mode: 3
* 1 = sun * 1 = sun
@ -240,7 +318,7 @@ const uint8_t u8x8_4LineDisplay_WLED_icons_4x4[1540] U8X8_FONT_SECTION("u8x8_4Li
/* /*
Fontname: 4LineDisplay_WLED_icons_6x Fontname: 4LineDisplay_WLED_icons_6x
Copyright: Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 11/11 Glyphs: 11/11
BBX Build Mode: 3 BBX Build Mode: 3
* 1 = sun * 1 = sun
@ -256,6 +334,7 @@ const uint8_t u8x8_4LineDisplay_WLED_icons_4x4[1540] U8X8_FONT_SECTION("u8x8_4Li
* 11 = heart * 11 = heart
* 12 = Akemi * 12 = Akemi
*/ */
// you can replace this (wasteful) font by using 3x3 variant with draw2x2Glyph()
const uint8_t u8x8_4LineDisplay_WLED_icons_6x6[3460] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_6x6") = const uint8_t u8x8_4LineDisplay_WLED_icons_6x6[3460] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_6x6") =
"\1\14\6\6\0\0\0\0\0\0\200\300\300\300\300\200\0\0\0\0\0\0\0\0\0\36\77\77\77\77\36\0" "\1\14\6\6\0\0\0\0\0\0\200\300\300\300\300\200\0\0\0\0\0\0\0\0\0\36\77\77\77\77\36\0"
"\0\0\0\0\0\0\0\0\200\300\300\300\300\200\0\0\0\0\0\0\0\0\0\0\0\0\7\17\17\17\17\7" "\0\0\0\0\0\0\0\0\200\300\300\300\300\200\0\0\0\0\0\0\0\0\0\0\0\0\7\17\17\17\17\7"
@ -371,7 +450,7 @@ const uint8_t u8x8_4LineDisplay_WLED_icons_6x6[3460] U8X8_FONT_SECTION("u8x8_4Li
/* /*
Fontname: akemi_8x8 Fontname: akemi_8x8
Copyright: Created with Fony 1.4.7 Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 1/1 Glyphs: 1/1
BBX Build Mode: 3 BBX Build Mode: 3
* 12 = Akemi * 12 = Akemi

View File

@ -119,6 +119,7 @@ class FourLineDisplayUsermod : public Usermod {
// HW interface & configuration // HW interface & configuration
U8X8 *u8x8 = nullptr; // pointer to U8X8 display object U8X8 *u8x8 = nullptr; // pointer to U8X8 display object
#ifndef FLD_SPI_DEFAULT #ifndef FLD_SPI_DEFAULT
int8_t ioPin[5] = {FLD_PIN_SCL, FLD_PIN_SDA, -1, -1, -1}; // I2C pins: SCL, SDA int8_t ioPin[5] = {FLD_PIN_SCL, FLD_PIN_SDA, -1, -1, -1}; // I2C pins: SCL, SDA
uint32_t ioFrequency = 400000; // in Hz (minimum is 100000, baseline is 400000 and maximum should be 3400000) uint32_t ioFrequency = 400000; // in Hz (minimum is 100000, baseline is 400000 and maximum should be 3400000)
@ -126,6 +127,7 @@ class FourLineDisplayUsermod : public Usermod {
int8_t ioPin[5] = {FLD_PIN_CLOCKSPI, FLD_PIN_DATASPI, FLD_PIN_CS, FLD_PIN_DC, FLD_PIN_RESET}; // SPI pins: CLK, MOSI, CS, DC, RST int8_t ioPin[5] = {FLD_PIN_CLOCKSPI, FLD_PIN_DATASPI, FLD_PIN_CS, FLD_PIN_DC, FLD_PIN_RESET}; // SPI pins: CLK, MOSI, CS, DC, RST
uint32_t ioFrequency = 1000000; // in Hz (minimum is 500kHz, baseline is 1MHz and maximum should be 20MHz) uint32_t ioFrequency = 1000000; // in Hz (minimum is 500kHz, baseline is 1MHz and maximum should be 20MHz)
#endif #endif
DisplayType type = FLD_TYPE; // display type DisplayType type = FLD_TYPE; // display type
bool flip = false; // flip display 180° bool flip = false; // flip display 180°
uint8_t contrast = 10; // screen contrast uint8_t contrast = 10; // screen contrast
@ -136,6 +138,7 @@ class FourLineDisplayUsermod : public Usermod {
bool clockMode = false; // display clock bool clockMode = false; // display clock
bool showSeconds = true; // display clock with seconds bool showSeconds = true; // display clock with seconds
bool enabled = true; bool enabled = true;
bool contrastFix = false;
// Next variables hold the previous known values to determine if redraw is // Next variables hold the previous known values to determine if redraw is
// required. // required.
@ -175,6 +178,7 @@ class FourLineDisplayUsermod : public Usermod {
static const char _clockMode[]; static const char _clockMode[];
static const char _showSeconds[]; static const char _showSeconds[];
static const char _busClkFrequency[]; static const char _busClkFrequency[];
static const char _contrastFix[];
// If display does not work or looks corrupted check the // If display does not work or looks corrupted check the
// constructor reference: // constructor reference:
@ -182,6 +186,15 @@ class FourLineDisplayUsermod : public Usermod {
// or check the gallery: // or check the gallery:
// https://github.com/olikraus/u8g2/wiki/gallery // https://github.com/olikraus/u8g2/wiki/gallery
// some displays need this to properly apply contrast
void setVcomh(bool highContrast) {
u8x8_t *u8x8_struct = u8x8->getU8x8();
u8x8_cad_StartTransfer(u8x8_struct);
u8x8_cad_SendCmd(u8x8_struct, 0x0db); //address of value
u8x8_cad_SendArg(u8x8_struct, highContrast ? 0x000 : 0x040); //value 0 for fix, reboot resets default back to 64
u8x8_cad_EndTransfer(u8x8_struct);
}
public: public:
// gets called once at boot. Do all initialization that doesn't depend on // gets called once at boot. Do all initialization that doesn't depend on
@ -189,9 +202,9 @@ class FourLineDisplayUsermod : public Usermod {
void setup() { void setup() {
if (type == NONE || !enabled) return; if (type == NONE || !enabled) return;
bool isHW; bool isHW, isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64);
PinOwner po = PinOwner::UM_FourLineDisplay; PinOwner po = PinOwner::UM_FourLineDisplay;
if (type == SSD1306_SPI || type == SSD1306_SPI64) { if (isSPI) {
isHW = (ioPin[0]==HW_PIN_CLOCKSPI && ioPin[1]==HW_PIN_DATASPI); isHW = (ioPin[0]==HW_PIN_CLOCKSPI && ioPin[1]==HW_PIN_DATASPI);
PinManagerPinType pins[5] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true }, { ioPin[3], true }, { ioPin[4], true }}; PinManagerPinType pins[5] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true }, { ioPin[3], true }, { ioPin[4], true }};
if (!pinManager.allocateMultiplePins(pins, 5, po)) { type=NONE; return; } if (!pinManager.allocateMultiplePins(pins, 5, po)) { type=NONE; return; }
@ -203,41 +216,72 @@ class FourLineDisplayUsermod : public Usermod {
} }
DEBUG_PRINTLN(F("Allocating display.")); DEBUG_PRINTLN(F("Allocating display."));
/*
// At some point it may be good to not new/delete U8X8 object but use this instead
// (does not currently work)
//-------------------------------------------------------------------------------
switch (type) {
case SSD1306:
u8x8_Setup(u8x8.getU8x8(), u8x8_d_ssd1306_128x32_univision, u8x8_cad_ssd13xx_fast_i2c, u8x8_byte_arduino_sw_i2c, u8x8_gpio_and_delay_arduino);
break;
case SH1106:
u8x8_Setup(u8x8.getU8x8(), u8x8_d_sh1106_128x64_winstar, u8x8_cad_ssd13xx_i2c, u8x8_byte_arduino_sw_i2c, u8x8_gpio_and_delay_arduino);
break;
case SSD1306_64:
u8x8_Setup(u8x8.getU8x8(), u8x8_d_ssd1306_128x64_noname, u8x8_cad_ssd13xx_fast_i2c, u8x8_byte_arduino_sw_i2c, u8x8_gpio_and_delay_arduino);
break;
case SSD1305:
u8x8_Setup(u8x8.getU8x8(), u8x8_d_ssd1305_128x32_adafruit, u8x8_cad_ssd13xx_i2c, u8x8_byte_arduino_hw_i2c, u8x8_gpio_and_delay_arduino);
break;
case SSD1305_64:
u8x8_Setup(u8x8.getU8x8(), u8x8_d_ssd1305_128x64_adafruit, u8x8_cad_ssd13xx_i2c, u8x8_byte_arduino_sw_i2c, u8x8_gpio_and_delay_arduino);
break;
case SSD1306_SPI:
u8x8_Setup(u8x8.getU8x8(), u8x8_d_ssd1306_128x32_univision, u8x8_cad_001, u8x8_byte_arduino_4wire_sw_spi, u8x8_gpio_and_delay_arduino);
break;
case SSD1306_SPI64:
u8x8_Setup(u8x8.getU8x8(), u8x8_d_ssd1306_128x64_noname, u8x8_cad_001, u8x8_byte_arduino_4wire_sw_spi, u8x8_gpio_and_delay_arduino);
break;
default:
type = NONE;
return;
}
if (isSPI) {
if (!isHW) u8x8_SetPin_4Wire_SW_SPI(u8x8.getU8x8(), ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
else u8x8_SetPin_4Wire_HW_SPI(u8x8.getU8x8(), ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
} else {
if (!isHW) u8x8_SetPin_SW_I2C(u8x8.getU8x8(), ioPin[0], ioPin[1], U8X8_PIN_NONE); // SCL, SDA, reset
else u8x8_SetPin_HW_I2C(u8x8.getU8x8(), U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
}
*/
switch (type) { switch (type) {
case SSD1306: case SSD1306:
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA else u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
lineHeight = 1;
break; break;
case SH1106: case SH1106:
if (!isHW) u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset if (!isHW) u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
else u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA else u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
lineHeight = 2;
break; break;
case SSD1306_64: case SSD1306_64:
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA else u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
lineHeight = 2;
break; break;
case SSD1305: case SSD1305:
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
else u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA else u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
lineHeight = 1;
break; break;
case SSD1305_64: case SSD1305_64:
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
else u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA else u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
lineHeight = 2;
break; break;
case SSD1306_SPI: case SSD1306_SPI:
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]); if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset else u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
lineHeight = 1;
break; break;
case SSD1306_SPI64: case SSD1306_SPI64:
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]); if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset else u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
lineHeight = 2;
break; break;
default: default:
u8x8 = nullptr; u8x8 = nullptr;
@ -245,20 +289,22 @@ class FourLineDisplayUsermod : public Usermod {
if (nullptr == u8x8) { if (nullptr == u8x8) {
DEBUG_PRINTLN(F("Display init failed.")); DEBUG_PRINTLN(F("Display init failed."));
pinManager.deallocateMultiplePins((const uint8_t*)ioPin, (type == SSD1306_SPI || type == SSD1306_SPI64) ? 5 : 2, po); pinManager.deallocateMultiplePins((const uint8_t*)ioPin, isSPI ? 5 : 2, po);
type = NONE; type = NONE;
return; return;
} }
initDone = true; lineHeight = u8x8->getRows() > 4 ? 2 : 1;
DEBUG_PRINTLN(F("Starting display.")); DEBUG_PRINTLN(F("Starting display."));
/*if (!(type == SSD1306_SPI || type == SSD1306_SPI64))*/ u8x8->setBusClock(ioFrequency); // can be used for SPI too u8x8->setBusClock(ioFrequency); // can be used for SPI too
u8x8->begin(); u8x8->begin();
setFlipMode(flip); setFlipMode(flip);
setVcomh(contrastFix);
setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255 setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
setPowerSave(0); setPowerSave(0);
//drawString(0, 0, "Loading..."); //drawString(0, 0, "Loading...");
overlayLogo(3500); overlayLogo(3500);
initDone = true;
} }
// gets called every time WiFi is (re-)connected. Initialize own network // gets called every time WiFi is (re-)connected. Initialize own network
@ -379,30 +425,29 @@ class FourLineDisplayUsermod : public Usermod {
String timer = PSTR("Timer On"); String timer = PSTR("Timer On");
center(timer,LINE_BUFFER_SIZE-1); center(timer,LINE_BUFFER_SIZE-1);
overlay(timer.c_str(), 2500, 6); overlay(timer.c_str(), 2500, 6);
//lastRedraw = millis();
} }
return; return;
} else if (wificonnected != interfacesInited) { //trigger wifi icon } else if (wificonnected != interfacesInited) { //trigger wifi icon
wificonnected = interfacesInited; wificonnected = interfacesInited;
drawStatusIcons(); drawStatusIcons();
return; return;
} else if (knownMode != effectCurrent) { } else if (knownMode != effectCurrent || knownPalette != effectPalette) {
knownMode = effectCurrent;
if (displayTurnedOff) needRedraw = true; if (displayTurnedOff) needRedraw = true;
else { showCurrentEffectOrPalette(knownMode, JSON_mode_names, 3); return; } else {
} else if (knownPalette != effectPalette) { if (knownPalette != effectPalette) { showCurrentEffectOrPalette(effectPalette, JSON_palette_names, 2); knownPalette = effectPalette; }
knownPalette = effectPalette; if (knownMode != effectCurrent) { showCurrentEffectOrPalette(effectCurrent, JSON_mode_names, 3); knownMode = effectCurrent; }
if (displayTurnedOff) needRedraw = true; lastRedraw = now;
else { showCurrentEffectOrPalette(knownPalette, JSON_palette_names, 2); return; } return;
}
} else if (knownBrightness != bri) { } else if (knownBrightness != bri) {
if (displayTurnedOff && nightlightActive) { knownBrightness = bri; } if (displayTurnedOff && nightlightActive) { knownBrightness = bri; }
else if (!displayTurnedOff) { updateBrightness(); return; } else if (!displayTurnedOff) { updateBrightness(); lastRedraw = now; return; }
} else if (knownEffectSpeed != effectSpeed) { } else if (knownEffectSpeed != effectSpeed) {
if (displayTurnedOff) needRedraw = true; if (displayTurnedOff) needRedraw = true;
else { updateSpeed(); return; } else { updateSpeed(); lastRedraw = now; return; }
} else if (knownEffectIntensity != effectIntensity) { } else if (knownEffectIntensity != effectIntensity) {
if (displayTurnedOff) needRedraw = true; if (displayTurnedOff) needRedraw = true;
else { updateIntensity(); return; } else { updateIntensity(); lastRedraw = now; return; }
} }
if (!needRedraw) { if (!needRedraw) {
@ -425,13 +470,13 @@ class FourLineDisplayUsermod : public Usermod {
wakeDisplay(); wakeDisplay();
// Update last known values. // Update last known values.
knownBrightness = bri; knownBrightness = bri;
knownMode = effectCurrent; knownMode = effectCurrent;
knownPalette = effectPalette; knownPalette = effectPalette;
knownEffectSpeed = effectSpeed; knownEffectSpeed = effectSpeed;
knownEffectIntensity = effectIntensity; knownEffectIntensity = effectIntensity;
knownnightlight = nightlightActive; knownnightlight = nightlightActive;
wificonnected = interfacesInited; wificonnected = interfacesInited;
// Do the actual drawing // Do the actual drawing
// First row: Icons // First row: Icons
@ -492,9 +537,9 @@ class FourLineDisplayUsermod : public Usermod {
drawGlyph(14, 2*lineHeight, 4, u8x8_4LineDisplay_WLED_icons_2x2, true); //palette icon drawGlyph(14, 2*lineHeight, 4, u8x8_4LineDisplay_WLED_icons_2x2, true); //palette icon
drawGlyph(14, 3*lineHeight, 5, u8x8_4LineDisplay_WLED_icons_2x2, true); //effect icon drawGlyph(14, 3*lineHeight, 5, u8x8_4LineDisplay_WLED_icons_2x2, true); //effect icon
} else { } else {
drawGlyph( 2, 0, 1, u8x8_4LineDisplay_WLED_icons_1x1); //brightness icon drawGlyph( 1, 0, 1, u8x8_4LineDisplay_WLED_icons_2x1); //brightness icon
drawGlyph( 6, 0, 2, u8x8_4LineDisplay_WLED_icons_1x1); //speed icon drawGlyph( 5, 0, 2, u8x8_4LineDisplay_WLED_icons_2x1); //speed icon
drawGlyph(10, 0, 3, u8x8_4LineDisplay_WLED_icons_1x1); //intensity icon drawGlyph( 9, 0, 3, u8x8_4LineDisplay_WLED_icons_2x1); //intensity icon
drawGlyph(15, 2, 4, u8x8_4LineDisplay_WLED_icons_1x1); //palette icon drawGlyph(15, 2, 4, u8x8_4LineDisplay_WLED_icons_1x1); //palette icon
drawGlyph(15, 3, 5, u8x8_4LineDisplay_WLED_icons_1x1); //effect icon drawGlyph(15, 3, 5, u8x8_4LineDisplay_WLED_icons_1x1); //effect icon
} }
@ -506,7 +551,7 @@ class FourLineDisplayUsermod : public Usermod {
drawGlyph(col, row, (wificonnected ? 20 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // wifi icon drawGlyph(col, row, (wificonnected ? 20 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // wifi icon
if (lineHeight==2) { col--; } else { row++; } if (lineHeight==2) { col--; } else { row++; }
drawGlyph(col, row, (bri > 0 ? 9 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // power icon drawGlyph(col, row, (bri > 0 ? 9 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // power icon
if (lineHeight==2) { col--; } else { row++; col = row = 0; } if (lineHeight==2) { col--; } else { col = row = 0; }
drawGlyph(col, row, (nightlightActive ? 6 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // moon icon for nighlight mode drawGlyph(col, row, (nightlightActive ? 6 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // moon icon for nighlight mode
} }
@ -529,18 +574,21 @@ class FourLineDisplayUsermod : public Usermod {
// on the appropriate line (row). // on the appropriate line (row).
void showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row) { void showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row) {
char lineBuffer[MAX_JSON_CHARS]; char lineBuffer[MAX_JSON_CHARS];
knownMode = effectCurrent;
knownPalette = effectPalette;
if (overlayUntil == 0) { if (overlayUntil == 0) {
// Find the mode name in JSON // Find the mode name in JSON
uint8_t printedChars = extractModeName(inputEffPal, qstring, lineBuffer, MAX_JSON_CHARS-1); uint8_t printedChars = extractModeName(inputEffPal, qstring, lineBuffer, MAX_JSON_CHARS-1);
if (lineBuffer[0]=='*' && lineBuffer[1]==' ') {
// remove "* " from dynamic palettes
for (byte i=2; i<=printedChars; i++) lineBuffer[i-2] = lineBuffer[i]; //include '\0'
printedChars -= 2;
}
if (lineHeight == 2) { // use this code for 8 line display if (lineHeight == 2) { // use this code for 8 line display
char smallBuffer1[MAX_MODE_LINE_SPACE]; char smallBuffer1[MAX_MODE_LINE_SPACE];
char smallBuffer2[MAX_MODE_LINE_SPACE]; char smallBuffer2[MAX_MODE_LINE_SPACE];
uint8_t smallChars1 = 0; uint8_t smallChars1 = 0;
uint8_t smallChars2 = 0; uint8_t smallChars2 = 0;
if (printedChars < MAX_MODE_LINE_SPACE) { // use big font if the text fits if (printedChars < MAX_MODE_LINE_SPACE) { // use big font if the text fits
for (;printedChars < (MAX_MODE_LINE_SPACE-1); printedChars++) lineBuffer[printedChars]=' '; while (printedChars < (MAX_MODE_LINE_SPACE-1)) lineBuffer[printedChars++]=' ';
lineBuffer[printedChars] = 0; lineBuffer[printedChars] = 0;
drawString(1, row*lineHeight, lineBuffer); drawString(1, row*lineHeight, lineBuffer);
} else { // for long names divide the text into 2 lines and print them small } else { // for long names divide the text into 2 lines and print them small
@ -561,23 +609,20 @@ class FourLineDisplayUsermod : public Usermod {
break; break;
} }
} }
for (; smallChars1 < (MAX_MODE_LINE_SPACE-1); smallChars1++) smallBuffer1[smallChars1]=' '; while (smallChars1 < (MAX_MODE_LINE_SPACE-1)) smallBuffer1[smallChars1++]=' ';
smallBuffer1[smallChars1] = 0; smallBuffer1[smallChars1] = 0;
drawString(1, row*lineHeight, smallBuffer1, true); drawString(1, row*lineHeight, smallBuffer1, true);
for (; smallChars2 < (MAX_MODE_LINE_SPACE-1); smallChars2++) smallBuffer2[smallChars2]=' '; while (smallChars2 < (MAX_MODE_LINE_SPACE-1)) smallBuffer2[smallChars2++]=' ';
smallBuffer2[smallChars2] = 0; smallBuffer2[smallChars2] = 0;
drawString(1, row*lineHeight+1, smallBuffer2, true); drawString(1, row*lineHeight+1, smallBuffer2, true);
} }
} else { // use this code for 4 ling displays } else { // use this code for 4 ling displays
char smallBuffer3[MAX_MODE_LINE_SPACE+1]; char smallBuffer3[MAX_MODE_LINE_SPACE+1]; // uses 1x1 icon for mode/palette
uint8_t smallChars3 = 0; uint8_t smallChars3 = 0;
if (printedChars > MAX_MODE_LINE_SPACE) printedChars = MAX_MODE_LINE_SPACE; for (uint8_t i = 0; i < MAX_MODE_LINE_SPACE; i++) smallBuffer3[smallChars3++] = (i >= printedChars) ? ' ' : lineBuffer[i];
for (uint8_t i = 0; i < printedChars; i++) smallBuffer3[smallChars3++] = lineBuffer[i];
for (; smallChars3 < (MAX_MODE_LINE_SPACE); smallChars3++) smallBuffer3[smallChars3]=' ';
smallBuffer3[smallChars3] = 0; smallBuffer3[smallChars3] = 0;
drawString(1, row*lineHeight, smallBuffer3, true); drawString(1, row*lineHeight, smallBuffer3, true);
} }
lastRedraw = millis();
} }
} }
@ -609,8 +654,8 @@ class FourLineDisplayUsermod : public Usermod {
if (!wakeDisplay()) clear(); if (!wakeDisplay()) clear();
// Print the overlay // Print the overlay
if (glyphType>0 && glyphType<255) { if (glyphType>0 && glyphType<255) {
if (lineHeight == 2) drawGlyph(5, 0, glyphType, u8x8_4LineDisplay_WLED_icons_6x6, true); if (lineHeight == 2) drawGlyph(5, 0, glyphType, u8x8_4LineDisplay_WLED_icons_6x6, true); // use 3x3 font with draw2x2Glyph() if flash runs short and comment out 6x6 font
else drawGlyph(7, lineHeight, glyphType, u8x8_4LineDisplay_WLED_icons_2x2, true); else drawGlyph(6, 0, glyphType, u8x8_4LineDisplay_WLED_icons_3x3, true);
} }
if (line1) { if (line1) {
String buf = line1; String buf = line1;
@ -646,7 +691,7 @@ class FourLineDisplayUsermod : public Usermod {
break; break;
case 2: case 2:
//Akemi //Akemi
//draw2x2Glyph( 4, 0, 12, u8x8_4LineDisplay_WLED_icons_4x4); //draw2x2Glyph( 5, 0, 12, u8x8_4LineDisplay_WLED_icons_3x3); // use this if flash runs short and comment out 6x6 font
drawGlyph( 5, 0, 12, u8x8_4LineDisplay_WLED_icons_6x6, true); drawGlyph( 5, 0, 12, u8x8_4LineDisplay_WLED_icons_6x6, true);
drawString(6, 6, "WLED"); drawString(6, 6, "WLED");
break; break;
@ -668,7 +713,7 @@ class FourLineDisplayUsermod : public Usermod {
break; break;
case 2: case 2:
//Akemi //Akemi
//drawGlyph( 6, 0, 12, u8x8_4LineDisplay_WLED_icons_4x4); //drawGlyph( 6, 0, 12, u8x8_4LineDisplay_WLED_icons_4x4); // a bit nicer, but uses extra 1.5k flash
draw2x2Glyph( 6, 0, 12, u8x8_4LineDisplay_WLED_icons_2x2); draw2x2Glyph( 6, 0, 12, u8x8_4LineDisplay_WLED_icons_2x2);
break; break;
} }
@ -795,6 +840,80 @@ class FourLineDisplayUsermod : public Usermod {
} }
} }
/**
* handleButton() can be used to override default button behaviour. Returning true
* will prevent button working in a default way.
* Replicating button.cpp
*/
bool handleButton(uint8_t b) {
yield();
if (!enabled
|| b // butto 0 only
|| buttonType[b] == BTN_TYPE_SWITCH
|| buttonType[b] == BTN_TYPE_NONE
|| buttonType[b] == BTN_TYPE_RESERVED
|| buttonType[b] == BTN_TYPE_PIR_SENSOR
|| buttonType[b] == BTN_TYPE_ANALOG
|| buttonType[b] == BTN_TYPE_ANALOG_INVERTED) {
return false;
}
unsigned long now = millis();
static bool buttonPressedBefore = false;
static bool buttonLongPressed = false;
static unsigned long buttonPressedTime = 0;
static unsigned long buttonWaitTime = 0;
bool handled = true;
//momentary button logic
if (isButtonPressed(b)) { //pressed
if (!buttonPressedBefore) buttonPressedTime = now;
buttonPressedBefore = true;
if (now - buttonPressedTime > 600) { //long press
buttonLongPressed = true;
//TODO: handleButton() handles button 0 without preset in a different way for double click
//so we need to override with same behaviour
longPressAction(0);
//handled = false;
}
} else if (!isButtonPressed(b) && buttonPressedBefore) { //released
long dur = now - buttonPressedTime;
if (dur < 50) {
buttonPressedBefore = false;
return true;
} //too short "press", debounce
bool doublePress = buttonWaitTime; //did we have short press before?
buttonWaitTime = 0;
if (!buttonLongPressed) { //short press
// if this is second release within 350ms it is a double press (buttonWaitTime!=0)
//TODO: handleButton() handles button 0 without preset in a different way for double click
if (doublePress) {
networkOverlay(PSTR("NETWORK INFO"),7000);
handled = true;
} else {
buttonWaitTime = now;
}
}
buttonPressedBefore = false;
buttonLongPressed = false;
}
// if 350ms elapsed since last press/release it is a short press
if (buttonWaitTime && now - buttonWaitTime > 350 && !buttonPressedBefore) {
buttonWaitTime = 0;
//TODO: handleButton() handles button 0 without preset in a different way for double click
//so we need to override with same behaviour
shortPressAction(0);
//handled = false;
}
return handled;
}
/* /*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
@ -846,6 +965,7 @@ class FourLineDisplayUsermod : public Usermod {
top["help4Type"] = F("1=SSD1306,2=SH1106,3=SSD1306_128x64,4=SSD1305,5=SSD1305_128x64,6=SSD1306_SPI,7=SSD1306_SPI_128x64"); // help for Settings page top["help4Type"] = F("1=SSD1306,2=SH1106,3=SSD1306_128x64,4=SSD1305,5=SSD1305_128x64,6=SSD1306_SPI,7=SSD1306_SPI_128x64"); // help for Settings page
top[FPSTR(_flip)] = (bool) flip; top[FPSTR(_flip)] = (bool) flip;
top[FPSTR(_contrast)] = contrast; top[FPSTR(_contrast)] = contrast;
top[FPSTR(_contrastFix)] = (bool) contrastFix;
top[FPSTR(_refreshRate)] = refreshRate; top[FPSTR(_refreshRate)] = refreshRate;
top[FPSTR(_screenTimeOut)] = screenTimeout/1000; top[FPSTR(_screenTimeOut)] = screenTimeout/1000;
top[FPSTR(_sleepMode)] = (bool) sleepMode; top[FPSTR(_sleepMode)] = (bool) sleepMode;
@ -886,6 +1006,7 @@ class FourLineDisplayUsermod : public Usermod {
sleepMode = top[FPSTR(_sleepMode)] | sleepMode; sleepMode = top[FPSTR(_sleepMode)] | sleepMode;
clockMode = top[FPSTR(_clockMode)] | clockMode; clockMode = top[FPSTR(_clockMode)] | clockMode;
showSeconds = top[FPSTR(_showSeconds)] | showSeconds; showSeconds = top[FPSTR(_showSeconds)] | showSeconds;
contrastFix = top[FPSTR(_contrastFix)] | contrastFix;
if (newType == SSD1306_SPI || newType == SSD1306_SPI64) if (newType == SSD1306_SPI || newType == SSD1306_SPI64)
ioFrequency = min(20000, max(500, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency ioFrequency = min(20000, max(500, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
else else
@ -914,16 +1035,18 @@ class FourLineDisplayUsermod : public Usermod {
} else type = newType; } else type = newType;
setup(); setup();
needsRedraw |= true; needsRedraw |= true;
} else {
u8x8->setBusClock(ioFrequency); // can be used for SPI too
setVcomh(contrastFix);
setContrast(contrast);
setFlipMode(flip);
} }
u8x8->setBusClock(ioFrequency); // can be used for SPI too
setContrast(contrast);
setFlipMode(flip);
knownHour = 99; knownHour = 99;
if (needsRedraw && !wakeDisplay()) redraw(true); if (needsRedraw && !wakeDisplay()) redraw(true);
else overlayLogo(3500); else overlayLogo(3500);
} }
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features // use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_refreshRate)].isNull(); return !top[FPSTR(_contrastFix)].isNull();
} }
/* /*
@ -946,3 +1069,4 @@ const char FourLineDisplayUsermod::_sleepMode[] PROGMEM = "sleepMode";
const char FourLineDisplayUsermod::_clockMode[] PROGMEM = "clockMode"; const char FourLineDisplayUsermod::_clockMode[] PROGMEM = "clockMode";
const char FourLineDisplayUsermod::_showSeconds[] PROGMEM = "showSeconds"; const char FourLineDisplayUsermod::_showSeconds[] PROGMEM = "showSeconds";
const char FourLineDisplayUsermod::_busClkFrequency[] PROGMEM = "i2c-freq-kHz"; const char FourLineDisplayUsermod::_busClkFrequency[] PROGMEM = "i2c-freq-kHz";
const char FourLineDisplayUsermod::_contrastFix[] PROGMEM = "contrastFix";

View File

@ -47,7 +47,7 @@
// The last UI state, remove color and saturation option if diplay not active(too many options) // The last UI state, remove color and saturation option if diplay not active(too many options)
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
#define LAST_UI_STATE 7 #define LAST_UI_STATE 8
#else #else
#define LAST_UI_STATE 4 #define LAST_UI_STATE 4
#endif #endif
@ -160,9 +160,14 @@ private:
uint8_t knownMode = 0; uint8_t knownMode = 0;
uint8_t knownPalette = 0; uint8_t knownPalette = 0;
uint8_t currentCCT = 128;
bool isRgbw = false;
byte presetHigh = 0; byte presetHigh = 0;
byte presetLow = 0; byte presetLow = 0;
bool applyToAll = true;
bool initDone = false; bool initDone = false;
bool enabled = true; bool enabled = true;
@ -174,6 +179,7 @@ private:
static const char _SW_pin[]; static const char _SW_pin[];
static const char _presetHigh[]; static const char _presetHigh[];
static const char _presetLow[]; static const char _presetLow[];
static const char _applyToAll[];
/** /**
* Sort the modes and palettes to the index arrays * Sort the modes and palettes to the index arrays
@ -276,6 +282,14 @@ public:
pinMode(pinC, INPUT_PULLUP); pinMode(pinC, INPUT_PULLUP);
loopTime = millis(); loopTime = millis();
for (uint8_t s = 0; s < busses.getNumBusses(); s++) {
Bus *bus = busses.getBus(s);
if (!bus || bus->getLength()==0) break;
isRgbw |= bus->isRgbw();
}
currentCCT = (approximateKelvinFromRGB(RGBW32(col[0], col[1], col[2], col[3])) - 1900) >> 5;
if (!initDone) sortModesAndPalettes(); if (!initDone) sortModesAndPalettes();
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
@ -355,11 +369,11 @@ public:
buttonLongPressed = false; buttonLongPressed = false;
buttonPressedBefore = false; buttonPressedBefore = false;
} }
if (buttonWaitTime && currentTime-buttonWaitTime>350 && !buttonPressedBefore) { if (buttonWaitTime && currentTime-buttonWaitTime>350 && !buttonPressedBefore) { //same speed as in button.cpp
buttonWaitTime = 0; buttonWaitTime = 0;
char newState = select_state + 1; char newState = select_state + 1;
bool changedState = true; bool changedState = true;
if (newState > LAST_UI_STATE || (newState == 7 && presetHigh==0 && presetLow == 0)) newState = 0; if (newState > LAST_UI_STATE || (newState == 8 && presetHigh==0 && presetLow == 0)) newState = 0;
if (display != nullptr) { if (display != nullptr) {
switch (newState) { switch (newState) {
case 0: changedState = changeState(PSTR("Brightness"), 1, 0, 1); break; //1 = sun case 0: changedState = changeState(PSTR("Brightness"), 1, 0, 1); break; //1 = sun
@ -369,7 +383,8 @@ public:
case 4: changedState = changeState(PSTR("Effect"), 3, 0, 5); break; //5 = puzzle piece case 4: changedState = changeState(PSTR("Effect"), 3, 0, 5); break; //5 = puzzle piece
case 5: changedState = changeState(PSTR("Main Color"), 255, 255, 7); break; //7 = brush case 5: changedState = changeState(PSTR("Main Color"), 255, 255, 7); break; //7 = brush
case 6: changedState = changeState(PSTR("Saturation"), 255, 255, 8); break; //8 = contrast case 6: changedState = changeState(PSTR("Saturation"), 255, 255, 8); break; //8 = contrast
case 7: changedState = changeState(PSTR("Preset"), 255, 255, 11); break; //11 = heart case 7: changedState = changeState(PSTR("CCT"), 255, 255, 10); break; //10 = star
case 8: changedState = changeState(PSTR("Preset"), 255, 255, 11); break; //11 = heart
} }
} }
if (changedState) select_state = newState; if (changedState) select_state = newState;
@ -389,7 +404,8 @@ public:
case 4: changeEffect(true); break; case 4: changeEffect(true); break;
case 5: changeHue(true); break; case 5: changeHue(true); break;
case 6: changeSat(true); break; case 6: changeSat(true); break;
case 7: changePreset(true); break; case 7: changeCCT(true); break;
case 8: changePreset(true); break;
} }
} }
else if (Enc_B == HIGH) else if (Enc_B == HIGH)
@ -402,7 +418,8 @@ public:
case 4: changeEffect(false); break; case 4: changeEffect(false); break;
case 5: changeHue(false); break; case 5: changeHue(false); break;
case 6: changeSat(false); break; case 6: changeSat(false); break;
case 7: changePreset(false); break; case 7: changeCCT(false); break;
case 8: changePreset(false); break;
} }
} }
} }
@ -452,6 +469,7 @@ public:
//bool fxChanged = strip.setEffectConfig(effectCurrent, effectSpeed, effectIntensity, effectPalette); //bool fxChanged = strip.setEffectConfig(effectCurrent, effectSpeed, effectIntensity, effectPalette);
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification) //call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification)
// 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa // 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa
setValuesFromMainSeg(); //to make transition work on main segment
colorUpdated(CALL_MODE_DIRECT_CHANGE); colorUpdated(CALL_MODE_DIRECT_CHANGE);
updateInterfaces(CALL_MODE_DIRECT_CHANGE); updateInterfaces(CALL_MODE_DIRECT_CHANGE);
} }
@ -463,6 +481,7 @@ public:
// Throw away wake up input // Throw away wake up input
return; return;
} }
display->updateRedrawTime();
#endif #endif
bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0); bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0);
lampUdated(); lampUdated();
@ -479,9 +498,21 @@ public:
// Throw away wake up input // Throw away wake up input
return; return;
} }
display->updateRedrawTime();
#endif #endif
effectCurrentIndex = max(min((increase ? effectCurrentIndex+1 : effectCurrentIndex-1), strip.getModeCount()-1), 0); effectCurrentIndex = max(min((increase ? effectCurrentIndex+1 : effectCurrentIndex-1), strip.getModeCount()-1), 0);
effectCurrent = modes_alpha_indexes[effectCurrentIndex]; effectCurrent = modes_alpha_indexes[effectCurrentIndex];
effectChanged = true;
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
strip.setMode(i, effectCurrent);
}
} else {
//WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
strip.setMode(strip.getMainSegmentId(), effectCurrent);
}
lampUdated(); lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
display->showCurrentEffectOrPalette(effectCurrent, JSON_mode_names, 3); display->showCurrentEffectOrPalette(effectCurrent, JSON_mode_names, 3);
@ -496,8 +527,20 @@ public:
// Throw away wake up input // Throw away wake up input
return; return;
} }
display->updateRedrawTime();
#endif #endif
effectSpeed = max(min((increase ? effectSpeed+fadeAmount : effectSpeed-fadeAmount), 255), 0); effectSpeed = max(min((increase ? effectSpeed+fadeAmount : effectSpeed-fadeAmount), 255), 0);
effectChanged = true;
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.speed = effectSpeed;
}
} else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.speed = effectSpeed;
}
lampUdated(); lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
display->updateSpeed(); display->updateSpeed();
@ -512,8 +555,20 @@ public:
// Throw away wake up input // Throw away wake up input
return; return;
} }
display->updateRedrawTime();
#endif #endif
effectIntensity = max(min((increase ? effectIntensity+fadeAmount : effectIntensity-fadeAmount), 255), 0); effectIntensity = max(min((increase ? effectIntensity+fadeAmount : effectIntensity-fadeAmount), 255), 0);
effectChanged = true;
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.intensity = effectIntensity;
}
} else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.intensity = effectIntensity;
}
lampUdated(); lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
display->updateIntensity(); display->updateIntensity();
@ -528,9 +583,21 @@ public:
// Throw away wake up input // Throw away wake up input
return; return;
} }
display->updateRedrawTime();
#endif #endif
effectPaletteIndex = max(min((increase ? effectPaletteIndex+1 : effectPaletteIndex-1), strip.getPaletteCount()-1), 0); effectPaletteIndex = max(min((increase ? effectPaletteIndex+1 : effectPaletteIndex-1), strip.getPaletteCount()-1), 0);
effectPalette = palettes_alpha_indexes[effectPaletteIndex]; effectPalette = palettes_alpha_indexes[effectPaletteIndex];
effectChanged = true;
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.palette = effectPalette;
}
} else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.palette = effectPalette;
}
lampUdated(); lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
display->showCurrentEffectOrPalette(effectPalette, JSON_palette_names, 2); display->showCurrentEffectOrPalette(effectPalette, JSON_palette_names, 2);
@ -545,15 +612,22 @@ public:
// Throw away wake up input // Throw away wake up input
return; return;
} }
display->updateRedrawTime();
#endif #endif
currentHue1 = max(min((increase ? currentHue1+fadeAmount : currentHue1-fadeAmount), 255), 0); currentHue1 = max(min((increase ? currentHue1+fadeAmount : currentHue1-fadeAmount), 255), 0);
colorHStoRGB(currentHue1*256, currentSat1, col); colorHStoRGB(currentHue1*256, currentSat1, col);
strip.applyToAllSelected = true; colorChanged = true;
strip.setColor(0, colorFromRgbw(col)); if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
}
} else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
}
lampUdated(); lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY
display->updateRedrawTime();
#endif
} }
void changeSat(bool increase){ void changeSat(bool increase){
@ -563,15 +637,21 @@ public:
// Throw away wake up input // Throw away wake up input
return; return;
} }
display->updateRedrawTime();
#endif #endif
currentSat1 = max(min((increase ? currentSat1+fadeAmount : currentSat1-fadeAmount), 255), 0); currentSat1 = max(min((increase ? currentSat1+fadeAmount : currentSat1-fadeAmount), 255), 0);
colorHStoRGB(currentHue1*256, currentSat1, col); colorHStoRGB(currentHue1*256, currentSat1, col);
strip.applyToAllSelected = true; if (applyToAll) {
strip.setColor(0, colorFromRgbw(col)); for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
}
} else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
}
lampUdated(); lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY
display->updateRedrawTime();
#endif
} }
void changePreset(bool increase) { void changePreset(bool increase) {
@ -581,6 +661,7 @@ public:
// Throw away wake up input // Throw away wake up input
return; return;
} }
display->updateRedrawTime();
#endif #endif
if (presetHigh && presetLow && presetHigh > presetLow) { if (presetHigh && presetLow && presetHigh > presetLow) {
String apireq = F("win&PL=~"); String apireq = F("win&PL=~");
@ -592,9 +673,29 @@ public:
handleSet(nullptr, apireq, false); handleSet(nullptr, apireq, false);
lampUdated(); lampUdated();
} }
}
void changeCCT(bool increase){
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
// Throw away wake up input
return;
}
display->updateRedrawTime(); display->updateRedrawTime();
#endif #endif
currentCCT = max(min((increase ? currentCCT+fadeAmount : currentCCT-fadeAmount), 255), 0);
// if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.setCCT(currentCCT, i);
}
// } else {
// WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
// seg.setCCT(currentCCT, strip.getMainSegmentId());
// }
lampUdated();
} }
/* /*
@ -650,6 +751,7 @@ public:
top[FPSTR(_SW_pin)] = pinC; top[FPSTR(_SW_pin)] = pinC;
top[FPSTR(_presetLow)] = presetLow; top[FPSTR(_presetLow)] = presetLow;
top[FPSTR(_presetHigh)] = presetHigh; top[FPSTR(_presetHigh)] = presetHigh;
top[FPSTR(_applyToAll)] = applyToAll;
DEBUG_PRINTLN(F("Rotary Encoder config saved.")); DEBUG_PRINTLN(F("Rotary Encoder config saved."));
} }
@ -676,6 +778,7 @@ public:
presetLow = MIN(250,MAX(0,presetLow)); presetLow = MIN(250,MAX(0,presetLow));
enabled = top[FPSTR(_enabled)] | enabled; enabled = top[FPSTR(_enabled)] | enabled;
applyToAll = top[FPSTR(_applyToAll)] | applyToAll;
DEBUG_PRINT(FPSTR(_name)); DEBUG_PRINT(FPSTR(_name));
if (!initDone) { if (!initDone) {
@ -702,7 +805,7 @@ public:
} }
} }
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features // use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_presetHigh)].isNull(); return !top[FPSTR(_applyToAll)].isNull();
} }
/* /*
@ -723,3 +826,4 @@ const char RotaryEncoderUIUsermod::_CLK_pin[] PROGMEM = "CLK-pin";
const char RotaryEncoderUIUsermod::_SW_pin[] PROGMEM = "SW-pin"; const char RotaryEncoderUIUsermod::_SW_pin[] PROGMEM = "SW-pin";
const char RotaryEncoderUIUsermod::_presetHigh[] PROGMEM = "preset-high"; const char RotaryEncoderUIUsermod::_presetHigh[] PROGMEM = "preset-high";
const char RotaryEncoderUIUsermod::_presetLow[] PROGMEM = "preset-low"; const char RotaryEncoderUIUsermod::_presetLow[] PROGMEM = "preset-low";
const char RotaryEncoderUIUsermod::_applyToAll[] PROGMEM = "apply-2-all-seg";

View File

@ -390,7 +390,7 @@ uint8_t WS2812FX::getPaletteCount()
//TODO effect transitions //TODO effect transitions
/*
bool WS2812FX::setEffectConfig(uint8_t m, uint8_t s, uint8_t in, uint8_t p) { bool WS2812FX::setEffectConfig(uint8_t m, uint8_t s, uint8_t in, uint8_t p) {
Segment& seg = _segments[getMainSegmentId()]; Segment& seg = _segments[getMainSegmentId()];
uint8_t modePrev = seg.mode, speedPrev = seg.speed, intensityPrev = seg.intensity, palettePrev = seg.palette; uint8_t modePrev = seg.mode, speedPrev = seg.speed, intensityPrev = seg.intensity, palettePrev = seg.palette;
@ -425,7 +425,7 @@ bool WS2812FX::setEffectConfig(uint8_t m, uint8_t s, uint8_t in, uint8_t p) {
void WS2812FX::setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w) { void WS2812FX::setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
setColor(slot, RGBW32(r, g, b, w)); setColor(slot, RGBW32(r, g, b, w));
} }
*/
void WS2812FX::setColor(uint8_t slot, uint32_t c) { void WS2812FX::setColor(uint8_t slot, uint32_t c) {
if (slot >= NUM_COLORS) return; if (slot >= NUM_COLORS) return;

View File

@ -257,6 +257,7 @@ void handleButton()
if (b == 0 && dur > WLED_LONG_AP) { //long press on button 0 (when released) if (b == 0 && dur > WLED_LONG_AP) { //long press on button 0 (when released)
WLED::instance().initAP(true); WLED::instance().initAP(true);
} else if (!buttonLongPressed[b]) { //short press } else if (!buttonLongPressed[b]) { //short press
//NOTE: this interferes with double click handling in usermods so it is commented out
if (b == 0 && !macroDoublePress[b]) { //don't wait for double press on button 0 if no double press macro set if (b == 0 && !macroDoublePress[b]) { //don't wait for double press on button 0 if no double press macro set
shortPressAction(b); shortPressAction(b);
} else { //double press if less than 350 ms between current press and previous short press release (buttonWaitTime!=0) } else { //double press if less than 350 ms between current press and previous short press release (buttonWaitTime!=0)

View File

@ -22,11 +22,6 @@ void colorFromUint24(uint32_t in, bool secondary)
_col[2] = B(in); _col[2] = B(in);
} }
//store color components in uint32_t
uint32_t colorFromRgbw(byte* rgbw) {
return RGBW32(rgbw[0], rgbw[1], rgbw[2], rgbw[3]);
}
//relatively change white brightness, minumum A=5 //relatively change white brightness, minumum A=5
void relativeChangeWhite(int8_t amount, byte lowerBoundary) void relativeChangeWhite(int8_t amount, byte lowerBoundary)
{ {
@ -259,7 +254,7 @@ uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb)
rgbw[1] = ((uint16_t) correctionRGB[1] * G(rgb)) /255; // correct G rgbw[1] = ((uint16_t) correctionRGB[1] * G(rgb)) /255; // correct G
rgbw[2] = ((uint16_t) correctionRGB[2] * B(rgb)) /255; // correct B rgbw[2] = ((uint16_t) correctionRGB[2] * B(rgb)) /255; // correct B
rgbw[3] = W(rgb); rgbw[3] = W(rgb);
return colorFromRgbw(rgbw); return RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3]);
} }
//approximates a Kelvin color temperature from an RGB color. //approximates a Kelvin color temperature from an RGB color.

View File

@ -38,12 +38,17 @@
l.addEventListener('load', (e) => { l.addEventListener('load', (e) => {
// after rangetouch is loaded initialize global variable // after rangetouch is loaded initialize global variable
ranges = RangeTouch.setup('input[type="range"]', {}); ranges = RangeTouch.setup('input[type="range"]', {});
onLoad(); // start processing UI let stateCheck = setInterval(() => {
if (document.readyState === 'complete') {
clearInterval(stateCheck);
// document ready, start processing UI
onLoad();
}
}, 100);
}); });
//h.appendChild(l); // if this fires too quickly for ESP8266 use next line setTimeout(()=>{h.appendChild(l)},50);
setTimeout(function(){h.appendChild(l)},50);
}); });
setTimeout(function(){h.appendChild(l)},50); setTimeout(()=>{h.appendChild(l)},50);
</script> </script>
<link rel="stylesheet" href="index.css"> <link rel="stylesheet" href="index.css">
</head> </head>

View File

@ -10,7 +10,6 @@ var nlDur = 60, nlTar = 0;
var nlMode = false; var nlMode = false;
var selectedFx = 0, prevFx = -1; var selectedFx = 0, prevFx = -1;
var selectedPal = 0; var selectedPal = 0;
var sliderControl = ""; //WLEDSR: used by togglePcMode
var csel = 0; var csel = 0;
var currentPreset = -1, prevPS = -1; var currentPreset = -1, prevPS = -1;
var lastUpdate = 0; var lastUpdate = 0;
@ -1614,6 +1613,11 @@ ${makePlSel(true)}
Save segment bounds Save segment bounds
<input type="checkbox" id="p${i}sbtgl" checked> <input type="checkbox" id="p${i}sbtgl" checked>
<span class="checkmark schk"></span> <span class="checkmark schk"></span>
</label>
<label class="check revchkl">
Checked segments only
<input type="checkbox" id="p${i}sbchk">
<span class="checkmark schk"></span>
</label>`; </label>`;
return `<input type="text" class="ptxt noslide" id="p${i}txt" autocomplete="off" maxlength=32 value="${(i>0)?pName(i):""}" placeholder="Enter name..."/> return `<input type="text" class="ptxt noslide" id="p${i}txt" autocomplete="off" maxlength=32 value="${(i>0)?pName(i):""}" placeholder="Enter name..."/>
@ -1911,6 +1915,7 @@ function saveP(i,pl)
} else { } else {
obj.ib = gId(`p${i}ibtgl`).checked; obj.ib = gId(`p${i}ibtgl`).checked;
obj.sb = gId(`p${i}sbtgl`).checked; obj.sb = gId(`p${i}sbtgl`).checked;
obj.sc = gId(`p${i}sbchk`).checked;
} }
} }
@ -2091,8 +2096,15 @@ function setBalance(b)
} }
var hc = 0; var hc = 0;
setInterval(()=>{if (!isInfo) return; hc+=18; if (hc>300) hc=0; if (hc>200)hc=306; if (hc==144) hc+=36; if (hc==108) hc+=18; setInterval(()=>{
gId('heart').style.color = `hsl(${hc}, 100%, 50%)`;}, 910); if (!isInfo) return;
hc+=18;
if (hc>300) hc=0;
if (hc>200)hc=306;
if (hc==144) hc+=36;
if (hc==108) hc+=18;
gId('heart').style.color = `hsl(${hc}, 100%, 50%)`;
}, 910);
function openGH() { window.open("https://github.com/Aircoookie/WLED/wiki"); } function openGH() { window.open("https://github.com/Aircoookie/WLED/wiki"); }
@ -2361,7 +2373,7 @@ function togglePcMode(fromB = false)
if (wW < 1250 && !pcMode) return; if (wW < 1250 && !pcMode) return;
if (!fromB && ((wW < 1250 && lastw < 1250) || (wW >= 1250 && lastw >= 1250))) return; if (!fromB && ((wW < 1250 && lastw < 1250) || (wW >= 1250 && lastw >= 1250))) return;
openTab(0, true); openTab(0, true);
if (w < 1250) {pcMode = false;} if (wW < 1250) {pcMode = false;}
else if (pcModeA && !fromB) pcMode = pcModeA; else if (pcModeA && !fromB) pcMode = pcModeA;
updateTablinks(0); updateTablinks(0);
gId('buttonPcm').className = (pcMode) ? "active":""; gId('buttonPcm').className = (pcMode) ? "active":"";

View File

@ -23,6 +23,8 @@ void updateBlynk();
//button.cpp //button.cpp
void shortPressAction(uint8_t b=0); void shortPressAction(uint8_t b=0);
void longPressAction(uint8_t b=0);
void doublePressAction(uint8_t b=0);
bool isButtonPressed(uint8_t b=0); bool isButtonPressed(uint8_t b=0);
void handleButton(); void handleButton();
void handleIO(); void handleIO();
@ -58,7 +60,7 @@ bool getJsonValue(const JsonVariant& element, DestType& destination, const Defau
//colors.cpp //colors.cpp
void colorFromUint32(uint32_t in, bool secondary = false); void colorFromUint32(uint32_t in, bool secondary = false);
void colorFromUint24(uint32_t in, bool secondary = false); void colorFromUint24(uint32_t in, bool secondary = false);
uint32_t colorFromRgbw(byte* rgbw); inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); }
void relativeChangeWhite(int8_t amount, byte lowerBoundary = 0); void relativeChangeWhite(int8_t amount, byte lowerBoundary = 0);
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb
void colorKtoRGB(uint16_t kelvin, byte* rgb); void colorKtoRGB(uint16_t kelvin, byte* rgb);
@ -146,7 +148,6 @@ void resetTimebase();
void toggleOnOff(); void toggleOnOff();
void setAllLeds(); void setAllLeds();
void setLedsStandard(); void setLedsStandard();
bool colorChanged();
void colorUpdated(int callMode); void colorUpdated(int callMode);
void updateInterfaces(uint8_t callMode); void updateInterfaces(uint8_t callMode);
void handleTransitions(); void handleTransitions();

File diff suppressed because it is too large Load Diff

View File

@ -131,14 +131,11 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
} }
if (!colValid) continue; if (!colValid) continue;
if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment
{ uint32_t color = RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3]);
if (i == 0) {col[0] = rgbw[0]; col[1] = rgbw[1]; col[2] = rgbw[2]; col[3] = rgbw[3];} colorChanged |= (seg.colors[i] != color);
if (i == 1) {colSec[0] = rgbw[0]; colSec[1] = rgbw[1]; colSec[2] = rgbw[2]; colSec[3] = rgbw[3];} seg.setColor(i, color, id);
} else { //normal case, apply directly to segment if (seg.mode == FX_MODE_STATIC) strip.trigger(); //instant refresh
seg.setColor(i, ((rgbw[3] << 24) | ((rgbw[0]&0xFF) << 16) | ((rgbw[1]&0xFF) << 8) | ((rgbw[2]&0xFF))), id);
if (seg.mode == FX_MODE_STATIC) strip.trigger(); //instant refresh
}
} }
} }
@ -161,26 +158,18 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
if (!(elem[F("sel")].isNull() && elem["rev"].isNull() && elem["on"].isNull() && elem[F("mi")].isNull())) effectChanged = true; //send UDP if (!(elem[F("sel")].isNull() && elem["rev"].isNull() && elem["on"].isNull() && elem[F("mi")].isNull())) effectChanged = true; //send UDP
//temporary, strip object gets updated via colorUpdated() byte fx = seg.mode;
if (id == strip.getMainSegmentId()) { byte fxPrev = fx;
byte effectPrev = effectCurrent; if (getVal(elem["fx"], &fx, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 1-255 exact value)
if (getVal(elem["fx"], &effectCurrent, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 1-255 exact value) strip.setMode(id, fx);
if (!presetId && effectCurrent != effectPrev) unloadPlaylist(); //stop playlist if active and FX changed manually if (!presetId && seg.mode != fxPrev) effectChanged = true; //send UDP
}
effectSpeed = elem[F("sx")] | effectSpeed;
effectIntensity = elem[F("ix")] | effectIntensity;
getVal(elem["pal"], &effectPalette, 1, strip.getPaletteCount());
} else { //permanent
byte fx = seg.mode;
byte fxPrev = fx;
if (getVal(elem["fx"], &fx, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 1-255 exact value)
strip.setMode(id, fx);
if (!presetId && seg.mode != fxPrev) unloadPlaylist(); //stop playlist if active and FX changed manually
}
seg.speed = elem[F("sx")] | seg.speed;
seg.intensity = elem[F("ix")] | seg.intensity;
getVal(elem["pal"], &seg.palette, 1, strip.getPaletteCount());
} }
byte prevSpd = seg.speed;
byte prevInt = seg.intensity;
byte prevPal = seg.palette;
if (getVal(elem[F("sx")], &seg.speed, 0, 255) && !presetId && prevSpd != seg.speed) effectChanged = true; //also supports inc/decrementing and random
if (getVal(elem[F("ix")], &seg.intensity, 0, 255) && !presetId && prevInt != seg.intensity) effectChanged = true; //also supports inc/decrementing and random
if (getVal(elem["pal"], &seg.palette, 1, strip.getPaletteCount()) && !presetId && prevPal != seg.palette) effectChanged = true; //also supports inc/decrementing and random
JsonArray iarr = elem[F("i")]; //set individual LEDs JsonArray iarr = elem[F("i")]; //set individual LEDs
if (!iarr.isNull()) { if (!iarr.isNull()) {
@ -307,32 +296,32 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
byte prevMain = strip.getMainSegmentId(); byte prevMain = strip.getMainSegmentId();
strip.mainSegment = root[F("mainseg")] | prevMain; strip.mainSegment = root[F("mainseg")] | prevMain;
if (strip.getMainSegmentId() != prevMain) setValuesFromMainSeg(); //if (strip.getMainSegmentId() != prevMain) setValuesFromMainSeg();
int it = 0; int it = 0;
JsonVariant segVar = root["seg"]; JsonVariant segVar = root["seg"];
if (segVar.is<JsonObject>()) if (segVar.is<JsonObject>())
{ {
int id = segVar["id"] | -1; int id = segVar["id"] | -1;
//if "seg" is not an array and ID not specified, apply to all selected/checked segments
if (id < 0) { //set all selected segments if (id < 0) {
bool didSet = false; //apply all selected segments
byte lowestActive = 99; //bool didSet = false;
for (byte s = 0; s < strip.getMaxSegments(); s++) //byte lowestActive = 99;
{ for (byte s = 0; s < strip.getMaxSegments(); s++) {
WS2812FX::Segment &sg = strip.getSegment(s); WS2812FX::Segment &sg = strip.getSegment(s);
if (sg.isActive()) if (sg.isActive()) {
{ //if (lowestActive == 99) lowestActive = s;
if (lowestActive == 99) lowestActive = s;
if (sg.isSelected()) { if (sg.isSelected()) {
deserializeSegment(segVar, s, presetId); deserializeSegment(segVar, s, presetId);
didSet = true; //didSet = true;
} }
} }
} }
if (!didSet && lowestActive < strip.getMaxSegments()) deserializeSegment(segVar, lowestActive, presetId); //TODO: not sure if it is good idea to change first active but unselected segment
} else { //set only the segment with the specified ID //if (!didSet && lowestActive < strip.getMaxSegments()) deserializeSegment(segVar, lowestActive, presetId);
deserializeSegment(segVar, it, presetId); } else {
deserializeSegment(segVar, id, presetId); //apply only the segment with the specified ID
} }
} else { } else {
JsonArray segs = segVar.as<JsonArray>(); JsonArray segs = segVar.as<JsonArray>();
@ -342,6 +331,8 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
it++; it++;
} }
} }
setValuesFromMainSeg(); //to make transition work on main segment
if (effectChanged) unloadPlaylist(); //if any of the effect parameter changed unload playlist
#ifndef WLED_DISABLE_CRONIXIE #ifndef WLED_DISABLE_CRONIXIE
if (root["nx"].is<const char*>()) { if (root["nx"].is<const char*>()) {
@ -437,12 +428,12 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
strcat_P(colstr, PSTR("]")); strcat_P(colstr, PSTR("]"));
root["col"] = serialized(colstr); root["col"] = serialized(colstr);
root["fx"] = seg.mode; root["fx"] = seg.mode;
root[F("sx")] = seg.speed; root[F("sx")] = seg.speed;
root[F("ix")] = seg.intensity; root[F("ix")] = seg.intensity;
root["pal"] = seg.palette; root["pal"] = seg.palette;
root[F("sel")] = seg.isSelected(); root[F("sel")] = seg.isSelected();
root["rev"] = seg.getOption(SEG_OPTION_REVERSED); root["rev"] = seg.getOption(SEG_OPTION_REVERSED);
root[F("mi")] = seg.getOption(SEG_OPTION_MIRROR); root[F("mi")] = seg.getOption(SEG_OPTION_MIRROR);
} }
@ -483,12 +474,12 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
root[F("mainseg")] = strip.getMainSegmentId(); root[F("mainseg")] = strip.getMainSegmentId();
bool selectedSegmentsOnly = root[F("sc")] | false;
JsonArray seg = root.createNestedArray("seg"); JsonArray seg = root.createNestedArray("seg");
for (byte s = 0; s < strip.getMaxSegments(); s++) for (byte s = 0; s < strip.getMaxSegments(); s++) {
{
WS2812FX::Segment &sg = strip.getSegment(s); WS2812FX::Segment &sg = strip.getSegment(s);
if (sg.isActive()) if (selectedSegmentsOnly && !sg.isSelected()) continue;
{ if (sg.isActive()) {
JsonObject seg0 = seg.createNestedObject(); JsonObject seg0 = seg.createNestedObject();
serializeSegment(seg0, sg, s, forPreset, segmentBounds); serializeSegment(seg0, sg, s, forPreset, segmentBounds);
} else if (forPreset && segmentBounds) { //disable segments not part of preset } else if (forPreset && segmentBounds) { //disable segments not part of preset

View File

@ -44,8 +44,8 @@ byte scaledBri(byte in)
void setAllLeds() { void setAllLeds() {
strip.setColor(0, col[0], col[1], col[2], col[3]); strip.setColor(0, RGBW32(col[0], col[1], col[2], col[3]));
strip.setColor(1, colSec[0], colSec[1], colSec[2], colSec[3]); strip.setColor(1, RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]));
if (!realtimeMode || !arlsForceMaxBri) if (!realtimeMode || !arlsForceMaxBri)
{ {
strip.setBrightness(scaledBri(briT)); strip.setBrightness(scaledBri(briT));
@ -61,106 +61,67 @@ void setLedsStandard()
} }
bool colorChanged()
{
for (byte i=0; i<4; i++)
{
if (col[i] != colIT[i]) return true;
if (colSec[i] != colSecIT[i]) return true;
}
if (bri != briIT) return true;
return false;
}
void colorUpdated(int callMode) void colorUpdated(int callMode)
{ {
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification) //call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification)
// 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa 11: ws send only 12: button preset // 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa 11: ws send only 12: button preset
if (callMode != CALL_MODE_INIT &&
callMode != CALL_MODE_DIRECT_CHANGE &&
callMode != CALL_MODE_NO_NOTIFY &&
callMode != CALL_MODE_BUTTON_PRESET) strip.applyToAllSelected = true; //if not from JSON api, which directly sets segments
bool someSel = false; if (bri != briOld || effectChanged || colorChanged) {
if (callMode == CALL_MODE_NOTIFICATION) {
someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects || receiveSegmentOptions);
}
//Notifier: apply received FX to selected segments only if actually receiving FX
if (someSel) strip.applyToAllSelected = receiveNotificationEffects;
bool fxChanged = strip.setEffectConfig(effectCurrent, effectSpeed, effectIntensity, effectPalette) || effectChanged;
bool colChanged = colorChanged();
//Notifier: apply received color to selected segments only if actually receiving color
if (someSel) strip.applyToAllSelected = receiveNotificationColor;
if (fxChanged || colChanged)
{
effectChanged = false;
if (realtimeTimeout == UINT32_MAX) realtimeTimeout = 0; if (realtimeTimeout == UINT32_MAX) realtimeTimeout = 0;
currentPreset = 0; //something changed, so we are no longer in the preset if (effectChanged) currentPreset = 0; //something changed, so we are no longer in the preset
notify(callMode); if (callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) notify(callMode);
//set flag to update blynk, ws and mqtt //set flag to update blynk, ws and mqtt
interfaceUpdateCallMode = callMode; interfaceUpdateCallMode = callMode;
effectChanged = false;
colorChanged = false;
} else { } else {
if (nightlightActive && !nightlightActiveOld && if (nightlightActive && !nightlightActiveOld && callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) {
callMode != CALL_MODE_NOTIFICATION &&
callMode != CALL_MODE_NO_NOTIFY)
{
notify(CALL_MODE_NIGHTLIGHT); notify(CALL_MODE_NIGHTLIGHT);
interfaceUpdateCallMode = CALL_MODE_NIGHTLIGHT; interfaceUpdateCallMode = CALL_MODE_NIGHTLIGHT;
} }
} }
if (!colChanged) return; //following code is for e.g. initiating transitions if (callMode != CALL_MODE_NO_NOTIFY && nightlightActive && (nightlightMode == NL_MODE_FADE || nightlightMode == NL_MODE_COLORFADE)) {
if (callMode != CALL_MODE_NO_NOTIFY && nightlightActive && (nightlightMode == NL_MODE_FADE || nightlightMode == NL_MODE_COLORFADE))
{
briNlT = bri; briNlT = bri;
nightlightDelayMs -= (millis() - nightlightStartTime); nightlightDelayMs -= (millis() - nightlightStartTime);
nightlightStartTime = millis(); nightlightStartTime = millis();
} }
for (byte i=0; i<4; i++) if (briT == 0) {
{
colIT[i] = col[i];
colSecIT[i] = colSec[i];
}
if (briT == 0)
{
if (callMode != CALL_MODE_NOTIFICATION) resetTimebase(); //effect start from beginning if (callMode != CALL_MODE_NOTIFICATION) resetTimebase(); //effect start from beginning
} }
briIT = bri;
if (bri > 0) briLast = bri; if (bri > 0) briLast = bri;
//deactivate nightlight if target brightness is reached //deactivate nightlight if target brightness is reached
if (bri == nightlightTargetBri && callMode != CALL_MODE_NO_NOTIFY && nightlightMode != NL_MODE_SUN) nightlightActive = false; if (bri == nightlightTargetBri && callMode != CALL_MODE_NO_NOTIFY && nightlightMode != NL_MODE_SUN) nightlightActive = false;
if (fadeTransition) if (fadeTransition) {
{
//set correct delay if not using notification delay //set correct delay if not using notification delay
if (callMode != CALL_MODE_NOTIFICATION && !jsonTransitionOnce) transitionDelayTemp = transitionDelay; if (callMode != CALL_MODE_NOTIFICATION && !jsonTransitionOnce) transitionDelayTemp = transitionDelay;
jsonTransitionOnce = false; jsonTransitionOnce = false;
strip.setTransition(transitionDelayTemp); strip.setTransition(transitionDelayTemp);
if (transitionDelayTemp == 0) {setLedsStandard(); strip.trigger(); return;} if (transitionDelayTemp == 0) {
//setLedsStandard();
if (transitionActive) briOld = briT = bri;
{ if (!realtimeMode || !arlsForceMaxBri) strip.setBrightness(scaledBri(briT));
strip.trigger();
return;
}
if (transitionActive) {
briOld = briT; briOld = briT;
tperLast = 0; tperLast = 0;
} }
strip.setTransitionMode(true); strip.setTransitionMode(true);
transitionActive = true; transitionActive = true;
transitionStartTime = millis(); transitionStartTime = millis();
} else } else {
{
strip.setTransition(0); strip.setTransition(0);
setLedsStandard(); //setLedsStandard();
briOld = briT = bri;
if (!realtimeMode || !arlsForceMaxBri) strip.setBrightness(scaledBri(briT));
strip.trigger(); strip.trigger();
} }
} }
@ -218,6 +179,12 @@ void handleTransitions()
void handleNightlight() void handleNightlight()
{ {
static unsigned long lastNlUpdate;
unsigned long now = millis();
if (now < 100 && lastNlUpdate > 0) lastNlUpdate = 0; //take care of millis() rollover
if (now - lastNlUpdate < 100) return; //allow only 10 NL updates per second
lastNlUpdate = now;
if (nightlightActive) if (nightlightActive)
{ {
if (!nightlightActiveOld) //init if (!nightlightActiveOld) //init

View File

@ -63,7 +63,6 @@ void handlePresets()
if (!errorFlag && presetToApply < 255) currentPreset = presetToApply; if (!errorFlag && presetToApply < 255) currentPreset = presetToApply;
effectChanged = true; //force UDP notification
colorUpdated(callModeToApply); colorUpdated(callModeToApply);
updateInterfaces(callModeToApply); updateInterfaces(callModeToApply);
@ -102,6 +101,7 @@ void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj)
sObj.remove("o"); sObj.remove("o");
sObj.remove("ib"); sObj.remove("ib");
sObj.remove("sb"); sObj.remove("sb");
sObj.remove(F("sc"));
sObj.remove(F("error")); sObj.remove(F("error"));
sObj.remove(F("time")); sObj.remove(F("time"));

View File

@ -532,7 +532,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
DEBUG_PRINT(F("API req: ")); DEBUG_PRINT(F("API req: "));
DEBUG_PRINTLN(req); DEBUG_PRINTLN(req);
strip.applyToAllSelected = false; strip.applyToAllSelected = true;
//segment select (sets main segment) //segment select (sets main segment)
byte prevMain = strip.getMainSegmentId(); byte prevMain = strip.getMainSegmentId();
@ -543,22 +543,28 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
byte selectedSeg = strip.getMainSegmentId(); byte selectedSeg = strip.getMainSegmentId();
if (selectedSeg != prevMain) setValuesFromMainSeg(); if (selectedSeg != prevMain) setValuesFromMainSeg();
//snapshot to check if request changed values later, temporary.
byte prevCol[4] = {col[0], col[1], col[2], col[3]};
byte prevColSec[4] = {colSec[0], colSec[1], colSec[2], colSec[3]};
byte prevEffect = effectCurrent;
byte prevSpeed = effectSpeed;
byte prevIntensity = effectIntensity;
byte prevPalette = effectPalette;
pos = req.indexOf(F("SS=")); pos = req.indexOf(F("SS="));
if (pos > 0) { if (pos > 0) {
byte t = getNumVal(&req, pos); byte t = getNumVal(&req, pos);
if (t < strip.getMaxSegments()) selectedSeg = t; if (t < strip.getMaxSegments()) {
selectedSeg = t;
strip.applyToAllSelected = false;
}
} }
WS2812FX::Segment& selseg = strip.getSegment(selectedSeg); WS2812FX::Segment& selseg = strip.getSegment(selectedSeg);
pos = req.indexOf(F("SV=")); //segment selected pos = req.indexOf(F("SV=")); //segment selected
if (pos > 0) { if (pos > 0) {
byte t = getNumVal(&req, pos); byte t = getNumVal(&req, pos);
if (t == 2) { if (t == 2) for (uint8_t i = 0; i < strip.getMaxSegments(); i++) strip.getSegment(i).setOption(SEG_OPTION_SELECTED, 0); // unselect other segments
for (uint8_t i = 0; i < strip.getMaxSegments(); i++)
{
strip.getSegment(i).setOption(SEG_OPTION_SELECTED, 0);
}
}
selseg.setOption(SEG_OPTION_SELECTED, t); selseg.setOption(SEG_OPTION_SELECTED, t);
} }
@ -624,26 +630,21 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
applyPreset(presetCycCurr); applyPreset(presetCycCurr);
} }
//snapshot to check if request changed values later, temporary.
byte prevCol[4] = {col[0], col[1], col[2], col[3]};
byte prevColSec[4] = {colSec[0], colSec[1], colSec[2], colSec[3]};
byte prevEffect = effectCurrent;
byte prevSpeed = effectSpeed;
byte prevIntensity = effectIntensity;
byte prevPalette = effectPalette;
//set brightness //set brightness
updateVal(&req, "&A=", &bri); updateVal(&req, "&A=", &bri);
bool col0Changed = false, col1Changed = false, col2Changed = false;
//set colors //set colors
updateVal(&req, "&R=", &col[0]); updateVal(&req, "&R=", &col[0]);
updateVal(&req, "&G=", &col[1]); updateVal(&req, "&G=", &col[1]);
updateVal(&req, "&B=", &col[2]); updateVal(&req, "&B=", &col[2]);
updateVal(&req, "&W=", &col[3]); updateVal(&req, "&W=", &col[3]);
for (byte i=0; i<4; i++) if (prevCol[i]!=col[i]) col0Changed = colorChanged = true;
updateVal(&req, "R2=", &colSec[0]); updateVal(&req, "R2=", &colSec[0]);
updateVal(&req, "G2=", &colSec[1]); updateVal(&req, "G2=", &colSec[1]);
updateVal(&req, "B2=", &colSec[2]); updateVal(&req, "B2=", &colSec[2]);
updateVal(&req, "W2=", &colSec[3]); updateVal(&req, "W2=", &colSec[3]);
for (byte i=0; i<4; i++) if (prevColSec[i]!=colSec[i]) col1Changed = colorChanged = true;
#ifdef WLED_ENABLE_LOXONE #ifdef WLED_ENABLE_LOXONE
//lox parser //lox parser
@ -674,52 +675,64 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
if (pos > 0) { if (pos > 0) {
tempsat = getNumVal(&req, pos); tempsat = getNumVal(&req, pos);
} }
colorHStoRGB(temphue,tempsat,(req.indexOf(F("H2"))>0)? colSec:col); byte sec = req.indexOf(F("H2"));
colorHStoRGB(temphue, tempsat, (sec>0) ? colSec : col);
if (sec>0) col1Changed = true;
else col0Changed = true;
colorChanged = true;
} }
//set white spectrum (kelvin) //set white spectrum (kelvin)
pos = req.indexOf(F("&K=")); pos = req.indexOf(F("&K="));
if (pos > 0) { if (pos > 0) {
colorKtoRGB(getNumVal(&req, pos),(req.indexOf(F("K2"))>0)? colSec:col); byte sec = req.indexOf(F("K2"));
colorKtoRGB(getNumVal(&req, pos), (sec>0) ? colSec : col);
if (sec>0) col1Changed = true;
else col0Changed = true;
colorChanged = true;
} }
//set color from HEX or 32bit DEC //set color from HEX or 32bit DEC
byte tmpCol[4];
pos = req.indexOf(F("CL=")); pos = req.indexOf(F("CL="));
if (pos > 0) { if (pos > 0) {
colorFromDecOrHexString(col, (char*)req.substring(pos + 3).c_str()); colorFromDecOrHexString(col, (char*)req.substring(pos + 3).c_str());
selseg.setColor(0, RGBW32(col[0], col[1], col[2], col[3]), selectedSeg); // defined above (SS= or main)
col0Changed = colorChanged = true;
} }
pos = req.indexOf(F("C2=")); pos = req.indexOf(F("C2="));
if (pos > 0) { if (pos > 0) {
colorFromDecOrHexString(colSec, (char*)req.substring(pos + 3).c_str()); colorFromDecOrHexString(colSec, (char*)req.substring(pos + 3).c_str());
selseg.setColor(1, RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]), selectedSeg); // defined above (SS= or main)
col1Changed = colorChanged = true;
} }
pos = req.indexOf(F("C3=")); pos = req.indexOf(F("C3="));
if (pos > 0) { if (pos > 0) {
byte t[4]; colorFromDecOrHexString(tmpCol, (char*)req.substring(pos + 3).c_str());
colorFromDecOrHexString(t, (char*)req.substring(pos + 3).c_str()); selseg.setColor(2, RGBW32(tmpCol[0], tmpCol[1], tmpCol[2], tmpCol[3]), selectedSeg); // defined above (SS= or main)
if (selectedSeg != strip.getMainSegmentId()) { col2Changed = colorChanged = true;
strip.applyToAllSelected = true;
strip.setColor(2, t[0], t[1], t[2], t[3]);
} else {
selseg.setColor(2, RGBW32(t[0], t[1], t[2], t[3]), selectedSeg); // defined above (SS=)
}
} }
//set to random hue SR=0->1st SR=1->2nd //set to random hue SR=0->1st SR=1->2nd
pos = req.indexOf(F("SR")); pos = req.indexOf(F("SR"));
if (pos > 0) { if (pos > 0) {
_setRandomColor(getNumVal(&req, pos)); byte sec = getNumVal(&req, pos);
_setRandomColor(sec);
if (sec>0) col1Changed = true;
else col0Changed = true;
colorChanged = true;
} }
//swap 2nd & 1st //swap 2nd & 1st
pos = req.indexOf(F("SC")); pos = req.indexOf(F("SC"));
if (pos > 0) { if (pos > 0) {
byte temp; byte temp;
for (uint8_t i=0; i<4; i++) for (uint8_t i=0; i<4; i++) {
{ temp = col[i];
temp = col[i]; col[i] = colSec[i];
col[i] = colSec[i];
colSec[i] = temp; colSec[i] = temp;
} }
col0Changed = col1Changed = colorChanged = true;
} }
//set effect parameters //set effect parameters
@ -727,6 +740,11 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
updateVal(&req, "SX=", &effectSpeed); updateVal(&req, "SX=", &effectSpeed);
updateVal(&req, "IX=", &effectIntensity); updateVal(&req, "IX=", &effectIntensity);
updateVal(&req, "FP=", &effectPalette, 0, strip.getPaletteCount()-1); updateVal(&req, "FP=", &effectPalette, 0, strip.getPaletteCount()-1);
strip.setMode(selectedSeg, effectCurrent);
selseg.speed = effectSpeed;
selseg.intensity = effectIntensity;
selseg.palette = effectPalette;
if (effectCurrent != prevEffect || effectSpeed != prevSpeed || effectIntensity != prevIntensity || effectPalette != prevPalette) effectChanged = true;
//set advanced overlay //set advanced overlay
pos = req.indexOf(F("OL=")); pos = req.indexOf(F("OL="));
@ -857,45 +875,21 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
//you can add more if you need //you can add more if you need
//apply to all selected manually to prevent #1618. Temporary //apply to all selected manually to prevent #1618. Temporary
bool col0Changed = false, col1Changed = false; if (strip.applyToAllSelected) {
for (uint8_t i = 0; i < 4; i++) { for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
if (col[i] != prevCol[i]) col0Changed = true; WS2812FX::Segment& seg = strip.getSegment(i);
if (colSec[i] != prevColSec[i]) col1Changed = true; if (!seg.isActive() || !seg.isSelected() || i == selectedSeg) continue;
} if (effectCurrent != prevEffect) strip.setMode(i, effectCurrent);
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) if (effectSpeed != prevSpeed) seg.speed = effectSpeed;
{ if (effectIntensity != prevIntensity) seg.intensity = effectIntensity;
WS2812FX::Segment& seg = strip.getSegment(i); if (effectPalette != prevPalette) seg.palette = effectPalette;
if (!seg.isSelected()) continue; if (col0Changed) seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
if (effectCurrent != prevEffect) { if (col1Changed) seg.colors[1] = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]);
strip.setMode(i, effectCurrent); if (col2Changed) seg.colors[2] = RGBW32(tmpCol[0], tmpCol[1], tmpCol[2], tmpCol[3]);
effectChanged = true;
}
if (effectSpeed != prevSpeed) {
seg.speed = effectSpeed;
effectChanged = true;
}
if (effectIntensity != prevIntensity) {
seg.intensity = effectIntensity;
effectChanged = true;
}
if (effectPalette != prevPalette) {
seg.palette = effectPalette;
effectChanged = true;
}
}
if (col0Changed) {
if (selectedSeg == strip.getMainSegmentId()) {
strip.applyToAllSelected = true;
strip.setColor(0, colorFromRgbw(col));
}
}
if (col1Changed) {
if (selectedSeg == strip.getMainSegmentId()) {
strip.applyToAllSelected = true;
strip.setColor(1, colorFromRgbw(colSec));
} }
} }
strip.applyToAllSelected = false;
setValuesFromMainSeg();
//end of temporary fix code //end of temporary fix code
if (!apply) return true; //when called by JSON API, do not call colorUpdated() here if (!apply) return true; //when called by JSON API, do not call colorUpdated() here
@ -904,8 +898,6 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
pos = req.indexOf(F("IN")); pos = req.indexOf(F("IN"));
if (pos < 1) XML_response(request); if (pos < 1) XML_response(request);
strip.applyToAllSelected = false;
pos = req.indexOf(F("&NN")); //do not send UDP notifications this time pos = req.indexOf(F("&NN")); //do not send UDP notifications this time
colorUpdated((pos > 0) ? CALL_MODE_NO_NOTIFY : CALL_MODE_DIRECT_CHANGE); colorUpdated((pos > 0) ? CALL_MODE_NO_NOTIFY : CALL_MODE_DIRECT_CHANGE);

View File

@ -4,7 +4,9 @@
* UDP sync notifier / Realtime / Hyperion / TPM2.NET * UDP sync notifier / Realtime / Hyperion / TPM2.NET
*/ */
#define WLEDPACKETSIZE (40+(MAX_NUM_SEGMENTS*3)) #define UDP_SEG_SIZE 22
#define SEG_OFFSET (41+(MAX_NUM_SEGMENTS*UDP_SEG_SIZE))
#define WLEDPACKETSIZE (41+(MAX_NUM_SEGMENTS*UDP_SEG_SIZE)+0)
#define UDP_IN_MAXSIZE 1472 #define UDP_IN_MAXSIZE 1472
#define PRESUMED_NETWORK_DELAY 3 //how many ms could it take on avg to reach the receiver? This will be added to transmitted times #define PRESUMED_NETWORK_DELAY 3 //how many ms could it take on avg to reach the receiver? This will be added to transmitted times
@ -86,13 +88,37 @@ void notify(byte callMode, bool followUp)
udpOut[38] = mainseg.cct; udpOut[38] = mainseg.cct;
udpOut[39] = strip.getMaxSegments(); udpOut[39] = strip.getMaxSegments();
udpOut[40] = UDP_SEG_SIZE; //size of each loop iteration (one segment)
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) { for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment &selseg = strip.getSegment(i); WS2812FX::Segment &selseg = strip.getSegment(i);
udpOut[40+i*3] = selseg.options & 0x0F; //only take into account mirrored, selected, on, reversed uint16_t ofs = 41 + i*UDP_SEG_SIZE; //start of segment offset byte
udpOut[41+i*3] = selseg.spacing; udpOut[0 +ofs] = selseg.grouping;
udpOut[42+i*3] = selseg.grouping; udpOut[1 +ofs] = selseg.spacing;
udpOut[2 +ofs] = selseg.offset >> 8;
udpOut[3 +ofs] = selseg.offset & 0xFF;
udpOut[4 +ofs] = selseg.options & 0x0F; //only take into account mirrored, selected, on, reversed
udpOut[5 +ofs] = selseg.opacity;
udpOut[6 +ofs] = selseg.mode;
udpOut[7 +ofs] = selseg.speed;
udpOut[8 +ofs] = selseg.intensity;
udpOut[9 +ofs] = selseg.palette;
udpOut[10+ofs] = R(selseg.colors[0]);
udpOut[11+ofs] = G(selseg.colors[0]);
udpOut[12+ofs] = B(selseg.colors[0]);
udpOut[13+ofs] = W(selseg.colors[0]);
udpOut[14+ofs] = R(selseg.colors[1]);
udpOut[15+ofs] = G(selseg.colors[1]);
udpOut[16+ofs] = B(selseg.colors[1]);
udpOut[17+ofs] = W(selseg.colors[1]);
udpOut[18+ofs] = R(selseg.colors[2]);
udpOut[19+ofs] = G(selseg.colors[2]);
udpOut[20+ofs] = B(selseg.colors[2]);
udpOut[21+ofs] = W(selseg.colors[2]);
} }
//uint16_t offs = SEG_OFFSET;
//next value to be added has index: udpOut[offs + 0]
IPAddress broadcastIp; IPAddress broadcastIp;
broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP()); broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP());
@ -256,24 +282,26 @@ void handleNotifications()
bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects); bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
//apply colors from notification //apply colors from notification
if (receiveNotificationColor || !someSel) if (receiveNotificationColor || !someSel) {
{ if (version < 11 || !receiveSegmentOptions) {
col[0] = udpIn[3]; // only change col[] if not syncing full segments
col[1] = udpIn[4]; col[0] = udpIn[3];
col[2] = udpIn[5]; col[1] = udpIn[4];
col[2] = udpIn[5];
}
if (version > 0) //sending module's white val is intended if (version > 0) //sending module's white val is intended
{ {
col[3] = udpIn[10]; // only change col[3] if not syncing full segments
if (version > 1) if (version < 11 || !receiveSegmentOptions) col[3] = udpIn[10];
{ if (version > 1 && (version < 11 || !receiveSegmentOptions)) {
// only change colSec[] if not syncing full segments
colSec[0] = udpIn[12]; colSec[0] = udpIn[12];
colSec[1] = udpIn[13]; colSec[1] = udpIn[13];
colSec[2] = udpIn[14]; colSec[2] = udpIn[14];
colSec[3] = udpIn[15]; colSec[3] = udpIn[15];
} }
if (version > 6) if (version > 6 && (version < 11 || !receiveSegmentOptions)) {
{ strip.setColor(2, RGBW32(udpIn[20], udpIn[21], udpIn[22], udpIn[23])); //tertiary color
strip.setColor(2, udpIn[20], udpIn[21], udpIn[22], udpIn[23]); //tertiary color
} }
if (version > 9 && version < 200 && udpIn[37] < 255) { //valid CCT/Kelvin value if (version > 9 && version < 200 && udpIn[37] < 255) { //valid CCT/Kelvin value
uint8_t cct = udpIn[38]; uint8_t cct = udpIn[38];
@ -291,24 +319,34 @@ void handleNotifications()
if (version < 200 && (receiveNotificationEffects || !someSel)) if (version < 200 && (receiveNotificationEffects || !someSel))
{ {
if (currentPlaylist>=0) unloadPlaylist(); if (currentPlaylist>=0) unloadPlaylist();
if (version>10) { if (version > 10 && receiveSegmentOptions) {
if (receiveSegmentOptions) { //does not sync start & stop
// will not sync start & stop uint8_t srcSegs = udpIn[39];
uint8_t srcSegs = udpIn[39]; if (srcSegs > strip.getMaxSegments()) srcSegs = strip.getMaxSegments();
if (srcSegs > strip.getMaxSegments()) srcSegs = strip.getMaxSegments(); for (uint8_t i = 0; i < srcSegs; i++) {
for (uint8_t i = 0; i < srcSegs; i++) { WS2812FX::Segment& selseg = strip.getSegment(i);
WS2812FX::Segment& selseg = strip.getSegment(i); uint16_t ofs = 41 + i*UDP_SEG_SIZE; //start of segment offset byte
for (uint8_t j = 0; j<4; j++) selseg.setOption(j, (udpIn[40+i*3] >> j) & 0x01); //only take into account mirrored, selected, on, reversed for (uint8_t j = 0; j<4; j++) selseg.setOption(j, (udpIn[4 +ofs] >> j) & 0x01); //only take into account mirrored, selected, on, reversed
strip.setSegment(i, selseg.start, selseg.stop, udpIn[42+i*3], udpIn[41+i*3], selseg.offset); // will also properly reset segments selseg.setOpacity( udpIn[5+ofs], i);
} strip.setMode(i, udpIn[6+ofs]);
} selseg.speed = udpIn[7+ofs];
} selseg.intensity = udpIn[8+ofs];
if (udpIn[8] < strip.getModeCount()) effectCurrent = udpIn[8]; selseg.palette = udpIn[9+ofs];
effectSpeed = udpIn[9]; selseg.setColor(0, RGBW32(udpIn[10+ofs],udpIn[11+ofs],udpIn[12+ofs],udpIn[13+ofs]), i);
if (version > 2) effectIntensity = udpIn[16]; selseg.setColor(1, RGBW32(udpIn[14+ofs],udpIn[15+ofs],udpIn[16+ofs],udpIn[17+ofs]), i);
if (version > 4 && udpIn[19] < strip.getPaletteCount()) effectPalette = udpIn[19]; selseg.setColor(2, RGBW32(udpIn[18+ofs],udpIn[19+ofs],udpIn[20+ofs],udpIn[21+ofs]), i);
if (version > 5) strip.setSegment(i, selseg.start, selseg.stop, udpIn[0+ofs], udpIn[1+ofs], (udpIn[2+ofs]<<8 | udpIn[3+ofs])); //also properly resets segments
{ }
setValuesFromMainSeg();
effectChanged = true;
colorChanged = true;
} else { //simple effect sync, applies to all selected
if (udpIn[8] < strip.getModeCount()) effectCurrent = udpIn[8];
effectSpeed = udpIn[9];
if (version > 2) effectIntensity = udpIn[16];
if (version > 4 && udpIn[19] < strip.getPaletteCount()) effectPalette = udpIn[19];
}
if (version > 5) {
uint32_t t = (udpIn[25] << 24) | (udpIn[26] << 16) | (udpIn[27] << 8) | (udpIn[28]); uint32_t t = (udpIn[25] << 24) | (udpIn[26] << 16) | (udpIn[27] << 8) | (udpIn[28]);
t += PRESUMED_NETWORK_DELAY; //adjust trivially for network delay t += PRESUMED_NETWORK_DELAY; //adjust trivially for network delay
t -= millis(); t -= millis();
@ -462,17 +500,18 @@ void handleNotifications()
// API over UDP // API over UDP
udpIn[packetSize] = '\0'; udpIn[packetSize] = '\0';
if (!requestJSONBufferLock(18)) return; if (requestJSONBufferLock(18)) {
if (udpIn[0] >= 'A' && udpIn[0] <= 'Z') { //HTTP API if (udpIn[0] >= 'A' && udpIn[0] <= 'Z') { //HTTP API
String apireq = "win&"; String apireq = "win&";
apireq += (char*)udpIn; apireq += (char*)udpIn;
handleSet(nullptr, apireq); handleSet(nullptr, apireq);
} else if (udpIn[0] == '{') { //JSON API } else if (udpIn[0] == '{') { //JSON API
DeserializationError error = deserializeJson(doc, udpIn); DeserializationError error = deserializeJson(doc, udpIn);
JsonObject root = doc.as<JsonObject>(); JsonObject root = doc.as<JsonObject>();
if (!error && !root.isNull()) deserializeState(root); if (!error && !root.isNull()) deserializeState(root);
}
releaseJSONBufferLock();
} }
releaseJSONBufferLock();
} }

View File

@ -187,7 +187,7 @@ void _setRandomColor(bool _sec, bool fromButton)
} else { } else {
colorHStoRGB(lastRandomIndex*256,255,col); colorHStoRGB(lastRandomIndex*256,255,col);
} }
if (fromButton) colorUpdated(2); if (fromButton) colorUpdated(CALL_MODE_BUTTON);
} }

View File

@ -8,7 +8,7 @@
*/ */
// version code in format yymmddb (b = daily build) // version code in format yymmddb (b = daily build)
#define VERSION 2201111 #define VERSION 2201221
//uncomment this if you have a "my_config.h" file you'd like to use //uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG //#define WLED_USE_MY_CONFIG
@ -16,6 +16,7 @@
// ESP8266-01 (blue) got too little storage space to work with WLED. 0.10.2 is the last release supporting this unit. // ESP8266-01 (blue) got too little storage space to work with WLED. 0.10.2 is the last release supporting this unit.
// ESP8266-01 (black) has 1MB flash and can thus fit the whole program, although OTA update is not possible. Use 1M(128K SPIFFS). // ESP8266-01 (black) has 1MB flash and can thus fit the whole program, although OTA update is not possible. Use 1M(128K SPIFFS).
// 2-step OTA may still be possible: https://github.com/Aircoookie/WLED/issues/2040#issuecomment-981111096
// Uncomment some of the following lines to disable features: // Uncomment some of the following lines to disable features:
// Alternatively, with platformio pass your chosen flags to your custom build target in platformio_override.ini // Alternatively, with platformio pass your chosen flags to your custom build target in platformio_override.ini
@ -422,9 +423,6 @@ WLED_GLOBAL bool interfacesInited _INIT(false);
WLED_GLOBAL bool wasConnected _INIT(false); WLED_GLOBAL bool wasConnected _INIT(false);
// color // color
WLED_GLOBAL byte colIT[] _INIT_N(({ 0, 0, 0, 0 })); // color that was last sent to LEDs
WLED_GLOBAL byte colSecIT[] _INIT_N(({ 0, 0, 0, 0 }));
WLED_GLOBAL byte lastRandomIndex _INIT(0); // used to save last random color so the new one is not the same WLED_GLOBAL byte lastRandomIndex _INIT(0); // used to save last random color so the new one is not the same
// transitions // transitions
@ -475,6 +473,7 @@ WLED_GLOBAL byte effectSpeed _INIT(128);
WLED_GLOBAL byte effectIntensity _INIT(128); WLED_GLOBAL byte effectIntensity _INIT(128);
WLED_GLOBAL byte effectPalette _INIT(0); WLED_GLOBAL byte effectPalette _INIT(0);
WLED_GLOBAL bool effectChanged _INIT(false); WLED_GLOBAL bool effectChanged _INIT(false);
WLED_GLOBAL bool colorChanged _INIT(false);
// network // network
WLED_GLOBAL bool udpConnected _INIT(false), udp2Connected _INIT(false), udpRgbConnected _INIT(false); WLED_GLOBAL bool udpConnected _INIT(false), udp2Connected _INIT(false), udpRgbConnected _INIT(false);