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
bool initDone = false;
bool HAautodiscovery = false;
uint16_t periodicBroadcastSec = 60;
unsigned long lastBroadcast = 0;
@ -57,13 +59,14 @@ class MultiRelay : public Usermod {
static const char _external[];
static const char _button[];
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
if (WLED_MQTT_CONNECTED){
char subuf[64];
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);
_relay[i].active = false;
} 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;
}
@ -112,7 +115,7 @@ class MultiRelay : public Usermod {
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
int value = getValue(p->value(), ',', i);
if (value==-1) {
error = F("There must be as much arugments as relays");
error = F("There must be as many arguments as relays");
} else {
// Switch
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++) {
int value = getValue(p->value(), ',', i);
if (value==-1) {
error = F("There must be as mutch arugments as relays");
error = F("There must be as many arguments as relays");
} else {
// Toggle
if (value && _relay[i].external) toggleRelay(i);
@ -206,7 +209,7 @@ class MultiRelay : public Usermod {
_relay[relay].state = mode;
pinMode(_relay[relay].pin, OUTPUT);
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);
strcat_P(subuf, PSTR("/relay/#"));
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) {
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;
}
bool handled = false;
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;
}
}
@ -362,6 +414,8 @@ class MultiRelay : public Usermod {
buttonPressedBefore[b] = true;
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;
}
@ -378,7 +432,8 @@ class MultiRelay : public Usermod {
if (!buttonLongPressed[b]) { //short press
// if this is second release within 350ms it is a double press (buttonWaitTime!=0)
if (doublePress) {
//doublePressAction(b);
//doublePressAction(b); //not exposed
//handled = false; //use if you want to pass to default behaviour
} else {
buttonWaitTime[b] = now;
}
@ -386,9 +441,10 @@ class MultiRelay : public Usermod {
buttonPressedBefore[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]) {
buttonWaitTime[b] = 0;
//shortPressAction(b); //not exposed
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].pin>=0 && _relay[i].button == b) {
toggleRelay(i);
@ -494,6 +550,7 @@ class MultiRelay : public Usermod {
relay[FPSTR(_external)] = _relay[i].external;
relay[FPSTR(_button)] = _relay[i].button;
}
top[FPSTR(_HAautodiscovery)] = HAautodiscovery;
DEBUG_PRINTLN(F("MultiRelay config saved."));
}
@ -516,6 +573,7 @@ class MultiRelay : public Usermod {
enabled = top[FPSTR(_enabled)] | enabled;
periodicBroadcastSec = top[FPSTR(_broadcast)] | periodicBroadcastSec;
periodicBroadcastSec = min(900,max(0,(int)periodicBroadcastSec));
HAautodiscovery = top[FPSTR(_HAautodiscovery)] | HAautodiscovery;
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; 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::_button[] PROGMEM = "button";
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)
/*
Fontname: wled_logo_akemi_4x4
Copyright:
Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 3/3
BBX Build Mode: 3
* 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
Copyright: Created with Fony 1.4.7
Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 3/3
BBX Build Mode: 3
* 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
Copyright: Created with Fony 1.4.7
Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 4/4
BBX Build Mode: 3
* 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
Copyright:
Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 13/13
BBX Build Mode: 3
* 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";
/*
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
@ -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"
"\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
Copyright:
Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 11/11
BBX Build Mode: 3
* 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
Copyright:
Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 11/11
BBX Build Mode: 3
* 1 = sun
@ -256,6 +334,7 @@ const uint8_t u8x8_4LineDisplay_WLED_icons_4x4[1540] U8X8_FONT_SECTION("u8x8_4Li
* 11 = heart
* 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") =
"\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"
@ -371,7 +450,7 @@ const uint8_t u8x8_4LineDisplay_WLED_icons_6x6[3460] U8X8_FONT_SECTION("u8x8_4Li
/*
Fontname: akemi_8x8
Copyright: Created with Fony 1.4.7
Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 1/1
BBX Build Mode: 3
* 12 = Akemi

View File

@ -119,6 +119,7 @@ class FourLineDisplayUsermod : public Usermod {
// HW interface & configuration
U8X8 *u8x8 = nullptr; // pointer to U8X8 display object
#ifndef FLD_SPI_DEFAULT
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)
@ -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
uint32_t ioFrequency = 1000000; // in Hz (minimum is 500kHz, baseline is 1MHz and maximum should be 20MHz)
#endif
DisplayType type = FLD_TYPE; // display type
bool flip = false; // flip display 180°
uint8_t contrast = 10; // screen contrast
@ -136,6 +138,7 @@ class FourLineDisplayUsermod : public Usermod {
bool clockMode = false; // display clock
bool showSeconds = true; // display clock with seconds
bool enabled = true;
bool contrastFix = false;
// Next variables hold the previous known values to determine if redraw is
// required.
@ -175,6 +178,7 @@ class FourLineDisplayUsermod : public Usermod {
static const char _clockMode[];
static const char _showSeconds[];
static const char _busClkFrequency[];
static const char _contrastFix[];
// If display does not work or looks corrupted check the
// constructor reference:
@ -182,6 +186,15 @@ class FourLineDisplayUsermod : public Usermod {
// or check the 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:
// gets called once at boot. Do all initialization that doesn't depend on
@ -189,9 +202,9 @@ class FourLineDisplayUsermod : public Usermod {
void setup() {
if (type == NONE || !enabled) return;
bool isHW;
bool isHW, isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64);
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);
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; }
@ -203,41 +216,72 @@ class FourLineDisplayUsermod : public Usermod {
}
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) {
case SSD1306:
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
lineHeight = 1;
break;
case SH1106:
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
lineHeight = 2;
break;
case SSD1306_64:
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
lineHeight = 2;
break;
case SSD1305:
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
lineHeight = 1;
break;
case SSD1305_64:
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
lineHeight = 2;
break;
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]);
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;
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]);
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;
default:
u8x8 = nullptr;
@ -245,20 +289,22 @@ class FourLineDisplayUsermod : public Usermod {
if (nullptr == u8x8) {
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;
return;
}
initDone = true;
lineHeight = u8x8->getRows() > 4 ? 2 : 1;
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();
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
setPowerSave(0);
//drawString(0, 0, "Loading...");
overlayLogo(3500);
initDone = true;
}
// gets called every time WiFi is (re-)connected. Initialize own network
@ -379,30 +425,29 @@ class FourLineDisplayUsermod : public Usermod {
String timer = PSTR("Timer On");
center(timer,LINE_BUFFER_SIZE-1);
overlay(timer.c_str(), 2500, 6);
//lastRedraw = millis();
}
return;
} else if (wificonnected != interfacesInited) { //trigger wifi icon
wificonnected = interfacesInited;
drawStatusIcons();
return;
} else if (knownMode != effectCurrent) {
knownMode = effectCurrent;
} else if (knownMode != effectCurrent || knownPalette != effectPalette) {
if (displayTurnedOff) needRedraw = true;
else { showCurrentEffectOrPalette(knownMode, JSON_mode_names, 3); return; }
} else if (knownPalette != effectPalette) {
knownPalette = effectPalette;
if (displayTurnedOff) needRedraw = true;
else { showCurrentEffectOrPalette(knownPalette, JSON_palette_names, 2); return; }
else {
if (knownPalette != effectPalette) { showCurrentEffectOrPalette(effectPalette, JSON_palette_names, 2); knownPalette = effectPalette; }
if (knownMode != effectCurrent) { showCurrentEffectOrPalette(effectCurrent, JSON_mode_names, 3); knownMode = effectCurrent; }
lastRedraw = now;
return;
}
} else if (knownBrightness != bri) {
if (displayTurnedOff && nightlightActive) { knownBrightness = bri; }
else if (!displayTurnedOff) { updateBrightness(); return; }
else if (!displayTurnedOff) { updateBrightness(); lastRedraw = now; return; }
} else if (knownEffectSpeed != effectSpeed) {
if (displayTurnedOff) needRedraw = true;
else { updateSpeed(); return; }
else { updateSpeed(); lastRedraw = now; return; }
} else if (knownEffectIntensity != effectIntensity) {
if (displayTurnedOff) needRedraw = true;
else { updateIntensity(); return; }
else { updateIntensity(); lastRedraw = now; return; }
}
if (!needRedraw) {
@ -425,13 +470,13 @@ class FourLineDisplayUsermod : public Usermod {
wakeDisplay();
// Update last known values.
knownBrightness = bri;
knownMode = effectCurrent;
knownPalette = effectPalette;
knownEffectSpeed = effectSpeed;
knownBrightness = bri;
knownMode = effectCurrent;
knownPalette = effectPalette;
knownEffectSpeed = effectSpeed;
knownEffectIntensity = effectIntensity;
knownnightlight = nightlightActive;
wificonnected = interfacesInited;
knownnightlight = nightlightActive;
wificonnected = interfacesInited;
// Do the actual drawing
// 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, 3*lineHeight, 5, u8x8_4LineDisplay_WLED_icons_2x2, true); //effect icon
} else {
drawGlyph( 2, 0, 1, u8x8_4LineDisplay_WLED_icons_1x1); //brightness icon
drawGlyph( 6, 0, 2, u8x8_4LineDisplay_WLED_icons_1x1); //speed icon
drawGlyph(10, 0, 3, u8x8_4LineDisplay_WLED_icons_1x1); //intensity icon
drawGlyph( 1, 0, 1, u8x8_4LineDisplay_WLED_icons_2x1); //brightness icon
drawGlyph( 5, 0, 2, u8x8_4LineDisplay_WLED_icons_2x1); //speed 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, 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
if (lineHeight==2) { col--; } else { row++; }
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
}
@ -529,18 +574,21 @@ class FourLineDisplayUsermod : public Usermod {
// on the appropriate line (row).
void showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row) {
char lineBuffer[MAX_JSON_CHARS];
knownMode = effectCurrent;
knownPalette = effectPalette;
if (overlayUntil == 0) {
// Find the mode name in JSON
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
char smallBuffer1[MAX_MODE_LINE_SPACE];
char smallBuffer2[MAX_MODE_LINE_SPACE];
uint8_t smallChars1 = 0;
uint8_t smallChars2 = 0;
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;
drawString(1, row*lineHeight, lineBuffer);
} else { // for long names divide the text into 2 lines and print them small
@ -561,23 +609,20 @@ class FourLineDisplayUsermod : public Usermod {
break;
}
}
for (; smallChars1 < (MAX_MODE_LINE_SPACE-1); smallChars1++) smallBuffer1[smallChars1]=' ';
while (smallChars1 < (MAX_MODE_LINE_SPACE-1)) smallBuffer1[smallChars1++]=' ';
smallBuffer1[smallChars1] = 0;
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;
drawString(1, row*lineHeight+1, smallBuffer2, true);
}
} 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;
if (printedChars > MAX_MODE_LINE_SPACE) printedChars = MAX_MODE_LINE_SPACE;
for (uint8_t i = 0; i < printedChars; i++) smallBuffer3[smallChars3++] = lineBuffer[i];
for (; smallChars3 < (MAX_MODE_LINE_SPACE); smallChars3++) smallBuffer3[smallChars3]=' ';
for (uint8_t i = 0; i < MAX_MODE_LINE_SPACE; i++) smallBuffer3[smallChars3++] = (i >= printedChars) ? ' ' : lineBuffer[i];
smallBuffer3[smallChars3] = 0;
drawString(1, row*lineHeight, smallBuffer3, true);
}
lastRedraw = millis();
}
}
@ -609,8 +654,8 @@ class FourLineDisplayUsermod : public Usermod {
if (!wakeDisplay()) clear();
// Print the overlay
if (glyphType>0 && glyphType<255) {
if (lineHeight == 2) drawGlyph(5, 0, glyphType, u8x8_4LineDisplay_WLED_icons_6x6, true);
else drawGlyph(7, lineHeight, glyphType, u8x8_4LineDisplay_WLED_icons_2x2, 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(6, 0, glyphType, u8x8_4LineDisplay_WLED_icons_3x3, true);
}
if (line1) {
String buf = line1;
@ -646,7 +691,7 @@ class FourLineDisplayUsermod : public Usermod {
break;
case 2:
//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);
drawString(6, 6, "WLED");
break;
@ -668,7 +713,7 @@ class FourLineDisplayUsermod : public Usermod {
break;
case 2:
//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);
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.
* 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[FPSTR(_flip)] = (bool) flip;
top[FPSTR(_contrast)] = contrast;
top[FPSTR(_contrastFix)] = (bool) contrastFix;
top[FPSTR(_refreshRate)] = refreshRate;
top[FPSTR(_screenTimeOut)] = screenTimeout/1000;
top[FPSTR(_sleepMode)] = (bool) sleepMode;
@ -886,6 +1006,7 @@ class FourLineDisplayUsermod : public Usermod {
sleepMode = top[FPSTR(_sleepMode)] | sleepMode;
clockMode = top[FPSTR(_clockMode)] | clockMode;
showSeconds = top[FPSTR(_showSeconds)] | showSeconds;
contrastFix = top[FPSTR(_contrastFix)] | contrastFix;
if (newType == SSD1306_SPI || newType == SSD1306_SPI64)
ioFrequency = min(20000, max(500, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
else
@ -914,16 +1035,18 @@ class FourLineDisplayUsermod : public Usermod {
} else type = newType;
setup();
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;
if (needsRedraw && !wakeDisplay()) redraw(true);
else overlayLogo(3500);
}
// 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::_showSeconds[] PROGMEM = "showSeconds";
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)
#ifdef USERMOD_FOUR_LINE_DISPLAY
#define LAST_UI_STATE 7
#define LAST_UI_STATE 8
#else
#define LAST_UI_STATE 4
#endif
@ -160,9 +160,14 @@ private:
uint8_t knownMode = 0;
uint8_t knownPalette = 0;
uint8_t currentCCT = 128;
bool isRgbw = false;
byte presetHigh = 0;
byte presetLow = 0;
bool applyToAll = true;
bool initDone = false;
bool enabled = true;
@ -174,6 +179,7 @@ private:
static const char _SW_pin[];
static const char _presetHigh[];
static const char _presetLow[];
static const char _applyToAll[];
/**
* Sort the modes and palettes to the index arrays
@ -276,6 +282,14 @@ public:
pinMode(pinC, INPUT_PULLUP);
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();
#ifdef USERMOD_FOUR_LINE_DISPLAY
@ -355,11 +369,11 @@ public:
buttonLongPressed = false;
buttonPressedBefore = false;
}
if (buttonWaitTime && currentTime-buttonWaitTime>350 && !buttonPressedBefore) {
if (buttonWaitTime && currentTime-buttonWaitTime>350 && !buttonPressedBefore) { //same speed as in button.cpp
buttonWaitTime = 0;
char newState = select_state + 1;
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) {
switch (newState) {
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 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 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;
@ -389,7 +404,8 @@ public:
case 4: changeEffect(true); break;
case 5: changeHue(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)
@ -402,7 +418,8 @@ public:
case 4: changeEffect(false); break;
case 5: changeHue(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);
//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
setValuesFromMainSeg(); //to make transition work on main segment
colorUpdated(CALL_MODE_DIRECT_CHANGE);
updateInterfaces(CALL_MODE_DIRECT_CHANGE);
}
@ -463,6 +481,7 @@ public:
// Throw away wake up input
return;
}
display->updateRedrawTime();
#endif
bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0);
lampUdated();
@ -479,9 +498,21 @@ public:
// Throw away wake up input
return;
}
display->updateRedrawTime();
#endif
effectCurrentIndex = max(min((increase ? effectCurrentIndex+1 : effectCurrentIndex-1), strip.getModeCount()-1), 0);
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();
#ifdef USERMOD_FOUR_LINE_DISPLAY
display->showCurrentEffectOrPalette(effectCurrent, JSON_mode_names, 3);
@ -496,8 +527,20 @@ public:
// Throw away wake up input
return;
}
display->updateRedrawTime();
#endif
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();
#ifdef USERMOD_FOUR_LINE_DISPLAY
display->updateSpeed();
@ -512,8 +555,20 @@ public:
// Throw away wake up input
return;
}
display->updateRedrawTime();
#endif
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();
#ifdef USERMOD_FOUR_LINE_DISPLAY
display->updateIntensity();
@ -528,9 +583,21 @@ public:
// Throw away wake up input
return;
}
display->updateRedrawTime();
#endif
effectPaletteIndex = max(min((increase ? effectPaletteIndex+1 : effectPaletteIndex-1), strip.getPaletteCount()-1), 0);
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();
#ifdef USERMOD_FOUR_LINE_DISPLAY
display->showCurrentEffectOrPalette(effectPalette, JSON_palette_names, 2);
@ -545,15 +612,22 @@ public:
// Throw away wake up input
return;
}
display->updateRedrawTime();
#endif
currentHue1 = max(min((increase ? currentHue1+fadeAmount : currentHue1-fadeAmount), 255), 0);
colorHStoRGB(currentHue1*256, currentSat1, col);
strip.applyToAllSelected = true;
strip.setColor(0, colorFromRgbw(col));
colorChanged = true;
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();
#ifdef USERMOD_FOUR_LINE_DISPLAY
display->updateRedrawTime();
#endif
}
void changeSat(bool increase){
@ -563,15 +637,21 @@ public:
// Throw away wake up input
return;
}
display->updateRedrawTime();
#endif
currentSat1 = max(min((increase ? currentSat1+fadeAmount : currentSat1-fadeAmount), 255), 0);
colorHStoRGB(currentHue1*256, currentSat1, col);
strip.applyToAllSelected = 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();
#ifdef USERMOD_FOUR_LINE_DISPLAY
display->updateRedrawTime();
#endif
}
void changePreset(bool increase) {
@ -581,6 +661,7 @@ public:
// Throw away wake up input
return;
}
display->updateRedrawTime();
#endif
if (presetHigh && presetLow && presetHigh > presetLow) {
String apireq = F("win&PL=~");
@ -592,9 +673,29 @@ public:
handleSet(nullptr, apireq, false);
lampUdated();
}
}
void changeCCT(bool increase){
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
// Throw away wake up input
return;
}
display->updateRedrawTime();
#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(_presetLow)] = presetLow;
top[FPSTR(_presetHigh)] = presetHigh;
top[FPSTR(_applyToAll)] = applyToAll;
DEBUG_PRINTLN(F("Rotary Encoder config saved."));
}
@ -676,6 +778,7 @@ public:
presetLow = MIN(250,MAX(0,presetLow));
enabled = top[FPSTR(_enabled)] | enabled;
applyToAll = top[FPSTR(_applyToAll)] | applyToAll;
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
@ -702,7 +805,7 @@ public:
}
}
// 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::_presetHigh[] PROGMEM = "preset-high";
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
/*
bool WS2812FX::setEffectConfig(uint8_t m, uint8_t s, uint8_t in, uint8_t p) {
Segment& seg = _segments[getMainSegmentId()];
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) {
setColor(slot, RGBW32(r, g, b, w));
}
*/
void WS2812FX::setColor(uint8_t slot, uint32_t c) {
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)
WLED::instance().initAP(true);
} 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
shortPressAction(b);
} 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);
}
//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
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[2] = ((uint16_t) correctionRGB[2] * B(rgb)) /255; // correct B
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.

View File

@ -38,12 +38,17 @@
l.addEventListener('load', (e) => {
// after rangetouch is loaded initialize global variable
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(function(){h.appendChild(l)},50);
setTimeout(()=>{h.appendChild(l)},50);
});
setTimeout(function(){h.appendChild(l)},50);
setTimeout(()=>{h.appendChild(l)},50);
</script>
<link rel="stylesheet" href="index.css">
</head>

View File

@ -10,7 +10,6 @@ var nlDur = 60, nlTar = 0;
var nlMode = false;
var selectedFx = 0, prevFx = -1;
var selectedPal = 0;
var sliderControl = ""; //WLEDSR: used by togglePcMode
var csel = 0;
var currentPreset = -1, prevPS = -1;
var lastUpdate = 0;
@ -1614,6 +1613,11 @@ ${makePlSel(true)}
Save segment bounds
<input type="checkbox" id="p${i}sbtgl" checked>
<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>`;
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 {
obj.ib = gId(`p${i}ibtgl`).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;
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;
gId('heart').style.color = `hsl(${hc}, 100%, 50%)`;}, 910);
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;
gId('heart').style.color = `hsl(${hc}, 100%, 50%)`;
}, 910);
function openGH() { window.open("https://github.com/Aircoookie/WLED/wiki"); }
@ -2361,7 +2373,7 @@ function togglePcMode(fromB = false)
if (wW < 1250 && !pcMode) return;
if (!fromB && ((wW < 1250 && lastw < 1250) || (wW >= 1250 && lastw >= 1250))) return;
openTab(0, true);
if (w < 1250) {pcMode = false;}
if (wW < 1250) {pcMode = false;}
else if (pcModeA && !fromB) pcMode = pcModeA;
updateTablinks(0);
gId('buttonPcm').className = (pcMode) ? "active":"";

View File

@ -23,6 +23,8 @@ void updateBlynk();
//button.cpp
void shortPressAction(uint8_t b=0);
void longPressAction(uint8_t b=0);
void doublePressAction(uint8_t b=0);
bool isButtonPressed(uint8_t b=0);
void handleButton();
void handleIO();
@ -58,7 +60,7 @@ bool getJsonValue(const JsonVariant& element, DestType& destination, const Defau
//colors.cpp
void colorFromUint32(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 colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb
void colorKtoRGB(uint16_t kelvin, byte* rgb);
@ -146,7 +148,6 @@ void resetTimebase();
void toggleOnOff();
void setAllLeds();
void setLedsStandard();
bool colorChanged();
void colorUpdated(int callMode);
void updateInterfaces(uint8_t callMode);
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 (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment
{
if (i == 0) {col[0] = rgbw[0]; col[1] = rgbw[1]; col[2] = rgbw[2]; col[3] = rgbw[3];}
if (i == 1) {colSec[0] = rgbw[0]; colSec[1] = rgbw[1]; colSec[2] = rgbw[2]; colSec[3] = rgbw[3];}
} else { //normal case, apply directly to segment
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
}
uint32_t color = RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3]);
colorChanged |= (seg.colors[i] != color);
seg.setColor(i, color, 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
//temporary, strip object gets updated via colorUpdated()
if (id == strip.getMainSegmentId()) {
byte effectPrev = effectCurrent;
if (getVal(elem["fx"], &effectCurrent, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 1-255 exact value)
if (!presetId && effectCurrent != effectPrev) unloadPlaylist(); //stop playlist if active and FX changed manually
}
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 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) effectChanged = true; //send UDP
}
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
if (!iarr.isNull()) {
@ -307,32 +296,32 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
byte prevMain = strip.getMainSegmentId();
strip.mainSegment = root[F("mainseg")] | prevMain;
if (strip.getMainSegmentId() != prevMain) setValuesFromMainSeg();
//if (strip.getMainSegmentId() != prevMain) setValuesFromMainSeg();
int it = 0;
JsonVariant segVar = root["seg"];
if (segVar.is<JsonObject>())
{
int id = segVar["id"] | -1;
if (id < 0) { //set all selected segments
bool didSet = false;
byte lowestActive = 99;
for (byte s = 0; s < strip.getMaxSegments(); s++)
{
//if "seg" is not an array and ID not specified, apply to all selected/checked segments
if (id < 0) {
//apply all selected segments
//bool didSet = false;
//byte lowestActive = 99;
for (byte s = 0; s < strip.getMaxSegments(); s++) {
WS2812FX::Segment &sg = strip.getSegment(s);
if (sg.isActive())
{
if (lowestActive == 99) lowestActive = s;
if (sg.isActive()) {
//if (lowestActive == 99) lowestActive = s;
if (sg.isSelected()) {
deserializeSegment(segVar, s, presetId);
didSet = true;
//didSet = true;
}
}
}
if (!didSet && lowestActive < strip.getMaxSegments()) deserializeSegment(segVar, lowestActive, presetId);
} else { //set only the segment with the specified ID
deserializeSegment(segVar, it, presetId);
//TODO: not sure if it is good idea to change first active but unselected segment
//if (!didSet && lowestActive < strip.getMaxSegments()) deserializeSegment(segVar, lowestActive, presetId);
} else {
deserializeSegment(segVar, id, presetId); //apply only the segment with the specified ID
}
} else {
JsonArray segs = segVar.as<JsonArray>();
@ -342,6 +331,8 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
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
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("]"));
root["col"] = serialized(colstr);
root["fx"] = seg.mode;
root["fx"] = seg.mode;
root[F("sx")] = seg.speed;
root[F("ix")] = seg.intensity;
root["pal"] = seg.palette;
root["pal"] = seg.palette;
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);
}
@ -483,12 +474,12 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
root[F("mainseg")] = strip.getMainSegmentId();
bool selectedSegmentsOnly = root[F("sc")] | false;
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);
if (sg.isActive())
{
if (selectedSegmentsOnly && !sg.isSelected()) continue;
if (sg.isActive()) {
JsonObject seg0 = seg.createNestedObject();
serializeSegment(seg0, sg, s, forPreset, segmentBounds);
} else if (forPreset && segmentBounds) { //disable segments not part of preset

View File

@ -44,8 +44,8 @@ byte scaledBri(byte in)
void setAllLeds() {
strip.setColor(0, col[0], col[1], col[2], col[3]);
strip.setColor(1, colSec[0], colSec[1], colSec[2], colSec[3]);
strip.setColor(0, RGBW32(col[0], col[1], col[2], col[3]));
strip.setColor(1, RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]));
if (!realtimeMode || !arlsForceMaxBri)
{
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)
{
//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
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 (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 (bri != briOld || effectChanged || colorChanged) {
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
interfaceUpdateCallMode = callMode;
effectChanged = false;
colorChanged = false;
} else {
if (nightlightActive && !nightlightActiveOld &&
callMode != CALL_MODE_NOTIFICATION &&
callMode != CALL_MODE_NO_NOTIFY)
{
if (nightlightActive && !nightlightActiveOld && callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) {
notify(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;
nightlightDelayMs -= (millis() - nightlightStartTime);
nightlightStartTime = millis();
}
for (byte i=0; i<4; i++)
{
colIT[i] = col[i];
colSecIT[i] = colSec[i];
}
if (briT == 0)
{
if (briT == 0) {
if (callMode != CALL_MODE_NOTIFICATION) resetTimebase(); //effect start from beginning
}
briIT = bri;
if (bri > 0) briLast = bri;
//deactivate nightlight if target brightness is reached
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
if (callMode != CALL_MODE_NOTIFICATION && !jsonTransitionOnce) transitionDelayTemp = transitionDelay;
jsonTransitionOnce = false;
strip.setTransition(transitionDelayTemp);
if (transitionDelayTemp == 0) {setLedsStandard(); strip.trigger(); return;}
if (transitionActive)
{
if (transitionDelayTemp == 0) {
//setLedsStandard();
briOld = briT = bri;
if (!realtimeMode || !arlsForceMaxBri) strip.setBrightness(scaledBri(briT));
strip.trigger();
return;
}
if (transitionActive) {
briOld = briT;
tperLast = 0;
}
strip.setTransitionMode(true);
transitionActive = true;
transitionStartTime = millis();
} else
{
} else {
strip.setTransition(0);
setLedsStandard();
//setLedsStandard();
briOld = briT = bri;
if (!realtimeMode || !arlsForceMaxBri) strip.setBrightness(scaledBri(briT));
strip.trigger();
}
}
@ -218,6 +179,12 @@ void handleTransitions()
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 (!nightlightActiveOld) //init

View File

@ -63,7 +63,6 @@ void handlePresets()
if (!errorFlag && presetToApply < 255) currentPreset = presetToApply;
effectChanged = true; //force UDP notification
colorUpdated(callModeToApply);
updateInterfaces(callModeToApply);
@ -102,6 +101,7 @@ void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj)
sObj.remove("o");
sObj.remove("ib");
sObj.remove("sb");
sObj.remove(F("sc"));
sObj.remove(F("error"));
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_PRINTLN(req);
strip.applyToAllSelected = false;
strip.applyToAllSelected = true;
//segment select (sets main segment)
byte prevMain = strip.getMainSegmentId();
@ -543,22 +543,28 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
byte selectedSeg = strip.getMainSegmentId();
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="));
if (pos > 0) {
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);
pos = req.indexOf(F("SV=")); //segment selected
if (pos > 0) {
byte t = getNumVal(&req, pos);
if (t == 2) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++)
{
strip.getSegment(i).setOption(SEG_OPTION_SELECTED, 0);
}
}
if (t == 2) for (uint8_t i = 0; i < strip.getMaxSegments(); i++) strip.getSegment(i).setOption(SEG_OPTION_SELECTED, 0); // unselect other segments
selseg.setOption(SEG_OPTION_SELECTED, t);
}
@ -624,26 +630,21 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
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
updateVal(&req, "&A=", &bri);
bool col0Changed = false, col1Changed = false, col2Changed = false;
//set colors
updateVal(&req, "&R=", &col[0]);
updateVal(&req, "&G=", &col[1]);
updateVal(&req, "&B=", &col[2]);
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, "G2=", &colSec[1]);
updateVal(&req, "B2=", &colSec[2]);
updateVal(&req, "W2=", &colSec[3]);
for (byte i=0; i<4; i++) if (prevColSec[i]!=colSec[i]) col1Changed = colorChanged = true;
#ifdef WLED_ENABLE_LOXONE
//lox parser
@ -674,52 +675,64 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
if (pos > 0) {
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)
pos = req.indexOf(F("&K="));
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
byte tmpCol[4];
pos = req.indexOf(F("CL="));
if (pos > 0) {
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="));
if (pos > 0) {
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="));
if (pos > 0) {
byte t[4];
colorFromDecOrHexString(t, (char*)req.substring(pos + 3).c_str());
if (selectedSeg != strip.getMainSegmentId()) {
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=)
}
colorFromDecOrHexString(tmpCol, (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)
col2Changed = colorChanged = true;
}
//set to random hue SR=0->1st SR=1->2nd
pos = req.indexOf(F("SR"));
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
pos = req.indexOf(F("SC"));
if (pos > 0) {
byte temp;
for (uint8_t i=0; i<4; i++)
{
temp = col[i];
col[i] = colSec[i];
for (uint8_t i=0; i<4; i++) {
temp = col[i];
col[i] = colSec[i];
colSec[i] = temp;
}
col0Changed = col1Changed = colorChanged = true;
}
//set effect parameters
@ -727,6 +740,11 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
updateVal(&req, "SX=", &effectSpeed);
updateVal(&req, "IX=", &effectIntensity);
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
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
//apply to all selected manually to prevent #1618. Temporary
bool col0Changed = false, col1Changed = false;
for (uint8_t i = 0; i < 4; i++) {
if (col[i] != prevCol[i]) col0Changed = true;
if (colSec[i] != prevColSec[i]) col1Changed = true;
}
for (uint8_t i = 0; i < strip.getMaxSegments(); i++)
{
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isSelected()) continue;
if (effectCurrent != prevEffect) {
strip.setMode(i, effectCurrent);
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));
if (strip.applyToAllSelected) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected() || i == selectedSeg) continue;
if (effectCurrent != prevEffect) strip.setMode(i, effectCurrent);
if (effectSpeed != prevSpeed) seg.speed = effectSpeed;
if (effectIntensity != prevIntensity) seg.intensity = effectIntensity;
if (effectPalette != prevPalette) seg.palette = effectPalette;
if (col0Changed) seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
if (col1Changed) seg.colors[1] = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]);
if (col2Changed) seg.colors[2] = RGBW32(tmpCol[0], tmpCol[1], tmpCol[2], tmpCol[3]);
}
}
strip.applyToAllSelected = false;
setValuesFromMainSeg();
//end of temporary fix code
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"));
if (pos < 1) XML_response(request);
strip.applyToAllSelected = false;
pos = req.indexOf(F("&NN")); //do not send UDP notifications this time
colorUpdated((pos > 0) ? CALL_MODE_NO_NOTIFY : CALL_MODE_DIRECT_CHANGE);

View File

@ -4,7 +4,9 @@
* 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 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[39] = strip.getMaxSegments();
udpOut[40] = UDP_SEG_SIZE; //size of each loop iteration (one segment)
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment &selseg = strip.getSegment(i);
udpOut[40+i*3] = selseg.options & 0x0F; //only take into account mirrored, selected, on, reversed
udpOut[41+i*3] = selseg.spacing;
udpOut[42+i*3] = selseg.grouping;
uint16_t ofs = 41 + i*UDP_SEG_SIZE; //start of segment offset byte
udpOut[0 +ofs] = 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;
broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP());
@ -256,24 +282,26 @@ void handleNotifications()
bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
//apply colors from notification
if (receiveNotificationColor || !someSel)
{
col[0] = udpIn[3];
col[1] = udpIn[4];
col[2] = udpIn[5];
if (receiveNotificationColor || !someSel) {
if (version < 11 || !receiveSegmentOptions) {
// only change col[] if not syncing full segments
col[0] = udpIn[3];
col[1] = udpIn[4];
col[2] = udpIn[5];
}
if (version > 0) //sending module's white val is intended
{
col[3] = udpIn[10];
if (version > 1)
{
// only change col[3] if not syncing full segments
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[1] = udpIn[13];
colSec[2] = udpIn[14];
colSec[3] = udpIn[15];
}
if (version > 6)
{
strip.setColor(2, udpIn[20], udpIn[21], udpIn[22], udpIn[23]); //tertiary color
if (version > 6 && (version < 11 || !receiveSegmentOptions)) {
strip.setColor(2, RGBW32(udpIn[20], udpIn[21], udpIn[22], udpIn[23])); //tertiary color
}
if (version > 9 && version < 200 && udpIn[37] < 255) { //valid CCT/Kelvin value
uint8_t cct = udpIn[38];
@ -291,24 +319,34 @@ void handleNotifications()
if (version < 200 && (receiveNotificationEffects || !someSel))
{
if (currentPlaylist>=0) unloadPlaylist();
if (version>10) {
if (receiveSegmentOptions) {
// will not sync start & stop
uint8_t srcSegs = udpIn[39];
if (srcSegs > strip.getMaxSegments()) srcSegs = strip.getMaxSegments();
for (uint8_t i = 0; i < srcSegs; i++) {
WS2812FX::Segment& selseg = strip.getSegment(i);
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
strip.setSegment(i, selseg.start, selseg.stop, udpIn[42+i*3], udpIn[41+i*3], selseg.offset); // will also properly reset segments
}
}
}
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)
{
if (version > 10 && receiveSegmentOptions) {
//does not sync start & stop
uint8_t srcSegs = udpIn[39];
if (srcSegs > strip.getMaxSegments()) srcSegs = strip.getMaxSegments();
for (uint8_t i = 0; i < srcSegs; 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[4 +ofs] >> j) & 0x01); //only take into account mirrored, selected, on, reversed
selseg.setOpacity( udpIn[5+ofs], i);
strip.setMode(i, udpIn[6+ofs]);
selseg.speed = udpIn[7+ofs];
selseg.intensity = udpIn[8+ofs];
selseg.palette = udpIn[9+ofs];
selseg.setColor(0, RGBW32(udpIn[10+ofs],udpIn[11+ofs],udpIn[12+ofs],udpIn[13+ofs]), i);
selseg.setColor(1, RGBW32(udpIn[14+ofs],udpIn[15+ofs],udpIn[16+ofs],udpIn[17+ofs]), i);
selseg.setColor(2, RGBW32(udpIn[18+ofs],udpIn[19+ofs],udpIn[20+ofs],udpIn[21+ofs]), i);
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]);
t += PRESUMED_NETWORK_DELAY; //adjust trivially for network delay
t -= millis();
@ -462,17 +500,18 @@ void handleNotifications()
// API over UDP
udpIn[packetSize] = '\0';
if (!requestJSONBufferLock(18)) return;
if (udpIn[0] >= 'A' && udpIn[0] <= 'Z') { //HTTP API
String apireq = "win&";
apireq += (char*)udpIn;
handleSet(nullptr, apireq);
} else if (udpIn[0] == '{') { //JSON API
DeserializationError error = deserializeJson(doc, udpIn);
JsonObject root = doc.as<JsonObject>();
if (!error && !root.isNull()) deserializeState(root);
if (requestJSONBufferLock(18)) {
if (udpIn[0] >= 'A' && udpIn[0] <= 'Z') { //HTTP API
String apireq = "win&";
apireq += (char*)udpIn;
handleSet(nullptr, apireq);
} else if (udpIn[0] == '{') { //JSON API
DeserializationError error = deserializeJson(doc, udpIn);
JsonObject root = doc.as<JsonObject>();
if (!error && !root.isNull()) deserializeState(root);
}
releaseJSONBufferLock();
}
releaseJSONBufferLock();
}

View File

@ -187,7 +187,7 @@ void _setRandomColor(bool _sec, bool fromButton)
} else {
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)
#define VERSION 2201111
#define VERSION 2201221
//uncomment this if you have a "my_config.h" file you'd like to use
//#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 (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:
// 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);
// 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
// transitions
@ -475,6 +473,7 @@ WLED_GLOBAL byte effectSpeed _INIT(128);
WLED_GLOBAL byte effectIntensity _INIT(128);
WLED_GLOBAL byte effectPalette _INIT(0);
WLED_GLOBAL bool effectChanged _INIT(false);
WLED_GLOBAL bool colorChanged _INIT(false);
// network
WLED_GLOBAL bool udpConnected _INIT(false), udp2Connected _INIT(false), udpRgbConnected _INIT(false);