diff --git a/usermods/PIR_sensor_switch/readme.md b/usermods/PIR_sensor_switch/readme.md
index 9b17c3ad..15a8db08 100644
--- a/usermods/PIR_sensor_switch/readme.md
+++ b/usermods/PIR_sensor_switch/readme.md
@@ -9,16 +9,17 @@ The LED strip is switched [using a relay](https://github.com/Aircoookie/WLED/wik
## Webinterface
-The info page in the web interface shows the remaining time of the off timer.
+The info page in the web interface shows the remaining time of the off timer. Usermod can also be temporarily disbled/enabled from the info page by clicking PIR button.
## Sensor connection
-My setup uses an HC-SR501 sensor, a HC-SR505 should also work.
+My setup uses an HC-SR501 or HC-SR602 sensor, a HC-SR505 should also work.
The usermod uses GPIO13 (D1 mini pin D7) by default for the sensor signal but can be changed in the Usermod settings page.
[This example page](http://www.esp8266learning.com/wemos-mini-pir-sensor-example.php) describes how to connect the sensor.
Use the potentiometers on the sensor to set the time-delay to the minimum and the sensitivity to about half, or slightly above.
+You can also use usermod's off timer instead of sensor's. In such case rotate the potentiometer to its shortest time possible (or use SR602 which lacks such potentiometer).
## Usermod installation
@@ -59,6 +60,8 @@ void registerUsermods()
}
```
+**NOTE:** Usermod has been included in master branch of WLED so it can be compiled in directly just by defining `-D USERMOD_PIRSWITCH` and optionaly `-D PIR_SENSOR_PIN=16` to override default pin.
+
## API to enable/disable the PIR sensor from outside. For example from another usermod.
To query or change the PIR sensor state the methods `bool PIRsensorEnabled()` and `void EnablePIRsensor(bool enable)` are available.
@@ -95,8 +98,27 @@ class MyUsermod : public Usermod {
};
```
-Have fun - @gegu
+### Configuration options
+
+Usermod can be configured in Usermods settings page.
+
+* `PIRenabled` - enable/disable usermod
+* `pin` - dynamically change GPIO pin where PIR sensor is attached to ESP
+* `PIRoffSec` - number of seconds after PIR sensor deactivates when usermod triggers Off preset (or turns WLED off)
+* `on-preset` - preset triggered when PIR activates (if this is 0 it will just turn WLED on)
+* `off-preset` - preset triggered when PIR deactivates (if this is 0 it will just turn WLED off)
+* `nighttime-only` - enable triggering only between sunset and sunrise (you will need to set up _NTP_, _Lat_ & _Lon_ in Time & Macro settings)
+* `mqtt-only` - only send MQTT messages, do not interact with WLED
+* `off-only` - only trigger presets or turn WLED on/off in WLED is not already on (displaying effect)
+* `notifications` - enable or disable sending notifications to other WLED instances using Sync button
+
+
+Have fun - @gegu & @blazoncek
## Change log
2021-04
-* Adaptation for runtime configuration.
\ No newline at end of file
+* Adaptation for runtime configuration.
+
+2021-11
+* Added information about dynamic configuration options
+* Added option to temporary enable/disble usermod from WLED UI (Info dialog)
\ No newline at end of file
diff --git a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h
index 6682dde3..8e683a49 100644
--- a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h
+++ b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h
@@ -55,33 +55,29 @@ public:
bool PIRsensorEnabled() { return enabled; }
private:
- // PIR sensor pin
- int8_t PIRsensorPin = PIR_SENSOR_PIN;
- // notification mode for colorUpdated()
- const byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // CALL_MODE_DIRECT_CHANGE
- // delay before switch off after the sensor state goes LOW
- uint32_t m_switchOffDelay = 600000; // 10min
- // off timer start time
- uint32_t m_offTimerStart = 0;
- // current PIR sensor pin state
- byte sensorPinState = LOW;
- // PIR sensor enabled
- bool enabled = true;
- // status of initialisation
- bool initDone = false;
- // on and off presets
- uint8_t m_onPreset = 0;
- uint8_t m_offPreset = 0;
- // flag to indicate that PIR sensor should activate WLED during nighttime only
- bool m_nightTimeOnly = false;
- // flag to send MQTT message only (assuming it is enabled)
- bool m_mqttOnly = false;
- // flag to enable triggering only if WLED is initially off (LEDs are not on, preventing running effect being overwritten by PIR)
- bool m_offOnly = false;
- bool PIRtriggered = false;
+ byte prevPreset = 0;
+ byte prevPlaylist = 0;
+ bool savedState = false;
+
+ uint32_t offTimerStart = 0; // off timer start time
+ byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // notification mode for colorUpdated(): CALL_MODE_NO_NOTIFY or CALL_MODE_DIRECT_CHANGE
+ byte sensorPinState = LOW; // current PIR sensor pin state
+ bool initDone = false; // status of initialization
+ bool PIRtriggered = false;
unsigned long lastLoop = 0;
+ // configurable parameters
+ bool enabled = true; // PIR sensor enabled
+ int8_t PIRsensorPin = PIR_SENSOR_PIN; // PIR sensor pin
+ uint32_t m_switchOffDelay = 600000; // delay before switch off after the sensor state goes LOW (10min)
+ uint8_t m_onPreset = 0; // on preset
+ uint8_t m_offPreset = 0; // off preset
+ bool m_nightTimeOnly = false; // flag to indicate that PIR sensor should activate WLED during nighttime only
+ bool m_mqttOnly = false; // flag to send MQTT message only (assuming it is enabled)
+ // flag to enable triggering only if WLED is initially off (LEDs are not on, preventing running effect being overwritten by PIR)
+ bool m_offOnly = false;
+
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _switchOffDelay[];
@@ -91,30 +87,30 @@ private:
static const char _nightTime[];
static const char _mqttOnly[];
static const char _offOnly[];
+ static const char _notify[];
/**
* check if it is daytime
* if sunrise/sunset is not defined (no NTP or lat/lon) default to nighttime
*/
bool isDayTime() {
- bool isDayTime = false;
updateLocalTime();
uint8_t hr = hour(localTime);
uint8_t mi = minute(localTime);
if (sunrise && sunset) {
if (hour(sunrise)
hr) {
- isDayTime = true;
+ return true;
} else {
if (hour(sunrise)==hr && minute(sunrise)mi) {
- isDayTime = true;
+ return true;
}
}
}
- return isDayTime;
+ return false;
}
/**
@@ -124,17 +120,47 @@ private:
{
if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return;
PIRtriggered = switchOn;
- if (switchOn && m_onPreset) {
- applyPreset(m_onPreset);
- } else if (!switchOn && m_offPreset) {
- applyPreset(m_offPreset);
- } else if (switchOn && bri == 0) {
- bri = briLast;
- colorUpdated(NotifyUpdateMode);
- } else if (!switchOn && bri != 0) {
- briLast = bri;
- bri = 0;
- colorUpdated(NotifyUpdateMode);
+ if (switchOn) {
+ if (m_onPreset) {
+ if (currentPlaylist>0) prevPlaylist = currentPlaylist;
+ else if (currentPreset>0) prevPreset = currentPreset;
+ else {
+ saveTemporaryPreset();
+ savedState = true;
+ prevPlaylist = 0;
+ prevPreset = 0;
+ }
+ applyPreset(m_onPreset, NotifyUpdateMode);
+ return;
+ }
+ // preset not assigned
+ if (bri == 0) {
+ bri = briLast;
+ colorUpdated(NotifyUpdateMode);
+ }
+ } else {
+ if (m_offPreset) {
+ applyPreset(m_offPreset, NotifyUpdateMode);
+ return;
+ } else if (prevPlaylist) {
+ applyPreset(prevPlaylist, NotifyUpdateMode);
+ prevPlaylist = 0;
+ return;
+ } else if (prevPreset) {
+ applyPreset(prevPreset, NotifyUpdateMode);
+ prevPreset = 0;
+ return;
+ } else if (savedState) {
+ applyTemporaryPreset();
+ savedState = false;
+ return;
+ }
+ // preset not assigned
+ if (bri != 0) {
+ briLast = bri;
+ bri = 0;
+ colorUpdated(NotifyUpdateMode);
+ }
}
}
@@ -160,12 +186,12 @@ private:
sensorPinState = pinState; // change previous state
if (sensorPinState == HIGH) {
- m_offTimerStart = 0;
+ offTimerStart = 0;
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true);
publishMqtt("on");
} else /*if (bri != 0)*/ {
// start switch off timer
- m_offTimerStart = millis();
+ offTimerStart = millis();
}
return true;
}
@@ -177,14 +203,14 @@ private:
*/
bool handleOffTimer()
{
- if (m_offTimerStart > 0 && millis() - m_offTimerStart > m_switchOffDelay)
+ if (offTimerStart > 0 && millis() - offTimerStart > m_switchOffDelay)
{
if (enabled == true)
{
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(false);
publishMqtt("off");
}
- m_offTimerStart = 0;
+ offTimerStart = 0;
return true;
}
return false;
@@ -248,15 +274,25 @@ public:
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
- if (enabled)
- {
- // off timer
- String uiDomString = F("PIR ");
- JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
- if (m_offTimerStart > 0)
+ String uiDomString = F("");
+ JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
+
+ if (enabled) {
+ if (offTimerStart > 0)
{
uiDomString = "";
- unsigned int offSeconds = (m_switchOffDelay - (millis() - m_offTimerStart)) / 1000;
+ unsigned int offSeconds = (m_switchOffDelay - (millis() - offTimerStart)) / 1000;
if (offSeconds >= 3600)
{
uiDomString += (offSeconds / 3600);
@@ -282,8 +318,6 @@ public:
infoArr.add(sensorPinState ? F("sensor on") : F("inactive"));
}
} else {
- String uiDomString = F("PIR sensor");
- JsonArray infoArr = user.createNestedArray(uiDomString);
infoArr.add(F("disabled"));
}
}
@@ -302,11 +336,18 @@ public:
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
-/*
+
void readFromJsonState(JsonObject &root)
{
+ if (!initDone) return; // prevent crash on boot applyPreset()
+ JsonObject usermod = root[FPSTR(_name)];
+ if (!usermod.isNull()) {
+ if (usermod[FPSTR(_enabled)].is()) {
+ enabled = usermod[FPSTR(_enabled)].as();
+ }
+ }
}
-*/
+
/**
* provide the changeable values
@@ -314,14 +355,15 @@ public:
void addToConfig(JsonObject &root)
{
JsonObject top = root.createNestedObject(FPSTR(_name));
- top[FPSTR(_enabled)] = enabled;
+ top[FPSTR(_enabled)] = enabled;
top[FPSTR(_switchOffDelay)] = m_switchOffDelay / 1000;
- top["pin"] = PIRsensorPin;
- top[FPSTR(_onPreset)] = m_onPreset;
- top[FPSTR(_offPreset)] = m_offPreset;
- top[FPSTR(_nightTime)] = m_nightTimeOnly;
- top[FPSTR(_mqttOnly)] = m_mqttOnly;
- top[FPSTR(_offOnly)] = m_offOnly;
+ top["pin"] = PIRsensorPin;
+ top[FPSTR(_onPreset)] = m_onPreset;
+ top[FPSTR(_offPreset)] = m_offPreset;
+ top[FPSTR(_nightTime)] = m_nightTimeOnly;
+ top[FPSTR(_mqttOnly)] = m_mqttOnly;
+ top[FPSTR(_offOnly)] = m_offOnly;
+ top[FPSTR(_notify)] = (NotifyUpdateMode != CALL_MODE_NO_NOTIFY);
DEBUG_PRINTLN(F("PIR config saved."));
}
@@ -336,9 +378,9 @@ public:
bool oldEnabled = enabled;
int8_t oldPin = PIRsensorPin;
+ DEBUG_PRINT(FPSTR(_name));
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
- DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
@@ -351,7 +393,6 @@ public:
m_onPreset = top[FPSTR(_onPreset)] | m_onPreset;
m_onPreset = max(0,min(250,(int)m_onPreset));
-
m_offPreset = top[FPSTR(_offPreset)] | m_offPreset;
m_offPreset = max(0,min(250,(int)m_offPreset));
@@ -359,7 +400,8 @@ public:
m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly;
m_offOnly = top[FPSTR(_offOnly)] | m_offOnly;
- DEBUG_PRINT(FPSTR(_name));
+ NotifyUpdateMode = top[FPSTR(_notify)] ? CALL_MODE_DIRECT_CHANGE : CALL_MODE_NO_NOTIFY;
+
if (!initDone) {
// reading config prior to setup()
DEBUG_PRINTLN(F(" config loaded."));
@@ -385,7 +427,7 @@ public:
DEBUG_PRINTLN(F(" config (re)loaded."));
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
- return !top[FPSTR(_offOnly)].isNull();
+ return !top[FPSTR(_notify)].isNull();
}
/**
@@ -407,3 +449,4 @@ const char PIRsensorSwitch::_offPreset[] PROGMEM = "off-preset";
const char PIRsensorSwitch::_nightTime[] PROGMEM = "nighttime-only";
const char PIRsensorSwitch::_mqttOnly[] PROGMEM = "mqtt-only";
const char PIRsensorSwitch::_offOnly[] PROGMEM = "off-only";
+const char PIRsensorSwitch::_notify[] PROGMEM = "notifications";
diff --git a/usermods/RTC/usermod_rtc.h b/usermods/RTC/usermod_rtc.h
index 75d91b31..8c174e6f 100644
--- a/usermods/RTC/usermod_rtc.h
+++ b/usermods/RTC/usermod_rtc.h
@@ -3,6 +3,14 @@
#include "src/dependencies/time/DS1307RTC.h"
#include "wled.h"
+#ifdef ARDUINO_ARCH_ESP32
+ #define HW_PIN_SCL 22
+ #define HW_PIN_SDA 21
+#else
+ #define HW_PIN_SCL 5
+ #define HW_PIN_SDA 4
+#endif
+
//Connect DS1307 to standard I2C pins (ESP32: GPIO 21 (SDA)/GPIO 22 (SCL))
class RTCUsermod : public Usermod {
@@ -12,6 +20,8 @@ class RTCUsermod : public Usermod {
public:
void setup() {
+ PinManagerPinType pins[2] = { { HW_PIN_SCL, true }, { HW_PIN_SDA, true } };
+ if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { disabled = true; return; }
time_t rtcTime = RTC.get();
if (rtcTime) {
toki.setTime(rtcTime,TOKI_NO_MS_ACCURACY,TOKI_TS_RTC);
@@ -22,12 +32,26 @@ class RTCUsermod : public Usermod {
}
void loop() {
+ if (strip.isUpdating()) return;
if (!disabled && toki.isTick()) {
time_t t = toki.second();
if (t != RTC.get()) RTC.set(t); //set RTC to NTP/UI-provided value
}
}
+ /*
+ * addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
+ * It will be called by WLED when settings are actually saved (for example, LED settings are saved)
+ * I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
+ */
+ void addToConfig(JsonObject& root)
+ {
+ JsonObject top = root.createNestedObject("RTC");
+ JsonArray pins = top.createNestedArray("pin");
+ pins.add(HW_PIN_SCL);
+ pins.add(HW_PIN_SDA);
+ }
+
uint16_t getId()
{
return USERMOD_ID_RTC;
diff --git a/usermods/Temperature/usermod_temperature.h b/usermods/Temperature/usermod_temperature.h
index 7c209f47..40df0e53 100644
--- a/usermods/Temperature/usermod_temperature.h
+++ b/usermods/Temperature/usermod_temperature.h
@@ -37,12 +37,12 @@ class UsermodTemperature : public Usermod {
// used to determine when we can read the sensors temperature
// we have to wait at least 93.75 ms after requestTemperatures() is called
unsigned long lastTemperaturesRequest;
- float temperature = -100; // default to -100, DS18B20 only goes down to -50C
+ float temperature;
// indicates requestTemperatures has been called but the sensor measurement is not complete
bool waitingForConversion = false;
// flag set at startup if DS18B20 sensor not found, avoids trying to keep getting
// temperature if flashed to a board without a sensor attached
- bool sensorFound = false;
+ byte sensorFound;
bool enabled = true;
@@ -54,27 +54,47 @@ class UsermodTemperature : public Usermod {
//Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013
float readDallas() {
- byte i;
- byte data[2];
+ byte data[9];
int16_t result; // raw data from sensor
- if (!oneWire->reset()) return -127.0f; // send reset command and fail fast
- oneWire->skip(); // skip ROM
- oneWire->write(0xBE); // read (temperature) from EEPROM
- for (i=0; i < 2; i++) data[i] = oneWire->read(); // first 2 bytes contain temperature
- for (i=2; i < 8; i++) oneWire->read(); // read unused bytes
- result = (data[1]<<4) | (data[0]>>4); // we only need whole part, we will add fraction when returning
- if (data[1]&0x80) result |= 0xFF00; // fix negative value
- oneWire->reset();
- oneWire->skip(); // skip ROM
- oneWire->write(0x44,parasite); // request new temperature reading (without parasite power)
- return (float)result + ((data[0]&0x0008) ? 0.5f : 0.0f);
+ float retVal = -127.0f;
+ if (oneWire->reset()) { // if reset() fails there are no OneWire devices
+ oneWire->skip(); // skip ROM
+ oneWire->write(0xBE); // read (temperature) from EEPROM
+ oneWire->read_bytes(data, 9); // first 2 bytes contain temperature
+ #ifdef WLED_DEBUG
+ if (OneWire::crc8(data,8) != data[8]) {
+ DEBUG_PRINTLN(F("CRC error reading temperature."));
+ for (byte i=0; i < 9; i++) DEBUG_PRINTF("0x%02X ", data[i]);
+ DEBUG_PRINT(F(" => "));
+ DEBUG_PRINTF("0x%02X\n", OneWire::crc8(data,8));
+ }
+ #endif
+ switch(sensorFound) {
+ case 0x10: // DS18S20 has 9-bit precision
+ result = (data[1] << 8) | data[0];
+ retVal = float(result) * 0.5f;
+ break;
+ case 0x22: // DS18B20
+ case 0x28: // DS1822
+ case 0x3B: // DS1825
+ case 0x42: // DS28EA00
+ result = (data[1]<<4) | (data[0]>>4); // we only need whole part, we will add fraction when returning
+ if (data[1] & 0x80) result |= 0xF000; // fix negative value
+ retVal = float(result) + ((data[0] & 0x08) ? 0.5f : 0.0f);
+ break;
+ }
+ }
+ for (byte i=1; i<9; i++) data[0] &= data[i];
+ return data[0]==0xFF ? -127.0f : retVal;
}
void requestTemperatures() {
- readDallas();
+ DEBUG_PRINTLN(F("Requesting temperature."));
+ oneWire->reset();
+ oneWire->skip(); // skip ROM
+ oneWire->write(0x44,parasite); // request new temperature reading (TODO: parasite would need special handling)
lastTemperaturesRequest = millis();
waitingForConversion = true;
- DEBUG_PRINTLN(F("Requested temperature."));
}
void readTemperature() {
@@ -102,10 +122,13 @@ class UsermodTemperature : public Usermod {
case 0x3B: // DS1825
case 0x42: // DS28EA00
DEBUG_PRINTLN(F("Sensor found."));
+ sensorFound = deviceAddress[0];
+ DEBUG_PRINTF("0x%02X\n", sensorFound);
return true;
}
}
}
+ DEBUG_PRINTLN(F("Sensor NOT found."));
return false;
}
@@ -113,16 +136,16 @@ class UsermodTemperature : public Usermod {
void setup() {
int retries = 10;
+ sensorFound = 0;
+ temperature = -127.0f; // default to -127, DS18B20 only goes down to -50C
if (enabled) {
// config says we are enabled
DEBUG_PRINTLN(F("Allocating temperature pin..."));
// pin retrieved from cfg.json (readFromConfig()) prior to running setup()
if (temperaturePin >= 0 && pinManager.allocatePin(temperaturePin, true, PinOwner::UM_Temperature)) {
oneWire = new OneWire(temperaturePin);
- if (!oneWire->reset()) {
- sensorFound = false; // resetting 1-Wire bus yielded an error
- } else {
- while ((sensorFound=findSensor()) && retries--) {
+ if (oneWire->reset()) {
+ while (!findSensor() && retries--) {
delay(25); // try to find sensor
}
}
@@ -131,7 +154,6 @@ class UsermodTemperature : public Usermod {
DEBUG_PRINTLN(F("Temperature pin allocation failed."));
}
temperaturePin = -1; // allocation failed
- sensorFound = false;
}
}
lastMeasurement = millis() - readingInterval + 10000;
@@ -139,8 +161,9 @@ class UsermodTemperature : public Usermod {
}
void loop() {
- if (!enabled || strip.isUpdating()) return;
+ if (!enabled || !sensorFound || strip.isUpdating()) return;
+ static uint8_t errorCount = 0;
unsigned long now = millis();
// check to see if we are due for taking a measurement
@@ -156,20 +179,26 @@ class UsermodTemperature : public Usermod {
}
// we were waiting for a conversion to complete, have we waited log enough?
- if (now - lastTemperaturesRequest >= 100 /* 93.75ms per the datasheet but can be up to 750ms */) {
+ if (now - lastTemperaturesRequest >= 750 /* 93.75ms per the datasheet but can be up to 750ms */) {
readTemperature();
+ if (getTemperatureC() < -100.0f) {
+ if (++errorCount > 10) sensorFound = 0;
+ lastMeasurement = now - readingInterval + 300; // force new measurement in 300ms
+ return;
+ }
+ errorCount = 0;
if (WLED_MQTT_CONNECTED) {
char subuf[64];
strcpy(subuf, mqttDeviceTopic);
- if (-100 <= temperature) {
+ if (temperature > -100.0f) {
// dont publish super low temperature as the graph will get messed up
// the DallasTemperature library returns -127C or -196.6F when problem
// reading the sensor
strcat_P(subuf, PSTR("/temperature"));
- mqtt->publish(subuf, 0, false, String(temperature).c_str());
+ mqtt->publish(subuf, 0, false, String(getTemperatureC()).c_str());
strcat_P(subuf, PSTR("_f"));
- mqtt->publish(subuf, 0, false, String((float)temperature * 1.8f + 32).c_str());
+ mqtt->publish(subuf, 0, false, String(getTemperatureF()).c_str());
} else {
// publish something else to indicate status?
}
@@ -202,13 +231,13 @@ class UsermodTemperature : public Usermod {
JsonArray temp = user.createNestedArray(FPSTR(_name));
//temp.add(F("Loaded."));
- if (temperature <= -100.0 || (!sensorFound && temperature == -1.0)) {
+ if (temperature <= -100.0f) {
temp.add(0);
temp.add(F(" Sensor Error!"));
return;
}
- temp.add(degC ? temperature : (float)temperature * 1.8f + 32);
+ temp.add(degC ? getTemperatureC() : getTemperatureF());
if (degC) temp.add(F("°C"));
else temp.add(F("°F"));
}
@@ -252,23 +281,21 @@ class UsermodTemperature : public Usermod {
bool readFromConfig(JsonObject &root) {
// we look for JSON object: {"Temperature": {"pin": 0, "degC": true}}
int8_t newTemperaturePin = temperaturePin;
+ DEBUG_PRINT(FPSTR(_name));
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
- DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
enabled = top[FPSTR(_enabled)] | enabled;
newTemperaturePin = top["pin"] | newTemperaturePin;
-// newTemperaturePin = min(33,max(-1,(int)newTemperaturePin)); // bounds check
degC = top["degC"] | degC;
readingInterval = top[FPSTR(_readInterval)] | readingInterval/1000;
readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms
parasite = top[FPSTR(_parasite)] | parasite;
- DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
// first run: reading from cfg.json
temperaturePin = newTemperaturePin;
diff --git a/usermods/VL53L0X_gestures/usermod_vl53l0x_gestures.h b/usermods/VL53L0X_gestures/usermod_vl53l0x_gestures.h
index 83f26e08..9597992f 100644
--- a/usermods/VL53L0X_gestures/usermod_vl53l0x_gestures.h
+++ b/usermods/VL53L0X_gestures/usermod_vl53l0x_gestures.h
@@ -21,6 +21,14 @@
#include
#include
+#ifdef ARDUINO_ARCH_ESP32
+ #define HW_PIN_SCL 22
+ #define HW_PIN_SDA 21
+#else
+ #define HW_PIN_SCL 5
+ #define HW_PIN_SDA 4
+#endif
+
#ifndef VL53L0X_MAX_RANGE_MM
#define VL53L0X_MAX_RANGE_MM 230 // max height in millimiters to react for motions
#endif
@@ -42,6 +50,7 @@ class UsermodVL53L0XGestures : public Usermod {
//Private class members. You can declare variables and functions only accessible to your usermod here
unsigned long lastTime = 0;
VL53L0X sensor;
+ bool enabled = true;
bool wasMotionBefore = false;
bool isLongMotion = false;
@@ -50,6 +59,8 @@ class UsermodVL53L0XGestures : public Usermod {
public:
void setup() {
+ PinManagerPinType pins[2] = { { HW_PIN_SCL, true }, { HW_PIN_SDA, true } };
+ if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; }
Wire.begin();
sensor.setTimeout(150);
@@ -63,6 +74,7 @@ class UsermodVL53L0XGestures : public Usermod {
void loop() {
+ if (!enabled || strip.isUpdating()) return;
if (millis() - lastTime > VL53L0X_DELAY_MS)
{
lastTime = millis();
@@ -110,6 +122,19 @@ class UsermodVL53L0XGestures : public Usermod {
}
}
+ /*
+ * addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
+ * It will be called by WLED when settings are actually saved (for example, LED settings are saved)
+ * I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
+ */
+ void addToConfig(JsonObject& root)
+ {
+ JsonObject top = root.createNestedObject("VL53L0x");
+ JsonArray pins = top.createNestedArray("pin");
+ pins.add(HW_PIN_SCL);
+ pins.add(HW_PIN_SDA);
+ }
+
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
* This could be used in the future for the system to determine whether your usermod is installed.
diff --git a/usermods/mpu6050_imu/usermod_mpu6050_imu.h b/usermods/mpu6050_imu/usermod_mpu6050_imu.h
index 965ab41b..4aa2a128 100644
--- a/usermods/mpu6050_imu/usermod_mpu6050_imu.h
+++ b/usermods/mpu6050_imu/usermod_mpu6050_imu.h
@@ -42,6 +42,14 @@
#include "Wire.h"
#endif
+#ifdef ARDUINO_ARCH_ESP32
+ #define HW_PIN_SCL 22
+ #define HW_PIN_SDA 21
+#else
+ #define HW_PIN_SCL 5
+ #define HW_PIN_SDA 4
+#endif
+
// ================================================================
// === INTERRUPT DETECTION ROUTINE ===
// ================================================================
@@ -55,7 +63,8 @@ void IRAM_ATTR dmpDataReady() {
class MPU6050Driver : public Usermod {
private:
MPU6050 mpu;
-
+ bool enabled = true;
+
// MPU control/status vars
bool dmpReady = false; // set true if DMP init was successful
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
@@ -84,6 +93,8 @@ class MPU6050Driver : public Usermod {
* setup() is called once at boot. WiFi is not yet connected at this point.
*/
void setup() {
+ PinManagerPinType pins[2] = { { HW_PIN_SCL, true }, { HW_PIN_SDA, true } };
+ if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; }
// join I2C bus (I2Cdev library doesn't do this automatically)
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
@@ -93,16 +104,16 @@ class MPU6050Driver : public Usermod {
#endif
// initialize device
- Serial.println(F("Initializing I2C devices..."));
+ DEBUG_PRINTLN(F("Initializing I2C devices..."));
mpu.initialize();
pinMode(INTERRUPT_PIN, INPUT);
// verify connection
- Serial.println(F("Testing device connections..."));
- Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
+ DEBUG_PRINTLN(F("Testing device connections..."));
+ DEBUG_PRINTLN(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
// load and configure the DMP
- Serial.println(F("Initializing DMP..."));
+ DEBUG_PRINTLN(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize();
// supply your own gyro offsets here, scaled for min sensitivity
@@ -114,16 +125,16 @@ class MPU6050Driver : public Usermod {
// make sure it worked (returns 0 if so)
if (devStatus == 0) {
// turn on the DMP, now that it's ready
- Serial.println(F("Enabling DMP..."));
+ DEBUG_PRINTLN(F("Enabling DMP..."));
mpu.setDMPEnabled(true);
// enable Arduino interrupt detection
- Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
+ DEBUG_PRINTLN(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
// set our DMP Ready flag so the main loop() function knows it's okay to use it
- Serial.println(F("DMP ready! Waiting for first interrupt..."));
+ DEBUG_PRINTLN(F("DMP ready! Waiting for first interrupt..."));
dmpReady = true;
// get expected DMP packet size for later comparison
@@ -133,9 +144,9 @@ class MPU6050Driver : public Usermod {
// 1 = initial memory load failed
// 2 = DMP configuration updates failed
// (if it's going to break, usually the code will be 1)
- Serial.print(F("DMP Initialization failed (code "));
- Serial.print(devStatus);
- Serial.println(F(")"));
+ DEBUG_PRINT(F("DMP Initialization failed (code "));
+ DEBUG_PRINT(devStatus);
+ DEBUG_PRINTLN(F(")"));
}
}
@@ -144,7 +155,7 @@ class MPU6050Driver : public Usermod {
* Use it to initialize network interfaces
*/
void connected() {
- //Serial.println("Connected to WiFi!");
+ //DEBUG_PRINTLN("Connected to WiFi!");
}
@@ -153,7 +164,7 @@ class MPU6050Driver : public Usermod {
*/
void loop() {
// if programming failed, don't try to do anything
- if (!dmpReady) return;
+ if (!enabled || !dmpReady || strip.isUpdating()) return;
// wait for MPU interrupt or extra packet(s) available
if (!mpuInterrupt && fifoCount < packetSize) return;
@@ -169,7 +180,7 @@ class MPU6050Driver : public Usermod {
if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
// reset so we can continue cleanly
mpu.resetFIFO();
- Serial.println(F("FIFO overflow!"));
+ DEBUG_PRINTLN(F("FIFO overflow!"));
// otherwise, check for DMP data ready interrupt (this should happen frequently)
} else if (mpuIntStatus & 0x02) {
@@ -259,10 +270,23 @@ class MPU6050Driver : public Usermod {
*/
void readFromJsonState(JsonObject& root)
{
- //if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
+ //if (root["bri"] == 255) DEBUG_PRINTLN(F("Don't burn down your garage!"));
}
-
-
+
+
+ /*
+ * addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
+ * It will be called by WLED when settings are actually saved (for example, LED settings are saved)
+ * I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
+ */
+ void addToConfig(JsonObject& root)
+ {
+ JsonObject top = root.createNestedObject("MPU6050_IMU");
+ JsonArray pins = top.createNestedArray("pin");
+ pins.add(HW_PIN_SCL);
+ pins.add(HW_PIN_SDA);
+ }
+
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
*/
diff --git a/usermods/multi_relay/readme.md b/usermods/multi_relay/readme.md
index 267bb54a..2d933cda 100644
--- a/usermods/multi_relay/readme.md
+++ b/usermods/multi_relay/readme.md
@@ -5,31 +5,35 @@ This usermod-v2 modification allows the connection of multiple relays each with
## HTTP API
All responses are returned as JSON.
-Status Request: `http://[device-ip]/relays`
-Switch Command: `http://[device-ip]/relays?switch=1,0,1,1`
+* Status Request: `http://[device-ip]/relays`
+* Switch Command: `http://[device-ip]/relays?switch=1,0,1,1`
+
The number of numbers behind the switch parameter must correspond to the number of relays. The number 1 switches the relay on. The number 0 switches the relay off.
-Toggle Command: `http://[device-ip]/relays?toggle=1,0,1,1`
+* Toggle Command: `http://[device-ip]/relays?toggle=1,0,1,1`
+
The number of numbers behind the parameter switch must correspond to the number of relays. The number 1 causes a toggling of the relay. The number 0 leaves the state of the device.
Examples
-1. 4 relays at all, relay 2 will be toggled: `http://[device-ip]/relays?toggle=0,1,0,0`
-2. 3 relays at all, relay 1&3 will be switched on: `http://[device-ip]/relays?switch=1,0,1`
+1. total of 4 relays, relay 2 will be toggled: `http://[device-ip]/relays?toggle=0,1,0,0`
+2. total of 3 relays, relay 1&3 will be switched on: `http://[device-ip]/relays?switch=1,0,1`
## JSON API
You can switch relay state using the following JSON object transmitted to: `http://[device-ip]/json`
+
Switch relay 0 on: `{"MultiRelay":{"relay":0,"on":true}}`
+
Switch relay4 3 & 4 off: `{"MultiRelay":[{"relay":2,"on":false},{"relay":3,"on":false}]}`
## MQTT API
-wled/deviceMAC/relay/0/command on|off|toggle
-wled/deviceMAC/relay/1/command on|off|toggle
+* `wled`/_deviceMAC_/`relay`/`0`/`command` `on`|`off`|`toggle`
+* `wled`/_deviceMAC_/`relay`/`1`/`command` `on`|`off`|`toggle`
When relay is switched it will publish a message:
-wled/deviceMAC/relay/0 on|off
+* `wled`/_deviceMAC_/`relay`/`0` `on`|`off`
## Usermod installation
@@ -76,10 +80,21 @@ void registerUsermods()
Usermod can be configured in Usermods settings page.
+* `enabled` - enable/disable usermod
+* `pin` - GPIO pin where relay is attached to ESP
+* `delay-s` - delay in seconds after on/off command is received
+* `active-high` - toggle high/low activation of relay (can be used to reverse relay states)
+* `external` - if enabled WLED does not control relay, it can only be triggered by external command (MQTT, HTTP, JSON or button)
+* `button` - button (from LED Settings) that controls this relay
+
If there is no MultiRelay section, just save current configuration and re-open Usermods settings page.
Have fun - @blazoncek
## Change log
2021-04
-* First implementation.
\ No newline at end of file
+* First implementation.
+
+2021-11
+* Added information about dynamic configuration options
+* Added button support.
\ No newline at end of file
diff --git a/usermods/usermod_v2_four_line_display_ALT/4LD_wled_fonts.c b/usermods/usermod_v2_four_line_display_ALT/4LD_wled_fonts.c
new file mode 100644
index 00000000..5495f919
--- /dev/null
+++ b/usermods/usermod_v2_four_line_display_ALT/4LD_wled_fonts.c
@@ -0,0 +1,477 @@
+#pragma once
+
+//WLED custom fonts, curtesy of @Benji (https://github.com/Proto-molecule)
+
+
+/*
+ Fontname: wled_logo_akemi_4x4
+ Copyright: Benji (https://github.com/proto-molecule)
+ Glyphs: 3/3
+ BBX Build Mode: 3
+ * this logo ...WLED/images/wled_logo_akemi.png
+ * encode map = 1, 2, 3
+*/
+const uint8_t u8x8_wled_logo_akemi_4x4[388] U8X8_FONT_SECTION("u8x8_wled_logo_akemi_4x4") =
+ "\1\3\4\4\0\0\0\0\0\0\0\0\0\340\360\10\350\10\350\210\270\210\350\210\270\350\10\360\340\0\0\0"
+ "\0\0\200\200\0\0@\340\300\340@\0\0\377\377\377\377\377\377\37\37\207\207\371\371\371\377\377\377\0\0\374"
+ "\374\7\7\371\0\0\6\4\15\34x\340\200\177\177\377\351yy\376\356\357\217\177\177\177o\377\377\0\70\77"
+ "\277\376~\71\0\0\0\0\0\0\0\1\3\3\3\1\0\0\37\77\353\365\77\37\0\0\0\0\5\7\2\3"
+ "\7\4\0\0\300\300\300\300\200\200\200\0\0\0\0\0\0\0\200\200\300\300\300\300\200\200\0\0\0\0\0\0"
+ "\0\200\200\300\371\37\37\371\371\7\7\377\374\0\0\0\374\377\377\37\37\341\341\377\377\377\377\374\0\0\0\374"
+ "\377\7\7\231\371\376>\371\371>~\377\277\70\0\270\377\177\77\376\376\71\371\371\71\177\377\277\70\0\70\377"
+ "\177>\376\371\377\377\0\77\77\0\0\4\7\2\7\5\0\0\0\377\377\0\77\77\0\0\0\5\7\2\7\5"
+ "\0\0\377\377\300\300\300\200\200\0\0\0\0\0\0\0\200\200\300\300\300\300\300\200\200\0\0\0\0\0\0\0"
+ "\0\0\0\0\231\231\231\371\377\377\374\0\0\0\374\377\347\347\371\1\1\371\371\7\7\377\374\0\0\0@\340"
+ "\300\340@\0\71\371\371\71\177\377\277\70\0\70\277\377\177\71\371\370\70\371\371~\376\377\77\70\200\340x\34"
+ "\15\4\6\0\0\77\77\0\0\0\5\7\2\7\5\0\0\0\377\377\0\77\77\0\0\1\3\3\1\1\0\0"
+ "\0\0\0";
+
+
+/*
+ Fontname: wled_logo_akemi_5x5
+ Copyright: Benji (https://github.com/proto-molecule)
+ Glyphs: 3/3
+ BBX Build Mode: 3
+ * this logo ...WLED/images/wled_logo_akemi.png
+ * encoded = 1, 2, 3
+*/
+/*
+const uint8_t u8x8_wled_logo_akemi_5x5[604] U8X8_FONT_SECTION("u8x8_wled_logo_akemi_5x5") =
+ "\1\3\5\5\0\0\0\0\0\0\0\0\0\0\0\0\340\340\374\14\354\14\354\14|\14\354\14||\14\354"
+ "\14\374\340\340\0\0\0\0\0\0\0\200\0\0\0\200\200\0\200\200\0\0\0\0\377\377\377\376\377\376\377\377"
+ "\377\377\77\77\307\307\307\307\306\377\377\377\0\0\0\360\374>\77\307\0\0\61cg\357\347\303\301\200\0\0"
+ "\377\377\377\317\317\317\317\360\360\360\374\374\377\377\377\377\377\377\377\377\0\0\200\377\377\340\340\37\0\0\0\0"
+ "\0\0\1\3\17\77\374\360\357\357\177\36\14\17\357\377\376\376>\376\360\357\17\17\14>\177o\340\300\343c"
+ "{\77\17\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\17\37\37\362\375\37\37\17\0\0"
+ "\0\0\1\1\1\0\1\1\1\0\0\0\200\300\300\300\300\200\200\0\0\0\0\0\0\0\0\0\0\0\200\200"
+ "\300\300\300\300\200\200\0\0\0\0\0\0\0\0\0\0\0\0\200\200\307\307\377\377\307\307\307\77>\374\360\0"
+ "\0\0\360\374\376\377\377\377\7\7\7\377\377\377\377\376\374\360\0\0\0\0\360\374\36\37\37\343\37\37\340\340"
+ "\37\37\37\340\340\377\377\200\0\200\377\377\377\340\340\340\37\37\37\37\37\37\37\377\377\377\200\0\0\200\377\377"
+ "\340\340\340\34\377\377\3\3\377\377\3\17\77{\343\303\300\303\343s\77\37\3\377\377\3\3\377\377\3\17\77"
+ "{\343\303\300\300\343{\37\17\3\377\377\377\377\0\0\37\37\0\0\1\1\1\1\0\1\1\1\1\0\0\377"
+ "\377\0\0\37\37\0\0\1\1\1\1\0\0\1\1\1\0\0\377\377\300\300\300\200\200\0\0\0\0\0\0\0"
+ "\0\0\0\0\200\200\300\300\300\300\200\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\343\343\343\343"
+ "\343\377\376\374\360\0\0\0\360\374\376\77\77\307\307\7\7\307\307\307\77>\374\360\0\0\0\0\0\200\200\0"
+ "\200\200\0\0\34\34\34\37\37\377\377\377\377\200\0\200\377\377\377\377\37\37\37\0\0\37\37\37\340\340\377\377"
+ "\200\0\0\0\1\303\347\357gc\61\0\3\3\377\377\3\7\37\177s\343\300\303s{\37\17\7\3\377\377"
+ "\3\3\377\377\3\37\77scp<\36\17\3\1\0\0\0\0\0\0\0\37\37\0\0\0\1\1\1\0\1"
+ "\1\1\0\0\0\0\377\377\0\0\37\37\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+*/
+
+/*
+ Fontname: wled_logo_2x2
+ 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
+ * encode map = 1, 2, 3, 4
+*/
+const uint8_t u8x8_wled_logo_2x2[133] U8X8_FONT_SECTION("u8x8_wled_logo_2x2") =
+ "\1\4\2\2\0\0\0\0\0\200\200\360\360\16\16\16\16\0\0\0\340\340\340\340\340\37\37\1\1\0\0\0"
+ "\0\0\0\0\360\360\16\16\16\200\200\16\16\16\360\360\0\0\0\200\37\37\340\340\340\37\37\340\340\340\37\37"
+ "\0\0\0\37\200~~\0\0\0\0\0\0\0\360\360\216\216\216\216\37\340\340\340\340\340\340\340\0\0\37\37"
+ "\343\343\343\343\16\16\0\0ppp\16\16\376\376\16\16\16\360\360\340\340\0\0\0\0\0\340\340\377\377\340"
+ "\340\340\37\37";
+
+
+/*
+ Fontname: wled_logo_4x4
+ Copyright: Created with Fony 1.4.7
+ Glyphs: 4/4
+ BBX Build Mode: 3
+ * this logo https://cdn.discordapp.com/attachments/706623245935444088/927361780613799956/wled_scaled.png
+ * encode map = 1, 2, 3, 4
+*/
+/*
+const uint8_t u8x8_wled_logo_4x4[517] U8X8_FONT_SECTION("u8x8_wled_logo_4x4") =
+ "\1\4\4\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\374\374\374\374\374\374\374\374\374"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\300\300\300\300\300\377\377\377\377\377\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\17\17\17\17\17\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\370\370\370\370\370\370\370\370\370\7\7\7\7\7\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\374\374\374\374\374\0\0\0\0\0\374\374\374\374\374\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\377\377\377\377\377\0\0\0\0\0\300\300\300\300\300\0\0\0\0\0\377\377\377\377\377\0\0"
+ "\0\0\300\300\0\377\377\377\377\377\0\0\0\0\0\377\377\377\377\377\0\0\0\0\0\377\377\377\377\377\0\0"
+ "\0\0\377\377\0\7\7\7\7\7\370\370\370\370\370\7\7\7\7\7\370\370\370\370\370\7\7\7\7\7\0\0"
+ "\0\0\7\7\0\0\0\374\374\374\374\374\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\374\374\374"
+ "\374\374\374\374\300\300\300\77\77\77\77\77\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\300\300\300"
+ "\300\300\300\300\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\37\37\37"
+ "\37\37\37\37\7\7\7\370\370\370\370\370\370\370\370\370\370\370\370\370\0\0\0\0\7\7\7\7\7\370\370\370"
+ "\370\370\370\370\374\374\374\374\374\374\0\0\0\0\0\0\0\0\374\374\374\374\374\374\374\374\374\374\374\374\374\374"
+ "\0\0\0\0\300\300\0\0\0\0\0\0\0\77\77\77\77\77\0\0\0\0\377\377\377\377\377\0\0\0\0\377"
+ "\377\377\377\377\37\37\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\0\0\0\0\377"
+ "\377\377\377\377\370\370\370\370\370\370\0\0\0\0\0\0\0\0\370\370\370\370\377\377\377\377\377\370\370\370\370\377"
+ "\7\7\7\7";
+*/
+
+
+/*
+ Fontname: 4LineDisplay_WLED_icons_1x
+ Copyright: Benji (https://github.com/proto-molecule)
+ Glyphs: 13/13
+ 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
+ *-----------
+ * 20 = wifi
+ * 21 = media-play
+*/
+const uint8_t u8x8_4LineDisplay_WLED_icons_1x1[172] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_1x1") =
+ "\1\25\1\1\0B\30<<\30B\0~<\30\0~<\30\0p\374\77\216\340\370\360\0||>\36\14\64 \336\67"
+ ";\336 \64\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\1\11\311"
+ "\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"
+ "\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
+ Copyright:
+ 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_2x2[389] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_2x2") =
+ "\1\14\2\2\200\200\14\14\300\340\360\363\363\360\340\300\14\14\200\200\1\1\60\60\3\7\17\317\317\17\7\3"
+ "\60\60\1\1\374\370\360\340\340\300\200\0\374\370\360\340\340\300\200\0\77\37\17\7\7\3\1\0\77\37\17\7"
+ "\7\3\1\0\0\200\340\360\377\376\374\360\0\0\300\200\0\0\0\0\17\77\177\377\17\7\301\340\370\374\377\377"
+ "\377|\0\0\360\370\234\236\376\363\363\377\377\363\363\376><\370\360\3\17\77yy\377\377\377\377\317\17\17"
+ "\17\17\7\3\360\360\360\360\366\377\377\366\360\360\360\360\0\0\0\0\377\377\377\377\237\17\17\237\377\377\377\377"
+ "\6\17\17\6\340\370\374\376\377\340\200\0\0\0\0\0\0\0\0\0\3\17\37\77\177\177\177\377\376|||"
+ "\70\30\14\0\0\0\0\0\0\0\0``\360\370|<\36\7\2\0\300\360\376\377\177\77\36\0\1\1\0"
+ "\0\0\0\0\340\370\374\376\376\377\377\377\3\3\7\6\16<\370\340\7\37\77\177\177\377\377\377\300\300\340`"
+ "p<\37\7\300\340p\30\0\0\377\377\0\0\30p\340\300\0\0\17\37\70`\340\300\300\300\300\340`\70"
+ "\37\17\0\0\0@\300\300\300\300\340\374\374\340\300\300\300\300@\0\0\0\0\1s\77\37\17\17\37\77s"
+ "\1\0\0\0\360\370\374\374\374\374\370\360\360\370\374\374\374\374\370\360\0\1\3\7\17\37\77\177\177\77\37\17"
+ "\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~\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>\177\177\377\377\377\377\377\377\371p\60\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0<\376\377\377\377\377\376<\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0"
+ "\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377~~\377\377"
+ "\377\377~<\377\377\377\377\377\377\377\377\303\1\0\0\0\0\1\303\377\377\377\377\377\377\377\377\0\0\0\0"
+ "\0\0\0\0\0\0\200\340\360\370\374\374\376\376\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\370\377\377\377\377\377\377\377\377\377\376\360\300\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\7\77\377\377\377\377\377\377\377\377\377\377\377\377\377\376\374\370\370\360\360\360\340\340\340\340\340\340"
+ "\340\340\60\0\0\0\0\1\3\7\17\37\37\77\77\77\177\177\177\177\177\177\177\177\77\77\77\37\37\17\7\3"
+ "\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\200\300\340\340\360\370\374\374"
+ "~\77\16\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\30\34>~\377\377\377\377\177\77\37\7\3\0"
+ "\0\0\0\0\0\0\0\0\0\360\374\376\377\377\377\377\377\376\374\370\0\0\0\3\3\1\0\0\0\0\0\0"
+ "\0\0\0\0@@\340\370\374\377\377\377\177\177\177\77\37\17\7\1\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\200\300\340\360\370\374\374\376\376\376\377\377\377\377\17\17\17\37\36\36>|\374\370\360\340"
+ "\300\200\0\0\360\376\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\1\3\37"
+ "\377\377\376\360\17\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\200\300\370"
+ "\377\377\177\17\0\0\1\3\7\17\37\77\77\177\177\177\377\377\377\377\360\360\360\370xx|>\77\37\17\7"
+ "\3\1\0\0\0\0\0\0\0\200\300\200\0\0\0\0\377\377\377\377\0\0\0\0\200\300\200\0\0\0\0\0"
+ "\0\0\0\0\300\360\374\376\177\37\7\3\3\0\0\0\377\377\377\377\0\0\0\3\3\7\37\177\376\374\360\300"
+ "\0\0\0\0\77\377\377\377\340\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\340\377\377\377\77"
+ "\0\0\0\0\0\0\3\7\17\37><|x\370\360\360\360\360\360\360\370x|<>\37\17\7\3\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\340\374\374\340\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\20\60p\360\360\360\360\360\360\360\360\370\377\377\377\377\377\377\370\360\360\360\360\360\360\360\360"
+ "p\60\20\0\0\0\0\0\0\0\1\3\7\317\377\377\377\377\377\377\377\377\377\377\377\377\317\7\3\1\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0p>\37\17\17\7\3\1\0\0\1\3\7\17\17\37>p\0\0\0"
+ "\0\0\0\0\0\200\300\340\340\360\360\360\360\360\360\340\340\300\200\0\0\200\300\340\340\360\360\360\360\360\360\340"
+ "\340\300\200\0~\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\376\377\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\377~\0\1\3\7\17\37\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\37\17"
+ "\7\3\1\0\0\0\0\0\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\0\0\0\0\0\0\0\200\300\340\340\360\360\360\360\340\340\300\200\0\0\0\0\0\0"
+ "\0\0\0\0\0@\340\300\340@\0\0\0\376\377\377\177\177\177\237\207\347\371\371\371\377\376\0\0\0\0@"
+ "\340\300\340@\2\4\4\35x\340\200\0\30\237\377\177\36\376\376\37\37\377\377\37\177\377\237\30\0\200\340x"
+ "\34\5\4\2\0\0\0\0\0\1\3\3\3\1\0\0\0\17\17\0\0\17\17\0\0\0\1\3\3\3\1\0"
+ "\0\0\0";
+*/
+
+/*
+ Fontname: 4LineDisplay_WLED_icons_6x
+ 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
+*/
+// 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"
+ "\0\0\0\0\200\300\340\340\340\360\360\360\360\360\360\340\340\340\300\200\0\0\0\0\7\17\17\17\17\7\0\0"
+ "\0\0\0\0\300\340\340\340\340\300\0\0\0\0\0\0\340\374\376\377\377\377\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\377\377\377\376\374\340\0\0\0\0\0\0\300\340\340\340\340\300\3\7\7\7\7\3\0\0\0\0\0\0"
+ "\7\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\7\0\0\0\0\0\0\3\7"
+ "\7\7\7\3\0\0\0\0\0\0\340\360\360\360\360\340\0\0\0\0\1\3\7\7\7\17\17\17\17\17\17\7"
+ "\7\7\3\1\0\0\0\0\340\360\360\360\360\340\0\0\0\0\0\0\0\0\0\0\0\0\1\3\3\3\3\1"
+ "\0\0\0\0\0\0\0\0\0x\374\374\374\374x\0\0\0\0\0\0\0\0\0\1\3\3\3\3\1\0\0"
+ "\0\0\0\0\300\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\200\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\376\376\374\370\360\360\340\300\200"
+ "\200\0\0\0\0\0\0\0\0\0\0\0\377\377\377\376\376\374\370\360\360\340\300\200\200\0\0\0\0\0\0\0"
+ "\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\374\374\370\360\340\340\300\200\0\377\377\377\377"
+ "\377\377\377\377\377\377\377\377\377\377\376\374\374\370\360\340\340\300\200\0\377\377\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\177\77\77\37\17\7\7\3\1\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\77\37\17\7"
+ "\7\3\1\0\377\377\377\177\177\77\37\17\17\7\3\1\1\0\0\0\0\0\0\0\0\0\0\0\377\377\377\177"
+ "\177\77\37\17\17\7\3\1\1\0\0\0\0\0\0\0\0\0\0\0\3\1\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\376\374\374\370\360\340\300\200\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\340\360\374"
+ "\377\377\377\377\377\377\377\377\377\376\370\300\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\300\340\360\374\376\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\37\0\0\0\0"
+ "\0\0\4\370\360\360\340\300\200\0\0\0\0\0\0\0\0\0\0\0\370\377\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\377\377\377\177\77\37\7\3\0\0\0\0\0\200\300\360\374\377\377\377\377\377\377\377\376\370\340\0\0\0"
+ "\0\0\0\0\3\37\177\377\377\377\377\377\377\377\377\377\77\17\7\1\0\0\0\0\0\200\300\360\370\374\376\377"
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\1\3\7\17\37\77\77\177\200"
+ "\0\0\0\0\0\0\340\374\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\17\1\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\200\300\340\340\360\360\370|<>>>~\377\377\377\377\377\377\377\177"
+ "\77\36\36\36\36<|\370\370\360\360\340\340\200\0\0\0\0\0\0\0\0\300\360\374\376\377\377\377\377\377\377"
+ "\377\360\340\300\300\300\300\340\360\377\377\377\377\377\377\370\360\340\340\340\340\360\370\377\377\377\377\377\377\377\377\377"
+ "\374\360\340\200\360\377\377\377\377\377\207\3\1\1\1\1\3\207\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\377\377\377\377\377\207\3\1\1\1\1\3\207\377\377\377\377\377\17\377\377\377\377\377\377\377\376~>>"
+ "\77\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\376\376\376\377\377\377"
+ "\177\77\37\7\0\0\3\17\77\177\377\377\360\340\300\300\300\300\340\360\377\377\377\377\377\377\377\377\377\377\77\17"
+ "\17\7\7\7\7\7\7\7\7\7\3\3\3\3\1\0\0\0\0\0\0\0\0\0\0\0\0\1\3\7\17\37"
+ "\37\77\77\177\177\177\377\377\377\377\377\377\377\377\377~\30\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\370\374\376\377\377\377\377\377\377\376\374\360\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\360\360\360\360\360\360\360\360\360\360\360\360"
+ "\360\363\377\377\377\377\377\377\377\377\363\360\360\360\360\360\360\360\360\360\360\360\360\360\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\374\374\376\376\377\377\377\377"
+ "\377\376\374\360\377\377\377\377\377\377\377\377\377\377\377\377\177\77\37\17\17\17\17\17\17\37\77\177\377\377\377\377"
+ "\377\377\377\377\377\377\377\377\3\3\7\7\17\17\17\17\7\7\3\0\377\377\377\377\377\377\377\377\377\377\377\377"
+ "\360\300\0\0\0\0\0\0\0\0\300\360\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\200\300\340\360\360\370\374\374\376\376\7\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\360\374\376\377\377\377\377\377\377\377"
+ "\377\377\377\340\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\374\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\374\360\300\200\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\17\177\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\377\377\377\377\377\377\377\377\376\374\370\360\360\340\340\300\300\300\200\200\200\200\0\0\0\0\0\0\200\200"
+ "\200\200\0\0\0\0\1\7\37\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\37\7\1\0\0\0\0\0\0\0\0\0\0\1\3\3\7"
+ "\17\17\37\37\37\77\77\77\77\177\177\177\177\177\177\77\77\77\77\37\37\37\17\17\7\3\3\1\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\200\200\300\340\360\360\370\374\374\376\377~\34\10\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\200\300\300\340\360\360\370\374\376\376\377\377\377\377\377\377\177\77\17\7\3"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\6\17\17\37\77\177\377"
+ "\377\377\377\377\377\377\77\37\7\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\370\374\376"
+ "\376\377\377\377\377\377\377\376\376\374\370\340\0\0\0\0\3\17\7\3\1\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\200\360\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\17\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`px\374\376\377\377\377\377\377\377"
+ "\177\177\177\77\77\37\17\7\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\200\300\340\360\360\370\374\374\374\376\376\376\377\377\377\377\377\77\77\77\77"
+ "\177~~\376\374\374\374\370\360\360\340\300\200\0\0\0\0\0\0\0\0\0\340\360\374\376\377\377\377\377\377\377"
+ "\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\1\1\3\7\17\37\177\377\377\376\374"
+ "\360\340\0\0\370\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\1\17\377\377\377\377\377\370\37\377\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\360\377\377"
+ "\377\377\377\37\0\0\7\17\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0"
+ "\0\0\0\0\0\200\200\300\340\360\370\376\377\377\177\77\17\7\0\0\0\0\0\0\0\0\0\1\3\7\17\17"
+ "\37\77\77\77\177\177\177\377\377\377\377\377\374\374\374\374\376~~\177\77\77\77\37\17\17\7\3\1\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\300\340\360\370\374\376\376|"
+ "x \0\0\0\0\377\377\377\377\377\377\0\0\0\0 x|\376\376\374\370\360\340\300\200\0\0\0\0\0"
+ "\0\0\0\0\300\370\376\377\377\377\177\17\7\1\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0"
+ "\0\0\0\0\1\7\37\177\377\377\377\376\370\200\0\0\0\0\0\0\177\377\377\377\377\377\200\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\377\377\377\377\377\177\0\0"
+ "\0\0\0\0\0\7\37\177\377\377\377\374\370\340\300\200\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\200\200\300\340\370\374\377\377\377\177\37\7\0\0\0\0\0\0\0\0\0\0\0\0\1\3\7\17\37\37\77"
+ "\77\177~~~\374\374\374\374\374\374\374\374~~~\177\77\77\37\37\17\7\3\1\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\340\374\374\340\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\300\370\377\377\377\377\377\377\370\300\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\4\14\34<<|\374\374\374\374\374\374\374\374\374\374\374\376\377\377\377\377\377\377\377\377\377"
+ "\377\376\374\374\374\374\374\374\374\374\374\374\374|<<\34\14\4\0\0\0\0\0\0\0\0\0\1\3\3\7"
+ "\17\37\77\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\77\37\17\7\3\3\1\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\370\377\377\377\377\377\377\177\77\37\17\17\37\77\177"
+ "\377\377\377\377\377\377\370\300\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0p>"
+ "\37\17\17\7\3\1\0\0\0\0\0\0\0\0\0\0\0\0\1\3\7\17\17\37>p\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\200\200\200\300\300\300\300\300\300\200\200\200\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\200\200\200\300\300\300\300\300\300\200\200\200\0\0\0\0\0\0\200\360\370\374\376\377\377\377\377\377\377\377"
+ "\377\377\377\377\377\377\377\376\374\370\360\200\200\360\370\374\376\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376"
+ "\374\370\360\200\37\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\37\0\0\1\3\7\17\37\77\177\377\377\377"
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\37\17\7"
+ "\3\1\0\0\0\0\0\0\0\0\0\0\0\0\1\3\7\17\37\77\177\377\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\377\177\77\37\17\7\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\1\3\7\17\37\77\77\37\17\7\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\300\300\300\300\300\300"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\340\370\370\376\376\377\377\377\377\377\377\377\377\77\77\77>\376\370\370\340\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0 p\360\340\360p \0\0\0\0\0\0\377\377\377\377\177\177\177\177\177\207\207\340\340\377"
+ "\377\377\377\377\377\377\377\0\0\0\0\0 p\360\340\360p \0\6\4\14\14\15|x\360\200\200\0\0"
+ "pp\177\177\377\377\374|\374\374\374\177\177\177\377\377\377\177\377\377\377\377\177pp\0\0\200\200\360x}"
+ "\14\14\4\6\0\0\0\0\0\0\0\3\37\37|ppp\34\34\37\3\3\0\377\377\377\0\0\0\377\377"
+ "\377\0\3\3\37\37\34ppp~\37\37\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\7\7\7\0\0\0\7\7\7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0";
+
+
+
+/*
+ Fontname: akemi_8x8
+ Copyright: Benji (https://github.com/proto-molecule)
+ Glyphs: 1/1
+ BBX Build Mode: 3
+ * 12 = Akemi
+*/
+/*
+const uint8_t u8x8_akemi_8x8[516] U8X8_FONT_SECTION("u8x8_akemi_8x8") =
+ "\14\14\10\10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\200\200\200\200\200\200\200\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\340\340\370\370\376\376\376\376"
+ "\377\377\377\377\377\377\377\377\376\376\376\376\370\370\340\340\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\376\376\377\377\377\377\377\377\377\377"
+ "\377\377\377\377\37\37\37\343\343\343\343\343\343\377\377\377\376\376\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\30\30~~\370\370~~\30\30\0\0\0\0\0\0\0\377\377\377\377\377\77\77\77\77\77"
+ "\77\300\300\300\370\370\370\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\30\0f\0\200\0\0"
+ "\0\0\0\0\6\6\30\30\30\31\371\370\370\340\340\0\0\0\0\0\340\340\377\377\377\377\377\376\376\376\376\376"
+ "\376\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\371\346\346\6\6\6\6\6\0\340\340\340\341\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\1\1\37\37\377\376\376\340\340\200\201\201\341\341\177\177\37\37\1\1\377\377"
+ "\377\377\1\1\1\1\377\377\377\377\1\1\37\37\177\177\341\341\201\201\200\200\370\370\376\376\37\37\1\1\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\7\7\7\7\7\7\1\1\0\0\0\0\0\0\377\377"
+ "\377\377\0\0\0\0\377\377\377\377\0\0\0\0\0\0\1\1\7\7\7\7\7\7\1\1\0\0\0\0\0\0"
+ "\0\0\0";
+*/
\ No newline at end of file
diff --git a/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h b/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h
index 3dcb5af6..383accc5 100644
--- a/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h
+++ b/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h
@@ -2,6 +2,7 @@
#include "wled.h"
#include // from https://github.com/olikraus/u8g2/
+#include "4LD_wled_fonts.c"
//
// Insired by the usermod_v2_four_line_display
@@ -25,6 +26,10 @@
//The SCL and SDA pins are defined here.
#ifdef ARDUINO_ARCH_ESP32
+ #define HW_PIN_SCL 22
+ #define HW_PIN_SDA 21
+ #define HW_PIN_CLOCKSPI 18
+ #define HW_PIN_DATASPI 23
#ifndef FLD_PIN_SCL
#define FLD_PIN_SCL 22
#endif
@@ -47,6 +52,10 @@
#define FLD_PIN_RESET 26
#endif
#else
+ #define HW_PIN_SCL 5
+ #define HW_PIN_SDA 4
+ #define HW_PIN_CLOCKSPI 14
+ #define HW_PIN_DATASPI 13
#ifndef FLD_PIN_SCL
#define FLD_PIN_SCL 5
#endif
@@ -70,15 +79,20 @@
#endif
#endif
+#ifndef FLD_TYPE
+ #ifndef FLD_SPI_DEFAULT
+ #define FLD_TYPE SSD1306
+ #else
+ #define FLD_TYPE SSD1306_SPI
+ #endif
+#endif
+
// When to time out to the clock or blank the screen
// if SLEEP_MODE_ENABLED.
#define SCREEN_TIMEOUT_MS 60*1000 // 1 min
-#define TIME_INDENT 0
-#define DATE_INDENT 2
-
// Minimum time between redrawing screen in ms
-#define USER_LOOP_REFRESH_RATE_MS 100
+#define USER_LOOP_REFRESH_RATE_MS 1000
// Extra char (+1) for null
#define LINE_BUFFER_SIZE 16+1
@@ -96,172 +110,47 @@ typedef enum {
SSD1306_SPI64 // U8X8_SSD1306_128X64_NONAME_HW_SPI
} DisplayType;
-/*
- Fontname: benji_custom_icons_1x
- Copyright:
- Glyphs: 1/1
- BBX Build Mode: 3
- * 4 = custom palette
-*/
-const uint8_t u8x8_font_benji_custom_icons_1x1[13] U8X8_FONT_SECTION("u8x8_font_benji_custom_icons_1x1") =
- "\4\4\1\1<\370\360\3\17\77yy\377\377\377\377\317\17\17"
- "\17\17\7\3\360\360\360\360\366\377\377\366\360\360\360\360\0\0\0\0\377\377\377\377\237\17\17\237\377\377\377\377"
- "\6\17\17\6\340\370\374\376\377\340\200\0\0\0\0\0\0\0\0\0\3\17\37\77\177\177\177\377\376|||"
- "\70\30\14\0\0\0\0\0\0\0\0``\360\370|<\36\7\2\0\300\360\376\377\177\77\36\0\1\1\0"
- "\0\0\0\0\200\200\14\14\300\340\360\363\363\360\340\300\14\14\200\200\1\1\60\60\3\4\10\310\310\10\4\3"
- "\60\60\1\1";
-
-/*
- Fontname: benji_custom_icons_6x
- Copyright:
- Glyphs: 8/8
- BBX Build Mode: 3
- // 6x6 icons libraries take up a lot of memory thus all the icons uses are consolidated into a single library
- // these are just the required icons stripped from the U8x8 libraries in addition to a few new custom icons
- * 1 = sun
- * 2 = skip forward
- * 3 = fire
- * 4 = custom palette
- * 5 = puzzle piece
- * 6 = moon
- * 7 = brush
- * 8 = custom saturation
-*/
-const uint8_t u8x8_font_benji_custom_icons_6x6[2308] U8X8_FONT_SECTION("u8x8_font_benji_custom_icons_6x6") =
- "\1\10\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\200\300\340\340\340\360\360\360\360\360\360\340\340\340\300\200\0\0\0\0\7\17\17\17\17\7\0\0"
- "\0\0\0\0\300\340\340\340\340\300\0\0\0\0\0\0\340\374\376\377\377\377\377\377\377\377\377\377\377\377\377\377"
- "\377\377\377\377\377\376\374\340\0\0\0\0\0\0\300\340\340\340\340\300\3\7\7\7\7\3\0\0\0\0\0\0"
- "\7\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\7\0\0\0\0\0\0\3\7"
- "\7\7\7\3\0\0\0\0\0\0\340\360\360\360\360\340\0\0\0\0\1\3\7\7\7\17\17\17\17\17\17\7"
- "\7\7\3\1\0\0\0\0\340\360\360\360\360\340\0\0\0\0\0\0\0\0\0\0\0\0\1\3\3\3\3\1"
- "\0\0\0\0\0\0\0\0\0x\374\374\374\374x\0\0\0\0\0\0\0\0\0\1\3\3\3\3\1\0\0"
- "\0\0\0\0\300\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\200\0\0"
- "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\376\376\374\370\360\360\340\300\200"
- "\200\0\0\0\0\0\0\0\0\0\0\0\377\377\377\376\376\374\370\360\360\340\300\200\200\0\0\0\0\0\0\0"
- "\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\374\374\370\360\340\340\300\200\0\377\377\377\377"
- "\377\377\377\377\377\377\377\377\377\377\376\374\374\370\360\340\340\300\200\0\377\377\377\377\377\377\377\377\377\377\377\377"
- "\377\377\177\77\77\37\17\7\7\3\1\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\77\37\17\7"
- "\7\3\1\0\377\377\377\177\177\77\37\17\17\7\3\1\1\0\0\0\0\0\0\0\0\0\0\0\377\377\377\177"
- "\177\77\37\17\17\7\3\1\1\0\0\0\0\0\0\0\0\0\0\0\3\1\0\0\0\0\0\0\0\0\0\0"
- "\0\0\0\0\0\0\0\0\0\0\0\0\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\376\374\374\370\360\340\300\200\0\0\0\0\0\0\0"
- "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\340\360\374"
- "\377\377\377\377\377\377\377\377\377\376\370\300\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- "\0\0\0\0\0\0\300\340\360\374\376\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\37\0\0\0\0"
- "\0\0\4\370\360\360\340\300\200\0\0\0\0\0\0\0\0\0\0\0\370\377\377\377\377\377\377\377\377\377\377\377"
- "\377\377\377\377\377\177\77\37\7\3\0\0\0\0\0\200\300\360\374\377\377\377\377\377\377\377\376\370\340\0\0\0"
- "\0\0\0\0\3\37\177\377\377\377\377\377\377\377\377\377\77\17\7\1\0\0\0\0\0\200\300\360\370\374\376\377"
- "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\1\3\7\17\37\77\77\177\200"
- "\0\0\0\0\0\0\340\374\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\17\1\0\0"
- "\0\0\0\0\0\0\0\0\0\0\0\200\300\340\340\360\360\370|<>>>~\377\377\377\377\377\377\377\177"
- "\77\36\36\36\36<|\370\370\360\360\340\340\200\0\0\0\0\0\0\0\0\300\360\374\376\377\377\377\377\377\377"
- "\377\360\340\300\300\300\300\340\360\377\377\377\377\377\377\370\360\340\340\340\340\360\370\377\377\377\377\377\377\377\377\377"
- "\374\360\340\200\360\377\377\377\377\377\207\3\1\1\1\1\3\207\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
- "\377\377\377\377\377\377\377\207\3\1\1\1\1\3\207\377\377\377\377\377\17\377\377\377\377\377\377\377\376~>>"
- "\77\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\376\376\376\377\377\377"
- "\177\77\37\7\0\0\3\17\77\177\377\377\360\340\300\300\300\300\340\360\377\377\377\377\377\377\377\377\377\377\77\17"
- "\17\7\7\7\7\7\7\7\7\7\3\3\3\3\1\0\0\0\0\0\0\0\0\0\0\0\0\1\3\7\17\37"
- "\37\77\77\177\177\177\377\377\377\377\377\377\377\377\377~\30\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\370\374\376\377\377\377\377\377\377\376\374\360\0\0\0\0"
- "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\360\360\360\360\360\360\360\360\360\360\360\360"
- "\360\363\377\377\377\377\377\377\377\377\363\360\360\360\360\360\360\360\360\360\360\360\360\360\0\0\0\0\0\0\0\0"
- "\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
- "\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377"
- "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\374\374\376\376\377\377\377\377"
- "\377\376\374\360\377\377\377\377\377\377\377\377\377\377\377\377\177\77\37\17\17\17\17\17\17\37\77\177\377\377\377\377"
- "\377\377\377\377\377\377\377\377\3\3\7\7\17\17\17\17\7\7\3\0\377\377\377\377\377\377\377\377\377\377\377\377"
- "\360\300\0\0\0\0\0\0\0\0\300\360\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0"
- "\0\0\0\0\0\0\0\0\0\200\300\340\360\360\370\374\374\376\376\7\0\0\0\0\0\0\0\0\0\0\0\0"
- "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\360\374\376\377\377\377\377\377\377\377"
- "\377\377\377\340\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- "\0\0\0\0\374\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\374\360\300\200\0\0\0\0\0\0\0"
- "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\17\177\377\377\377\377\377\377\377\377\377\377"
- "\377\377\377\377\377\377\377\377\377\377\376\374\370\360\360\340\340\300\300\300\200\200\200\200\0\0\0\0\0\0\200\200"
- "\200\200\0\0\0\0\1\7\37\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
- "\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\37\7\1\0\0\0\0\0\0\0\0\0\0\1\3\3\7"
- "\17\17\37\37\37\77\77\77\77\177\177\177\177\177\177\77\77\77\77\37\37\37\17\17\7\3\3\1\0\0\0\0"
- "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- "\0\0\0\0\0\0\200\200\300\340\360\360\370\374\374\376\377~\34\10\0\0\0\0\0\0\0\0\0\0\0\0"
- "\0\0\0\0\0\0\0\0\0\0\0\200\300\300\340\360\360\370\374\376\376\377\377\377\377\377\377\177\77\17\7\3"
- "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\6\17\17\37\77\177\377"
- "\377\377\377\377\377\377\77\37\7\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\370\374\376"
- "\376\377\377\377\377\377\377\376\376\374\370\340\0\0\0\0\3\17\7\3\1\0\0\0\0\0\0\0\0\0\0\0"
- "\0\0\0\0\0\0\0\0\0\0\200\360\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\17\0\0\0\0"
- "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`px\374\376\377\377\377\377\377\377"
- "\177\177\177\77\77\37\17\7\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- "\0\0\0\0\0\0\0\0\0\0\0\200\300\300\200\0\0\0\0\0\0\0\0\0\14\36\77\77\36\14\0\0"
- "\0\0\0\0\0\0\0\200\300\300\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\7\17\17\7\3"
- "\0\200\300\340\360\360\370\370\370\374\374\374\374\370\370\370\360\360\340\300\200\0\3\7\17\17\7\3\0\0\0\0"
- "\0\0\0\0\300\340\360\360\340\300\0\0\0\0\340\374\377\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177"
- "\177\177\177\177\177\377\374\340\0\0\0\0\300\340\360\360\340\300\0\0\0\1\3\3\1\0\0\0\0\0\1\17"
- "\77\177\370\340\300\200\200\0\0\0\0\0\0\0\0\200\200\300\340\370\177\77\17\1\0\0\0\0\0\1\3\3"
- "\1\0\0\0\0\0\0\0\0\0\60x\374\374x\60\0\0\0\1\3\3\7\7\7\16\16\16\16\7\7\7"
- "\3\3\1\0\0\0\60x\374\374x\60\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- "\0\0\0\0\0\0\0\0\14\36\77\77\36\14\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- "\0\0\0";
class FourLineDisplayUsermod : public Usermod {
private:
bool initDone = false;
- unsigned long lastTime = 0;
// 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)
- DisplayType type = SSD1306_64; // display type
#else
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
- DisplayType type = SSD1306_SPI; // display type
+ 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
uint8_t lineHeight = 1; // 1 row or 2 rows
- uint32_t refreshRate = USER_LOOP_REFRESH_RATE_MS; // in ms
+ uint16_t refreshRate = USER_LOOP_REFRESH_RATE_MS; // in ms
uint32_t screenTimeout = SCREEN_TIMEOUT_MS; // in ms
bool sleepMode = true; // allow screen sleep?
bool clockMode = false; // display clock
-
- // needRedraw marks if redraw is required to prevent often redrawing.
- bool needRedraw = true;
+ 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.
- String knownSsid = "";
- IPAddress knownIp;
+ String knownSsid = apSSID;
+ IPAddress knownIp = IPAddress(4, 3, 2, 1);
uint8_t knownBrightness = 0;
uint8_t knownEffectSpeed = 0;
uint8_t knownEffectIntensity = 0;
uint8_t knownMode = 0;
uint8_t knownPalette = 0;
uint8_t knownMinute = 99;
+ uint8_t knownHour = 99;
byte brightness100;
byte fxspeed100;
byte fxintensity100;
@@ -270,22 +159,26 @@ class FourLineDisplayUsermod : public Usermod {
bool powerON = true;
bool displayTurnedOff = false;
- unsigned long lastUpdate = 0;
+ unsigned long nextUpdate = 0;
unsigned long lastRedraw = 0;
unsigned long overlayUntil = 0;
+
// Set to 2 or 3 to mark lines 2 or 3. Other values ignored.
- byte markLineNum = 0;
- byte markColNum = 0;
+ byte markLineNum = 255;
+ byte markColNum = 255;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
+ static const char _enabled[];
static const char _contrast[];
static const char _refreshRate[];
static const char _screenTimeOut[];
static const char _flip[];
static const char _sleepMode[];
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:
@@ -293,154 +186,189 @@ 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
// network here
void setup() {
- if (type == NONE) return;
- if (type == SSD1306_SPI || type == SSD1306_SPI64) {
- PinManagerPinType pins[5] = { { ioPin[0], true }, { ioPin[1], true}, { ioPin[2], true }, { ioPin[3], true}, { ioPin[4], true }};
- if (!pinManager.allocateMultiplePins(pins, 5, PinOwner::UM_FourLineDisplay)) { type=NONE; return; }
+ if (type == NONE || !enabled) return;
+
+ bool isHW, isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64);
+ PinOwner po = PinOwner::UM_FourLineDisplay;
+ 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; }
} else {
- PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true} };
- if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::UM_FourLineDisplay)) { type=NONE; return; }
+ isHW = (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA);
+ PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } };
+ if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
+ if (!pinManager.allocateMultiplePins(pins, 2, po)) { type=NONE; return; }
}
+
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:
- #ifdef ESP8266
- if (!(ioPin[0]==5 && ioPin[1]==4))
- u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
- else
- #endif
- u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
- lineHeight = 1;
+ 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:
- #ifdef ESP8266
- if (!(ioPin[0]==5 && ioPin[1]==4))
- u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
- else
- #endif
- u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
- lineHeight = 2;
+ 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:
- #ifdef ESP8266
- if (!(ioPin[0]==5 && ioPin[1]==4))
- u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
- else
- #endif
- u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
- lineHeight = 2;
+ 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:
- #ifdef ESP8266
- if (!(ioPin[0]==5 && ioPin[1]==4))
- u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
- else
- #endif
- u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
- lineHeight = 1;
+ 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:
- #ifdef ESP8266
- if (!(ioPin[0]==5 && ioPin[1]==4))
- u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
- else
- #endif
- u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
- lineHeight = 2;
+ 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:
- if (!(ioPin[0]==FLD_PIN_CLOCKSPI && ioPin[1]==FLD_PIN_DATASPI)) // if not overridden these sould be HW accellerated
- 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;
+ 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:
- if (!(ioPin[0]==FLD_PIN_CLOCKSPI && ioPin[1]==FLD_PIN_DATASPI)) // if not overridden these sould be HW accellerated
- 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;
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
break;
default:
u8x8 = nullptr;
}
+
if (nullptr == u8x8) {
DEBUG_PRINTLN(F("Display init failed."));
- for (byte i=0; i<5 && ioPin[i]>=0; i++) pinManager.deallocatePin(ioPin[i], PinOwner::UM_FourLineDisplay);
+ 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...");
+ //drawString(0, 0, "Loading...");
+ overlayLogo(3500);
+ initDone = true;
}
// gets called every time WiFi is (re-)connected. Initialize own network
// interfaces here
- void connected() {}
+ void connected() {
+ knownSsid = WiFi.SSID(); //apActive ? apSSID : WiFi.SSID(); //apActive ? WiFi.softAPSSID() :
+ knownIp = Network.localIP(); //apActive ? IPAddress(4, 3, 2, 1) : Network.localIP();
+ networkOverlay(PSTR("NETWORK INFO"),7000);
+ }
/**
* Da loop.
*/
void loop() {
- if (displayTurnedOff && millis() - lastUpdate < 1000) {
- return;
- }else if (millis() - lastUpdate < refreshRate){
- return;}
+ if (!enabled || strip.isUpdating()) return;
+ unsigned long now = millis();
+ if (now < nextUpdate) return;
+ nextUpdate = now + ((displayTurnedOff && clockMode && showSeconds) ? 1000 : refreshRate);
redraw(false);
- lastUpdate = millis();
}
/**
* Wrappers for screen drawing
*/
void setFlipMode(uint8_t mode) {
- if (type==NONE) return;
+ if (type == NONE || !enabled) return;
u8x8->setFlipMode(mode);
}
void setContrast(uint8_t contrast) {
- if (type==NONE) return;
+ if (type == NONE || !enabled) return;
u8x8->setContrast(contrast);
}
void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false) {
- if (type==NONE) return;
+ if (type == NONE || !enabled) return;
u8x8->setFont(u8x8_font_chroma48medium8_r);
if (!ignoreLH && lineHeight==2) u8x8->draw1x2String(col, row, string);
else u8x8->drawString(col, row, string);
}
void draw2x2String(uint8_t col, uint8_t row, const char *string) {
- if (type==NONE) return;
+ if (type == NONE || !enabled) return;
u8x8->setFont(u8x8_font_chroma48medium8_r);
u8x8->draw2x2String(col, row, string);
}
void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH=false) {
- if (type==NONE) return;
+ if (type == NONE || !enabled) return;
u8x8->setFont(font);
if (!ignoreLH && lineHeight==2) u8x8->draw1x2Glyph(col, row, glyph);
else u8x8->drawGlyph(col, row, glyph);
}
+ void draw2x2Glyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font) {
+ if (type == NONE || !enabled) return;
+ u8x8->setFont(font);
+ u8x8->draw2x2Glyph(col, row, glyph);
+ }
uint8_t getCols() {
- if (type==NONE) return 0;
+ if (type==NONE || !enabled) return 0;
return u8x8->getCols();
}
void clear() {
- if (type==NONE) return;
+ if (type == NONE || !enabled) return;
u8x8->clear();
}
void setPowerSave(uint8_t save) {
- if (type==NONE) return;
+ if (type == NONE || !enabled) return;
u8x8->setPowerSave(save);
}
@@ -451,99 +379,104 @@ class FourLineDisplayUsermod : public Usermod {
}
//function to update lastredraw
- void updateRedrawTime(){
- lastRedraw = millis();
- }
+ void updateRedrawTime() {
+ lastRedraw = millis();
+ }
/**
* Redraw the screen (but only if things have changed
* or if forceRedraw).
*/
void redraw(bool forceRedraw) {
- if (type==NONE) return;
- if (overlayUntil > 0) {
- if (millis() >= overlayUntil) {
- // Time to display the overlay has elapsed.
- overlayUntil = 0;
- forceRedraw = true;
- } else {
- // We are still displaying the overlay
- // Don't redraw.
- return;
- }
+ bool needRedraw = false;
+ unsigned long now = millis();
+
+ if (type == NONE || !enabled) return;
+ if (overlayUntil > 0) {
+ if (now >= overlayUntil) {
+ // Time to display the overlay has elapsed.
+ overlayUntil = 0;
+ forceRedraw = true;
+ } else {
+ // We are still displaying the overlay
+ // Don't redraw.
+ return;
}
+ }
+
+ if (apActive && WLED_WIFI_CONFIGURED && now<15000) {
+ knownSsid = apSSID;
+ networkOverlay(PSTR("NETWORK INFO"),30000);
+ return;
+ }
-
// Check if values which are shown on display changed from the last time.
if (forceRedraw) {
- needRedraw = true;
+ needRedraw = true;
+ clear();
} else if ((bri == 0 && powerON) || (bri > 0 && !powerON)) { //trigger power icon
- powerON = !powerON;
- drawStatusIcons();
- lastRedraw = millis();
+ powerON = !powerON;
+ drawStatusIcons();
+ return;
} else if (knownnightlight != nightlightActive) { //trigger moon icon
- knownnightlight = nightlightActive;
- drawStatusIcons();
- if (knownnightlight) overlay(" Timer On", 1000, 6);
- lastRedraw = millis();
- }else if (wificonnected != interfacesInited){ //trigger wifi icon
- wificonnected = interfacesInited;
- drawStatusIcons();
- lastRedraw = millis();
- } else if (knownMode != effectCurrent) {
- knownMode = effectCurrent;
- if(displayTurnedOff)needRedraw = true;
- else showCurrentEffectOrPalette(knownMode, JSON_mode_names, 3);
- } else if (knownPalette != effectPalette) {
- knownPalette = effectPalette;
- if(displayTurnedOff)needRedraw = true;
- else showCurrentEffectOrPalette(knownPalette, JSON_palette_names, 2);
+ knownnightlight = nightlightActive;
+ drawStatusIcons();
+ if (knownnightlight) {
+ String timer = PSTR("Timer On");
+ center(timer,LINE_BUFFER_SIZE-1);
+ overlay(timer.c_str(), 2500, 6);
+ }
+ return;
+ } else if (wificonnected != interfacesInited) { //trigger wifi icon
+ wificonnected = interfacesInited;
+ drawStatusIcons();
+ return;
+ } else if (knownMode != effectCurrent || knownPalette != effectPalette) {
+ if (displayTurnedOff) needRedraw = true;
+ 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){needRedraw = false; knownBrightness = bri;}
- else if(displayTurnedOff)needRedraw = true;
- else updateBrightness();
+ if (displayTurnedOff && nightlightActive) { knownBrightness = bri; }
+ else if (!displayTurnedOff) { updateBrightness(); lastRedraw = now; return; }
} else if (knownEffectSpeed != effectSpeed) {
- if(displayTurnedOff)needRedraw = true;
- else updateSpeed();
+ if (displayTurnedOff) needRedraw = true;
+ else { updateSpeed(); lastRedraw = now; return; }
} else if (knownEffectIntensity != effectIntensity) {
- if(displayTurnedOff)needRedraw = true;
- else updateIntensity();
+ if (displayTurnedOff) needRedraw = true;
+ else { updateIntensity(); lastRedraw = now; return; }
}
-
if (!needRedraw) {
// Nothing to change.
// Turn off display after 1 minutes with no change.
- if(sleepMode && !displayTurnedOff && (millis() - lastRedraw > screenTimeout)) {
+ if (sleepMode && !displayTurnedOff && (millis() - lastRedraw > screenTimeout)) {
// We will still check if there is a change in redraw()
// and turn it back on if it changed.
+ clear();
sleepOrClock(true);
- } else if (displayTurnedOff && clockMode) {
+ } else if (displayTurnedOff && ntpEnabled) {
showTime();
}
return;
- } else {
- clear();
}
- needRedraw = false;
- lastRedraw = millis();
+ lastRedraw = now;
- if (displayTurnedOff) {
- // Turn the display back on
- sleepOrClock(false);
- }
+ // Turn the display back on
+ wakeDisplay();
// Update last known values.
- knownSsid = apActive ? apSSID : WiFi.SSID(); //apActive ? WiFi.softAPSSID() :
- knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
- 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
@@ -563,57 +496,63 @@ class FourLineDisplayUsermod : public Usermod {
showCurrentEffectOrPalette(knownMode, JSON_mode_names, 3); //Effect Mode info
}
- void updateBrightness(){
+ void updateBrightness() {
knownBrightness = bri;
- if(overlayUntil == 0){
- brightness100 = (((float)(bri)/255)*100);
- char lineBuffer[4];
- sprintf_P(lineBuffer, PSTR("%-3d"), brightness100);
- drawString(1, lineHeight, lineBuffer);
- lastRedraw = millis();}
+ if (overlayUntil == 0) {
+ brightness100 = ((uint16_t)bri*100)/255;
+ char lineBuffer[4];
+ sprintf_P(lineBuffer, PSTR("%-3d"), brightness100);
+ drawString(1, lineHeight, lineBuffer);
+ //lastRedraw = millis();
+ }
}
- void updateSpeed(){
+ void updateSpeed() {
knownEffectSpeed = effectSpeed;
- if(overlayUntil == 0){
- fxspeed100 = (((float)(effectSpeed)/255)*100);
- char lineBuffer[4];
- sprintf_P(lineBuffer, PSTR("%-3d"), fxspeed100);
- drawString(5, lineHeight, lineBuffer);
- lastRedraw = millis();}
+ if (overlayUntil == 0) {
+ fxspeed100 = ((uint16_t)effectSpeed*100)/255;
+ char lineBuffer[4];
+ sprintf_P(lineBuffer, PSTR("%-3d"), fxspeed100);
+ drawString(5, lineHeight, lineBuffer);
+ //lastRedraw = millis();
+ }
}
- void updateIntensity(){
+ void updateIntensity() {
knownEffectIntensity = effectIntensity;
- if(overlayUntil == 0){
- fxintensity100 = (((float)(effectIntensity)/255)*100);
- char lineBuffer[4];
- sprintf_P(lineBuffer, PSTR("%-3d"), fxintensity100);
- drawString(9, lineHeight, lineBuffer);
- lastRedraw = millis();}
- }
-
- void draw2x2GlyphIcons(){
- if(lineHeight == 2){
- drawGlyph(1, 0, 1, u8x8_font_benji_custom_icons_2x2, true);//brightness icon
- drawGlyph(5, 0, 2, u8x8_font_benji_custom_icons_2x2, true);//speed icon
- drawGlyph(9, 0, 3, u8x8_font_benji_custom_icons_2x2, true);//intensity icon
- drawGlyph(14, 2*lineHeight, 4, u8x8_font_benji_custom_icons_2x2, true);//palette icon
- drawGlyph(14, 3*lineHeight, 5, u8x8_font_benji_custom_icons_2x2, true);//effect icon
- }
- else{
- drawGlyph(2, 0, 69, u8x8_font_open_iconic_weather_1x1);//brightness icon
- drawGlyph(6, 0, 72, u8x8_font_open_iconic_play_1x1);//speed icon
- drawGlyph(10, 0, 78, u8x8_font_open_iconic_thing_1x1);//intensity icon
- drawGlyph(15, 2*lineHeight, 4, u8x8_font_benji_custom_icons_1x1);//palette icon
- drawGlyph(15, 3*lineHeight, 70, u8x8_font_open_iconic_thing_1x1);//effect icon
+ if (overlayUntil == 0) {
+ fxintensity100 = ((uint16_t)effectIntensity*100)/255;
+ char lineBuffer[4];
+ sprintf_P(lineBuffer, PSTR("%-3d"), fxintensity100);
+ drawString(9, lineHeight, lineBuffer);
+ //lastRedraw = millis();
}
}
- void drawStatusIcons(){
- drawGlyph(14, 0, 80 + (wificonnected?0:1), u8x8_font_open_iconic_embedded_1x1, true); // wifi icon
- drawGlyph(15, 0, 78 + (bri > 0 ? 0 : 3), u8x8_font_open_iconic_embedded_1x1, true); // power icon
- drawGlyph(13, 0, 66 + (nightlightActive?0:4), u8x8_font_open_iconic_weather_1x1, true); // moon icon for nighlight mode
+ void draw2x2GlyphIcons() {
+ if (lineHeight == 2) {
+ drawGlyph( 1, 0, 1, u8x8_4LineDisplay_WLED_icons_2x2, true); //brightness icon
+ drawGlyph( 5, 0, 2, u8x8_4LineDisplay_WLED_icons_2x2, true); //speed icon
+ drawGlyph( 9, 0, 3, u8x8_4LineDisplay_WLED_icons_2x2, true); //intensity 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
+ } else {
+ 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
+ }
+ }
+
+ void drawStatusIcons() {
+ uint8_t col = 15;
+ uint8_t row = 0;
+ 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 { col = row = 0; }
+ drawGlyph(col, row, (nightlightActive ? 6 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // moon icon for nighlight mode
}
/**
@@ -622,99 +561,69 @@ class FourLineDisplayUsermod : public Usermod {
* pass line and colum info
*/
void setMarkLine(byte newMarkLineNum, byte newMarkColNum) {
- markLineNum = newMarkLineNum;
- markColNum = newMarkColNum;
+ markLineNum = newMarkLineNum;
+ markColNum = newMarkColNum;
}
//Draw the arrow for the current setting beiong changed
- void drawArrow(){
- if(markColNum != 255 && markLineNum !=255)drawGlyph(markColNum, markLineNum*lineHeight, 69, u8x8_font_open_iconic_play_1x1);
+ void drawArrow() {
+ if (markColNum != 255 && markLineNum !=255) drawGlyph(markColNum, markLineNum*lineHeight, 21, u8x8_4LineDisplay_WLED_icons_1x1);
}
//Display the current effect or palette (desiredEntry)
// on the appropriate line (row).
void showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row) {
- knownMode = effectCurrent;
- knownPalette = effectPalette;
- if(overlayUntil == 0){
- char lineBuffer[MAX_JSON_CHARS];
+ char lineBuffer[MAX_JSON_CHARS];
+ 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];
- char smallBuffer3[MAX_MODE_LINE_SPACE+1];
- uint8_t qComma = 0;
- bool insideQuotes = false;
- bool spaceHit = false;
- uint8_t printedChars = 0;
uint8_t smallChars1 = 0;
uint8_t smallChars2 = 0;
- uint8_t smallChars3 = 0;
- uint8_t totalCount = 0;
- char singleJsonSymbol;
-
- // Find the mode name in JSON
- for (size_t i = 0; i < strlen_P(qstring); i++) { //find and get the full text for printing
- singleJsonSymbol = pgm_read_byte_near(qstring + i);
- if (singleJsonSymbol == '\0') break;
- switch (singleJsonSymbol) {
- case '"':
- insideQuotes = !insideQuotes;
- break;
- case '[':
- case ']':
- break;
- case ',':
- qComma++;
- default:
- if (!insideQuotes || (qComma != inputEffPal)) break;
- lineBuffer[printedChars++] = singleJsonSymbol;
- totalCount++;
- }
- if ((qComma > inputEffPal)) break;
- }
-
- if(lineHeight ==2){ // use this code for 8 line display
- if(printedChars < (MAX_MODE_LINE_SPACE)){ // use big font if the text fits
- for (;printedChars < (MAX_MODE_LINE_SPACE-1); printedChars++) {lineBuffer[printedChars]=' '; }
- lineBuffer[printedChars] = 0;
- drawString(1, row*lineHeight, lineBuffer);
- lastRedraw = millis();
- }else{ // for long names divide the text into 2 lines and print them small
- for (uint8_t i = 0; i < printedChars; i++){
- switch (lineBuffer[i]){
- case ' ':
- if(i > 4 && !spaceHit) {
- spaceHit = true;
- break;}
- if(!spaceHit) smallBuffer1[smallChars1++] = lineBuffer[i];
- if (spaceHit) smallBuffer2[smallChars2++] = lineBuffer[i];
- break;
- default:
- if(!spaceHit) smallBuffer1[smallChars1++] = lineBuffer[i];
- if (spaceHit) smallBuffer2[smallChars2++] = lineBuffer[i];
+ if (printedChars < MAX_MODE_LINE_SPACE) { // use big font if the text fits
+ 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
+ bool spaceHit = false;
+ for (uint8_t i = 0; i < printedChars; i++) {
+ switch (lineBuffer[i]) {
+ case ' ':
+ if (i > 4 && !spaceHit) {
+ spaceHit = true;
break;
}
- }
- for (; smallChars1 < (MAX_MODE_LINE_SPACE-1); smallChars1++) smallBuffer1[smallChars1]=' ';
- smallBuffer1[smallChars1] = 0;
- drawString(1, row*lineHeight, smallBuffer1, true);
- for (; smallChars2 < (MAX_MODE_LINE_SPACE-1); smallChars2++) smallBuffer2[smallChars2]=' ';
- smallBuffer2[smallChars2] = 0;
- drawString(1, row*lineHeight+1, smallBuffer2, true);
- lastRedraw = millis();
+ if (spaceHit) smallBuffer2[smallChars2++] = lineBuffer[i];
+ else smallBuffer1[smallChars1++] = lineBuffer[i];
+ break;
+ default:
+ if (spaceHit) smallBuffer2[smallChars2++] = lineBuffer[i];
+ else smallBuffer1[smallChars1++] = lineBuffer[i];
+ break;
+ }
}
+ while (smallChars1 < (MAX_MODE_LINE_SPACE-1)) smallBuffer1[smallChars1++]=' ';
+ smallBuffer1[smallChars1] = 0;
+ drawString(1, row*lineHeight, smallBuffer1, true);
+ 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
- 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]=' ';
- smallBuffer3[smallChars3] = 0;
- drawString(1, row*lineHeight, smallBuffer3, true);
- lastRedraw = millis();
- }
- }
+ } else { // use this code for 4 ling displays
+ char smallBuffer3[MAX_MODE_LINE_SPACE+1]; // uses 1x1 icon for mode/palette
+ uint8_t smallChars3 = 0;
+ 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);
+ }
+ }
}
/**
@@ -724,60 +633,147 @@ class FourLineDisplayUsermod : public Usermod {
* to wake up the screen.
*/
bool wakeDisplay() {
- //knownHour = 99;
+ if (type == NONE || !enabled) return false;
if (displayTurnedOff) {
+ clear();
// Turn the display back on
sleepOrClock(false);
- redraw(true);
+ //lastRedraw = millis();
return true;
}
return false;
}
/**
- * Allows you to show one line and a glyph as overlay for a
- * period of time.
+ * Allows you to show one line and a glyph as overlay for a period of time.
* Clears the screen and prints.
+ * Used in Rotary Encoder usermod.
*/
void overlay(const char* line1, long showHowLong, byte glyphType) {
- if (displayTurnedOff) {
- // Turn the display back on
- sleepOrClock(false);
- }
+ // Turn the display back on
+ if (!wakeDisplay()) clear();
+ // Print the overlay
+ if (glyphType>0 && glyphType<255) {
+ 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;
+ center(buf, getCols());
+ drawString(0, (glyphType<255?3:0)*lineHeight, buf.c_str());
+ }
+ overlayUntil = millis() + showHowLong;
+ }
- // Print the overlay
- clear();
- if (glyphType > 0){
- if ( lineHeight == 2) drawGlyph(5, 0, glyphType, u8x8_font_benji_custom_icons_6x6, true);
- else drawGlyph(7, lineHeight, glyphType, u8x8_font_benji_custom_icons_2x2, true);
- }
- if (line1) drawString(0, 3*lineHeight, line1);
- overlayUntil = millis() + showHowLong;
+ /**
+ * Allows you to show Akemi WLED logo overlay for a period of time.
+ * Clears the screen and prints.
+ */
+ void overlayLogo(long showHowLong) {
+ // Turn the display back on
+ if (!wakeDisplay()) clear();
+ // Print the overlay
+ if (lineHeight == 2) {
+ //add a bit of randomness
+ switch (millis()%3) {
+ case 0:
+ //WLED
+ draw2x2Glyph( 0, 2, 1, u8x8_wled_logo_2x2);
+ draw2x2Glyph( 4, 2, 2, u8x8_wled_logo_2x2);
+ draw2x2Glyph( 8, 2, 3, u8x8_wled_logo_2x2);
+ draw2x2Glyph(12, 2, 4, u8x8_wled_logo_2x2);
+ break;
+ case 1:
+ //WLED Akemi
+ drawGlyph( 2, 2, 1, u8x8_wled_logo_akemi_4x4, true);
+ drawGlyph( 6, 2, 2, u8x8_wled_logo_akemi_4x4, true);
+ drawGlyph(10, 2, 3, u8x8_wled_logo_akemi_4x4, true);
+ break;
+ case 2:
+ //Akemi
+ //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;
+ }
+ } else {
+ switch (millis()%3) {
+ case 0:
+ //WLED
+ draw2x2Glyph( 0, 0, 1, u8x8_wled_logo_2x2);
+ draw2x2Glyph( 4, 0, 2, u8x8_wled_logo_2x2);
+ draw2x2Glyph( 8, 0, 3, u8x8_wled_logo_2x2);
+ draw2x2Glyph(12, 0, 4, u8x8_wled_logo_2x2);
+ break;
+ case 1:
+ //WLED Akemi
+ drawGlyph( 2, 0, 1, u8x8_wled_logo_akemi_4x4);
+ drawGlyph( 6, 0, 2, u8x8_wled_logo_akemi_4x4);
+ drawGlyph(10, 0, 3, u8x8_wled_logo_akemi_4x4);
+ break;
+ case 2:
+ //Akemi
+ //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;
+ }
+ }
+ overlayUntil = millis() + showHowLong;
+ }
+
+ /**
+ * Allows you to show two lines as overlay for a period of time.
+ * Clears the screen and prints.
+ * Used in Auto Save usermod
+ */
+ void overlay(const char* line1, const char* line2, long showHowLong) {
+ // Turn the display back on
+ if (!wakeDisplay()) clear();
+ // Print the overlay
+ if (line1) {
+ String buf = line1;
+ center(buf, getCols());
+ drawString(0, 1*lineHeight, buf.c_str());
+ }
+ if (line2) {
+ String buf = line2;
+ center(buf, getCols());
+ drawString(0, 2*lineHeight, buf.c_str());
+ }
+ overlayUntil = millis() + showHowLong;
}
void networkOverlay(const char* line1, long showHowLong) {
- if (displayTurnedOff) {
- // Turn the display back on
- sleepOrClock(false);
- }
+ String line;
+ // Turn the display back on
+ if (!wakeDisplay()) clear();
// Print the overlay
- clear();
- // First row string
- if (line1) drawString(0, 0, line1);
+ if (line1) {
+ line = line1;
+ center(line, getCols());
+ drawString(0, 0, line.c_str());
+ }
// Second row with Wifi name
- String ssidString = knownSsid.substring(0, getCols() > 1 ? getCols() - 2 : 0); //
- drawString(0, lineHeight, ssidString.c_str());
+ line = knownSsid.substring(0, getCols() > 1 ? getCols() - 2 : 0);
+ if (line.length() < getCols()) center(line, getCols());
+ drawString(0, lineHeight, line.c_str());
// Print `~` char to indicate that SSID is longer, than our display
- if (knownSsid.length() > getCols()) {
- drawString(getCols() - 1, 0, "~");
- }
- // Third row with IP and Psssword in AP Mode
- drawString(0, lineHeight*2, (knownIp.toString()).c_str());
- if (apActive) {
- String appassword = apPass;
- drawString(0, lineHeight*3, appassword.c_str());
- }
- overlayUntil = millis() + showHowLong;
+ if (knownSsid.length() > getCols()) {
+ drawString(getCols() - 1, 0, "~");
+ }
+ // Third row with IP and Password in AP Mode
+ line = knownIp.toString();
+ center(line, getCols());
+ drawString(0, lineHeight*2, line.c_str());
+ line = "";
+ if (apActive) {
+ line = apPass;
+ } else if (strcmp(serverDescription, "WLED") != 0) {
+ line = serverDescription;
+ }
+ center(line, getCols());
+ drawString(0, lineHeight*3, line.c_str());
+ overlayUntil = millis() + showHowLong;
}
@@ -786,16 +782,15 @@ class FourLineDisplayUsermod : public Usermod {
*/
void sleepOrClock(bool enabled) {
if (enabled) {
- if (clockMode) {
- clear();
- knownMinute = 99;
- showTime();
- }else setPowerSave(1);
displayTurnedOff = true;
- }
- else {
- setPowerSave(0);
+ if (clockMode && ntpEnabled) {
+ knownMinute = knownHour = 99;
+ showTime();
+ } else
+ setPowerSave(1);
+ } else {
displayTurnedOff = false;
+ setPowerSave(0);
}
}
@@ -805,31 +800,120 @@ class FourLineDisplayUsermod : public Usermod {
* the useAMPM configuration.
*/
void showTime() {
- if(knownMinute != minute(localTime)){ //only redraw clock if it has changed
+ if (type == NONE || !enabled || !displayTurnedOff) return;
+
char lineBuffer[LINE_BUFFER_SIZE];
+ static byte lastSecond;
+ byte secondCurrent = second(localTime);
+ byte minuteCurrent = minute(localTime);
+ byte hourCurrent = hour(localTime);
- //updateLocalTime();
- byte AmPmHour = hour(localTime);
- boolean isitAM = true;
- if (useAMPM) {
- if (AmPmHour > 11) AmPmHour -= 12;
- if (AmPmHour == 0) AmPmHour = 12;
- if (hour(localTime) > 11) isitAM = false;
- }
- clear();
- drawStatusIcons(); //icons power, wifi, timer, etc
-
- sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(month(localTime)), day(localTime));
- draw2x2String(DATE_INDENT, lineHeight==1 ? 0 : lineHeight, lineBuffer); // adjust for 8 line displays, draw month and day
-
- sprintf_P(lineBuffer,PSTR("%2d:%02d"), (useAMPM ? AmPmHour : hour(localTime)), minute(localTime));
- draw2x2String(TIME_INDENT+2, lineHeight*2, lineBuffer); //draw hour, min. blink ":" depending on odd/even seconds
-
+ if (knownMinute != minuteCurrent) { //only redraw clock if it has changed
+ //updateLocalTime();
+ byte AmPmHour = hourCurrent;
+ boolean isitAM = true;
+ if (useAMPM) {
+ if (AmPmHour > 11) { AmPmHour -= 12; isitAM = false; }
+ if (AmPmHour == 0) { AmPmHour = 12; }
+ }
+ if (knownHour != hourCurrent) {
+ // only update date when hour changes
+ sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(month(localTime)), day(localTime));
+ draw2x2String(2, lineHeight==1 ? 0 : lineHeight, lineBuffer); // adjust for 8 line displays, draw month and day
+ }
+ sprintf_P(lineBuffer,PSTR("%2d:%02d"), (useAMPM ? AmPmHour : hourCurrent), minuteCurrent);
+ draw2x2String(2, lineHeight*2, lineBuffer); //draw hour, min. blink ":" depending on odd/even seconds
if (useAMPM) drawString(12, lineHeight*2, (isitAM ? "AM" : "PM"), true); //draw am/pm if using 12 time
- knownMinute = minute(localTime);
+
+ drawStatusIcons(); //icons power, wifi, timer, etc
+
+ knownMinute = minuteCurrent;
+ knownHour = hourCurrent;
+ } else {
+ if (secondCurrent == lastSecond) return;
+ }
+ if (showSeconds) {
+ lastSecond = secondCurrent;
+ draw2x2String(6, lineHeight*2, secondCurrent%2 ? " " : ":");
+ sprintf_P(lineBuffer, PSTR("%02d"), secondCurrent);
+ drawString(12, lineHeight*2+1, lineBuffer, true); // even with double sized rows print seconds in 1 line
}
}
+ /**
+ * 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.
@@ -873,16 +957,20 @@ class FourLineDisplayUsermod : public Usermod {
*/
void addToConfig(JsonObject& root) {
JsonObject top = root.createNestedObject(FPSTR(_name));
+ top[FPSTR(_enabled)] = enabled;
JsonArray io_pin = top.createNestedArray("pin");
for (byte i=0; i<5; i++) io_pin.add(ioPin[i]);
- top["help4PinTypes"] = F("Clk,Data,CS,DC,RST"); // help for Settings page
+ top["help4Pins"] = F("Clk,Data,CS,DC,RST"); // help for Settings page
top["type"] = type;
+ 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(_refreshRate)] = refreshRate/10;
+ top[FPSTR(_contrastFix)] = (bool) contrastFix;
+ top[FPSTR(_refreshRate)] = refreshRate;
top[FPSTR(_screenTimeOut)] = screenTimeout/1000;
top[FPSTR(_sleepMode)] = (bool) sleepMode;
top[FPSTR(_clockMode)] = (bool) clockMode;
+ top[FPSTR(_showSeconds)] = (bool) showSeconds;
top[FPSTR(_busClkFrequency)] = ioFrequency/1000;
DEBUG_PRINTLN(F("4 Line Display config saved."));
}
@@ -907,15 +995,22 @@ class FourLineDisplayUsermod : public Usermod {
return false;
}
+ enabled = top[FPSTR(_enabled)] | enabled;
newType = top["type"] | newType;
for (byte i=0; i<5; i++) newPin[i] = top["pin"][i] | ioPin[i];
flip = top[FPSTR(_flip)] | flip;
contrast = top[FPSTR(_contrast)] | contrast;
- refreshRate = (top[FPSTR(_refreshRate)] | refreshRate/10) * 10;
+ refreshRate = top[FPSTR(_refreshRate)] | refreshRate;
+ refreshRate = min(5000, max(250, (int)refreshRate));
screenTimeout = (top[FPSTR(_screenTimeOut)] | screenTimeout/1000) * 1000;
sleepMode = top[FPSTR(_sleepMode)] | sleepMode;
clockMode = top[FPSTR(_clockMode)] | clockMode;
- ioFrequency = min(3400, max(100, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
+ 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
+ ioFrequency = min(3400, max(100, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
@@ -930,24 +1025,28 @@ class FourLineDisplayUsermod : public Usermod {
for (byte i=0; i<5; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; }
if (pinsChanged || type!=newType) {
if (type != NONE) delete u8x8;
- for (byte i=0; i<5; i++) {
- if (ioPin[i]>=0) pinManager.deallocatePin(ioPin[i], PinOwner::UM_FourLineDisplay);
- ioPin[i] = newPin[i];
- }
+ PinOwner po = PinOwner::UM_FourLineDisplay;
+ if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
+ pinManager.deallocateMultiplePins((const uint8_t *)ioPin, (type == SSD1306_SPI || type == SSD1306_SPI64) ? 5 : 2, po);
+ for (byte i=0; i<5; i++) ioPin[i] = newPin[i];
if (ioPin[0]<0 || ioPin[1]<0) { // data & clock must be > -1
type = NONE;
return true;
} else type = newType;
setup();
needsRedraw |= true;
+ } else {
+ u8x8->setBusClock(ioFrequency); // can be used for SPI too
+ setVcomh(contrastFix);
+ setContrast(contrast);
+ setFlipMode(flip);
}
- if (!(type == SSD1306_SPI || type == SSD1306_SPI64)) 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[_busClkFrequency]).isNull();
+ return !top[FPSTR(_contrastFix)].isNull();
}
/*
@@ -960,11 +1059,14 @@ class FourLineDisplayUsermod : public Usermod {
};
// strings to reduce flash memory usage (used more than twice)
-const char FourLineDisplayUsermod::_name[] PROGMEM = "4LineDisplay";
-const char FourLineDisplayUsermod::_contrast[] PROGMEM = "contrast";
-const char FourLineDisplayUsermod::_refreshRate[] PROGMEM = "refreshRate0.01Sec";
-const char FourLineDisplayUsermod::_screenTimeOut[] PROGMEM = "screenTimeOutSec";
-const char FourLineDisplayUsermod::_flip[] PROGMEM = "flip";
-const char FourLineDisplayUsermod::_sleepMode[] PROGMEM = "sleepMode";
-const char FourLineDisplayUsermod::_clockMode[] PROGMEM = "clockMode";
+const char FourLineDisplayUsermod::_name[] PROGMEM = "4LineDisplay";
+const char FourLineDisplayUsermod::_enabled[] PROGMEM = "enabled";
+const char FourLineDisplayUsermod::_contrast[] PROGMEM = "contrast";
+const char FourLineDisplayUsermod::_refreshRate[] PROGMEM = "refreshRate-ms";
+const char FourLineDisplayUsermod::_screenTimeOut[] PROGMEM = "screenTimeOutSec";
+const char FourLineDisplayUsermod::_flip[] PROGMEM = "flip";
+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";
diff --git a/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h b/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h
index 625af0af..98d84016 100644
--- a/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h
+++ b/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h
@@ -19,17 +19,20 @@
// Change between modes by pressing a button.
//
// Dependencies
-// * This usermod REQURES the ModeSortUsermod
// * This Usermod works best coupled with
// FourLineDisplayUsermod.
//
-// If FourLineDisplayUsermod is used the folowing options are also inabled
+// If FourLineDisplayUsermod is used the folowing options are also enabled
//
// * main color
// * saturation of main color
// * display network (long press buttion)
//
+#ifdef USERMOD_MODE_SORT
+ #error "Usermod Mode Sort is no longer required. Remove -D USERMOD_MODE_SORT from platformio.ini"
+#endif
+
#ifndef ENCODER_DT_PIN
#define ENCODER_DT_PIN 18
#endif
@@ -44,27 +47,90 @@
// 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 6
+ #define LAST_UI_STATE 8
#else
#define LAST_UI_STATE 4
#endif
+// Number of modes at the start of the list to not sort
+#define MODE_SORT_SKIP_COUNT 1
+
+// Which list is being sorted
+static char **listBeingSorted;
+
+/**
+ * Modes and palettes are stored as strings that
+ * end in a quote character. Compare two of them.
+ * We are comparing directly within either
+ * JSON_mode_names or JSON_palette_names.
+ */
+static int re_qstringCmp(const void *ap, const void *bp) {
+ char *a = listBeingSorted[*((byte *)ap)];
+ char *b = listBeingSorted[*((byte *)bp)];
+ int i = 0;
+ do {
+ char aVal = pgm_read_byte_near(a + i);
+ if (aVal >= 97 && aVal <= 122) {
+ // Lowercase
+ aVal -= 32;
+ }
+ char bVal = pgm_read_byte_near(b + i);
+ if (bVal >= 97 && bVal <= 122) {
+ // Lowercase
+ bVal -= 32;
+ }
+ // Relly we shouldn't ever get to '\0'
+ if (aVal == '"' || bVal == '"' || aVal == '\0' || bVal == '\0') {
+ // We're done. one is a substring of the other
+ // or something happenend and the quote didn't stop us.
+ if (aVal == bVal) {
+ // Same value, probably shouldn't happen
+ // with this dataset
+ return 0;
+ }
+ else if (aVal == '"' || aVal == '\0') {
+ return -1;
+ }
+ else {
+ return 1;
+ }
+ }
+ if (aVal == bVal) {
+ // Same characters. Move to the next.
+ i++;
+ continue;
+ }
+ // We're done
+ if (aVal < bVal) {
+ return -1;
+ }
+ else {
+ return 1;
+ }
+ } while (true);
+ // We shouldn't get here.
+ return 0;
+}
+
class RotaryEncoderUIUsermod : public Usermod {
private:
- int fadeAmount = 5; // Amount to change every step (brightness)
- unsigned long currentTime;
+ int8_t fadeAmount = 5; // Amount to change every step (brightness)
unsigned long loopTime;
- unsigned long buttonHoldTIme;
+
+ unsigned long buttonPressedTime = 0;
+ unsigned long buttonWaitTime = 0;
+ bool buttonPressedBefore = false;
+ bool buttonLongPressed = false;
+
int8_t pinA = ENCODER_DT_PIN; // DT from encoder
int8_t pinB = ENCODER_CLK_PIN; // CLK from encoder
int8_t pinC = ENCODER_SW_PIN; // SW from encoder
- unsigned char select_state = 0; // 0: brightness, 1: effect, 2: effect speed
- unsigned char button_state = HIGH;
- unsigned char prev_button_state = HIGH;
- bool networkShown = false;
- uint16_t currentHue1 = 6425; // default reboot color
- byte currentSat1 = 255;
+
+ unsigned char select_state = 0; // 0: brightness, 1: effect, 2: effect speed, ...
+
+ uint16_t currentHue1 = 16; // default boot color
+ byte currentSat1 = 255;
#ifdef USERMOD_FOUR_LINE_DISPLAY
FourLineDisplayUsermod *display;
@@ -72,7 +138,16 @@ private:
void* display = nullptr;
#endif
+ // Pointers the start of the mode names within JSON_mode_names
+ char **modes_qstrings = nullptr;
+
+ // Array of mode indexes in alphabetical order.
byte *modes_alpha_indexes = nullptr;
+
+ // Pointers the start of the palette names within JSON_palette_names
+ char **palettes_qstrings = nullptr;
+
+ // Array of palette indexes in alphabetical order.
byte *palettes_alpha_indexes = nullptr;
unsigned char Enc_A;
@@ -85,6 +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;
@@ -94,14 +177,94 @@ private:
static const char _DT_pin[];
static const char _CLK_pin[];
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
+ * modes_alpha_indexes and palettes_alpha_indexes.
+ */
+ void sortModesAndPalettes() {
+ modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount());
+ modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
+ re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
+
+ palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount());
+ palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount());
+
+ // How many palette names start with '*' and should not be sorted?
+ // (Also skipping the first one, 'Default').
+ int skipPaletteCount = 1;
+ while (pgm_read_byte_near(palettes_qstrings[skipPaletteCount++]) == '*') ;
+ re_sortModes(palettes_qstrings, palettes_alpha_indexes, strip.getPaletteCount(), skipPaletteCount);
+ }
+
+ byte *re_initIndexArray(int numModes) {
+ byte *indexes = (byte *)malloc(sizeof(byte) * numModes);
+ for (byte i = 0; i < numModes; i++) {
+ indexes[i] = i;
+ }
+ return indexes;
+ }
+
+ /**
+ * Return an array of mode or palette names from the JSON string.
+ * They don't end in '\0', they end in '"'.
+ */
+ char **re_findModeStrings(const char json[], int numModes) {
+ char **modeStrings = (char **)malloc(sizeof(char *) * numModes);
+ uint8_t modeIndex = 0;
+ bool insideQuotes = false;
+ // advance past the mark for markLineNum that may exist.
+ char singleJsonSymbol;
+
+ // Find the mode name in JSON
+ bool complete = false;
+ for (size_t i = 0; i < strlen_P(json); i++) {
+ singleJsonSymbol = pgm_read_byte_near(json + i);
+ if (singleJsonSymbol == '\0') break;
+ switch (singleJsonSymbol) {
+ case '"':
+ insideQuotes = !insideQuotes;
+ if (insideQuotes) {
+ // We have a new mode or palette
+ modeStrings[modeIndex] = (char *)(json + i + 1);
+ }
+ break;
+ case '[':
+ break;
+ case ']':
+ if (!insideQuotes) complete = true;
+ break;
+ case ',':
+ if (!insideQuotes) modeIndex++;
+ default:
+ if (!insideQuotes) break;
+ }
+ if (complete) break;
+ }
+ return modeStrings;
+ }
+
+ /**
+ * Sort either the modes or the palettes using quicksort.
+ */
+ void re_sortModes(char **modeNames, byte *indexes, int count, int numSkip) {
+ listBeingSorted = modeNames;
+ qsort(indexes + numSkip, count - numSkip, sizeof(byte), re_qstringCmp);
+ listBeingSorted = nullptr;
+ }
+
public:
/*
- * setup() is called once at boot. WiFi is not yet connected at this point.
- * You can use it to initialize variables, sensors or similar.
- */
+ * setup() is called once at boot. WiFi is not yet connected at this point.
+ * You can use it to initialize variables, sensors or similar.
+ */
void setup()
{
+ DEBUG_PRINTLN(F("Usermod Rotary Encoder init."));
PinManagerPinType pins[3] = { { pinA, false }, { pinB, false }, { pinC, false } };
if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_RotaryEncoderUI)) {
// BUG: configuring this usermod with conflicting pins
@@ -117,12 +280,17 @@ public:
pinMode(pinA, INPUT_PULLUP);
pinMode(pinB, INPUT_PULLUP);
pinMode(pinC, INPUT_PULLUP);
- currentTime = millis();
- loopTime = currentTime;
+ loopTime = millis();
- ModeSortUsermod *modeSortUsermod = (ModeSortUsermod*) usermods.lookup(USERMOD_ID_MODE_SORT);
- modes_alpha_indexes = modeSortUsermod->getModesAlphaIndexes();
- palettes_alpha_indexes = modeSortUsermod->getPalettesAlphaIndexes();
+ 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
// This Usermod uses FourLineDisplayUsermod for the best experience.
@@ -140,91 +308,87 @@ public:
}
/*
- * connected() is called every time the WiFi is (re)connected
- * Use it to initialize network interfaces
- */
+ * connected() is called every time the WiFi is (re)connected
+ * Use it to initialize network interfaces
+ */
void connected()
{
//Serial.println("Connected to WiFi!");
}
/*
- * loop() is called continuously. Here you can check for events, read sensors, etc.
- *
- * Tips:
- * 1. You can use "if (WLED_CONNECTED)" to check for a successful network connection.
- * Additionally, "if (WLED_MQTT_CONNECTED)" is available to check for a connection to an MQTT broker.
- *
- * 2. Try to avoid using the delay() function. NEVER use delays longer than 10 milliseconds.
- * Instead, use a timer check as shown here.
- */
+ * loop() is called continuously. Here you can check for events, read sensors, etc.
+ *
+ * Tips:
+ * 1. You can use "if (WLED_CONNECTED)" to check for a successful network connection.
+ * Additionally, "if (WLED_MQTT_CONNECTED)" is available to check for a connection to an MQTT broker.
+ *
+ * 2. Try to avoid using the delay() function. NEVER use delays longer than 10 milliseconds.
+ * Instead, use a timer check as shown here.
+ */
void loop()
{
- currentTime = millis(); // get the current elapsed time
+ if (!enabled || strip.isUpdating()) return;
+ unsigned long currentTime = millis(); // get the current elapsed time
// Initialize effectCurrentIndex and effectPaletteIndex to
// current state. We do it here as (at least) effectCurrent
// is not yet initialized when setup is called.
if (!currentEffectAndPaletteInitialized) {
- findCurrentEffectAndPalette();}
+ findCurrentEffectAndPalette();
+ }
- if(modes_alpha_indexes[effectCurrentIndex] != effectCurrent
- || palettes_alpha_indexes[effectPaletteIndex] != effectPalette){
+ if (modes_alpha_indexes[effectCurrentIndex] != effectCurrent || palettes_alpha_indexes[effectPaletteIndex] != effectPalette) {
currentEffectAndPaletteInitialized = false;
- }
+ }
if (currentTime >= (loopTime + 2)) // 2ms since last check of encoder = 500Hz
{
- button_state = digitalRead(pinC);
- if (prev_button_state != button_state)
- {
- if (button_state == HIGH && (millis()-buttonHoldTIme < 3000))
- {
- prev_button_state = button_state;
+ loopTime = currentTime; // Updates loopTime
- char newState = select_state + 1;
- if (newState > LAST_UI_STATE) newState = 0;
-
- bool changedState = true;
- if (display != nullptr) {
- switch(newState) {
- case 0:
- changedState = changeState(" Brightness", 1, 0, 1);
- break;
- case 1:
- changedState = changeState(" Speed", 1, 4, 2);
- break;
- case 2:
- changedState = changeState(" Intensity", 1 ,8, 3);
- break;
- case 3:
- changedState = changeState(" Color Palette", 2, 0, 4);
- break;
- case 4:
- changedState = changeState(" Effect", 3, 0, 5);
- break;
- case 5:
- changedState = changeState(" Main Color", 255, 255, 7);
- break;
- case 6:
- changedState = changeState(" Saturation", 255, 255, 8);
- break;
- }
- }
- if (changedState) {
- select_state = newState;
+ bool buttonPressed = !digitalRead(pinC); //0=pressed, 1=released
+ if (buttonPressed) {
+ if (!buttonPressedBefore) buttonPressedTime = currentTime;
+ buttonPressedBefore = true;
+ if (currentTime-buttonPressedTime > 3000) {
+ if (!buttonLongPressed) displayNetworkInfo(); //long press for network info
+ buttonLongPressed = true;
+ }
+ } else if (!buttonPressed && buttonPressedBefore) {
+ bool doublePress = buttonWaitTime;
+ buttonWaitTime = 0;
+ if (!buttonLongPressed) {
+ if (doublePress) {
+ toggleOnOff();
+ lampUdated();
+ } else {
+ buttonWaitTime = currentTime;
}
}
- else
- {
- prev_button_state = button_state;
- networkShown = false;
- if(!prev_button_state)buttonHoldTIme = millis();
- }
+ buttonLongPressed = false;
+ buttonPressedBefore = false;
+ }
+ 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 == 8 && presetHigh==0 && presetLow == 0)) newState = 0;
+ if (display != nullptr) {
+ switch (newState) {
+ case 0: changedState = changeState(PSTR("Brightness"), 1, 0, 1); break; //1 = sun
+ case 1: changedState = changeState(PSTR("Speed"), 1, 4, 2); break; //2 = skip forward
+ case 2: changedState = changeState(PSTR("Intensity"), 1, 8, 3); break; //3 = fire
+ case 3: changedState = changeState(PSTR("Color Palette"), 2, 0, 4); break; //4 = custom palette
+ 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("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 (!prev_button_state && (millis()-buttonHoldTIme > 3000) && !networkShown) displayNetworkInfo(); //long press for network info
Enc_A = digitalRead(pinA); // Read encoder pins
Enc_B = digitalRead(pinB);
@@ -233,65 +397,39 @@ public:
if (Enc_B == LOW) //changes to LOW so that then encoder registers a change at the very end of a pulse
{ // B is high so clockwise
switch(select_state) {
- case 0:
- changeBrightness(true);
- break;
- case 1:
- changeEffectSpeed(true);
- break;
- case 2:
- changeEffectIntensity(true);
- break;
- case 3:
- changePalette(true);
- break;
- case 4:
- changeEffect(true);
- break;
- case 5:
- changeHue(true);
- break;
- case 6:
- changeSat(true);
- break;
+ case 0: changeBrightness(true); break;
+ case 1: changeEffectSpeed(true); break;
+ case 2: changeEffectIntensity(true); break;
+ case 3: changePalette(true); break;
+ case 4: changeEffect(true); break;
+ case 5: changeHue(true); break;
+ case 6: changeSat(true); break;
+ case 7: changeCCT(true); break;
+ case 8: changePreset(true); break;
}
}
else if (Enc_B == HIGH)
{ // B is low so counter-clockwise
switch(select_state) {
- case 0:
- changeBrightness(false);
- break;
- case 1:
- changeEffectSpeed(false);
- break;
- case 2:
- changeEffectIntensity(false);
- break;
- case 3:
- changePalette(false);
- break;
- case 4:
- changeEffect(false);
- break;
- case 5:
- changeHue(false);
- break;
- case 6:
- changeSat(false);
- break;
+ case 0: changeBrightness(false); break;
+ case 1: changeEffectSpeed(false); break;
+ case 2: changeEffectIntensity(false); break;
+ case 3: changePalette(false); break;
+ case 4: changeEffect(false); break;
+ case 5: changeHue(false); break;
+ case 6: changeSat(false); break;
+ case 7: changeCCT(false); break;
+ case 8: changePreset(false); break;
}
}
}
Enc_A_prev = Enc_A; // Store value of A for next time
- loopTime = currentTime; // Updates loopTime
}
}
- void displayNetworkInfo(){
+ void displayNetworkInfo() {
#ifdef USERMOD_FOUR_LINE_DISPLAY
- display->networkOverlay(" NETWORK INFO", 15000);
- networkShown = true;
+ display->networkOverlay(PSTR("NETWORK INFO"), 10000);
#endif
}
@@ -313,180 +451,293 @@ public:
}
boolean changeState(const char *stateName, byte markedLine, byte markedCol, byte glyph) {
- #ifdef USERMOD_FOUR_LINE_DISPLAY
- if (display != nullptr) {
- if (display->wakeDisplay()) {
- // Throw away wake up input
- return false;
- }
- display->overlay(stateName, 750, glyph);
- display->setMarkLine(markedLine, markedCol);
- }
- #endif
- return true;
+ #ifdef USERMOD_FOUR_LINE_DISPLAY
+ if (display != nullptr) {
+ if (display->wakeDisplay()) {
+ // Throw away wake up input
+ display->redraw(true);
+ return false;
+ }
+ display->overlay(stateName, 750, glyph);
+ display->setMarkLine(markedLine, markedCol);
+ }
+ #endif
+ return true;
}
void lampUdated() {
//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);
}
void changeBrightness(bool increase) {
- #ifdef USERMOD_FOUR_LINE_DISPLAY
- if (display && display->wakeDisplay()) {
- // Throw away wake up input
- return;
- }
- #endif
- if (increase) bri = (bri + fadeAmount <= 255) ? (bri + fadeAmount) : 255;
- else bri = (bri - fadeAmount >= 0) ? (bri - fadeAmount) : 0;
- lampUdated();
- #ifdef USERMOD_FOUR_LINE_DISPLAY
- display->updateBrightness();
- #endif
+ #ifdef USERMOD_FOUR_LINE_DISPLAY
+ if (display && display->wakeDisplay()) {
+ display->redraw(true);
+ // Throw away wake up input
+ return;
+ }
+ display->updateRedrawTime();
+ #endif
+ bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0);
+ lampUdated();
+ #ifdef USERMOD_FOUR_LINE_DISPLAY
+ display->updateBrightness();
+ #endif
}
void changeEffect(bool increase) {
- #ifdef USERMOD_FOUR_LINE_DISPLAY
- if (display && display->wakeDisplay()) {
- // Throw away wake up input
- return;
- }
- #endif
- if (increase) effectCurrentIndex = (effectCurrentIndex + 1 >= strip.getModeCount()) ? 0 : (effectCurrentIndex + 1);
- else effectCurrentIndex = (effectCurrentIndex - 1 < 0) ? (strip.getModeCount() - 1) : (effectCurrentIndex - 1);
- effectCurrent = modes_alpha_indexes[effectCurrentIndex];
- lampUdated();
- #ifdef USERMOD_FOUR_LINE_DISPLAY
- display->showCurrentEffectOrPalette(effectCurrent, JSON_mode_names, 3);
- #endif
+ #ifdef USERMOD_FOUR_LINE_DISPLAY
+ if (display && display->wakeDisplay()) {
+ display->redraw(true);
+ // 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; ishowCurrentEffectOrPalette(effectCurrent, JSON_mode_names, 3);
+ #endif
}
void changeEffectSpeed(bool increase) {
- #ifdef USERMOD_FOUR_LINE_DISPLAY
- if (display && display->wakeDisplay()) {
- // Throw away wake up input
- return;
- }
- #endif
- if (increase) effectSpeed = (effectSpeed + fadeAmount <= 255) ? (effectSpeed + fadeAmount) : 255;
- else effectSpeed = (effectSpeed - fadeAmount >= 0) ? (effectSpeed - fadeAmount) : 0;
- lampUdated();
- #ifdef USERMOD_FOUR_LINE_DISPLAY
- display->updateSpeed();
- #endif
+ #ifdef USERMOD_FOUR_LINE_DISPLAY
+ if (display && display->wakeDisplay()) {
+ display->redraw(true);
+ // 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; iupdateSpeed();
+ #endif
}
void changeEffectIntensity(bool increase) {
- #ifdef USERMOD_FOUR_LINE_DISPLAY
- if (display && display->wakeDisplay()) {
- // Throw away wake up input
- return;
- }
- #endif
- if (increase) effectIntensity = (effectIntensity + fadeAmount <= 255) ? (effectIntensity + fadeAmount) : 255;
- else effectIntensity = (effectIntensity - fadeAmount >= 0) ? (effectIntensity - fadeAmount) : 0;
- lampUdated();
- #ifdef USERMOD_FOUR_LINE_DISPLAY
- display->updateIntensity();
- #endif
+ #ifdef USERMOD_FOUR_LINE_DISPLAY
+ if (display && display->wakeDisplay()) {
+ display->redraw(true);
+ // 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; iupdateIntensity();
+ #endif
}
void changePalette(bool increase) {
- #ifdef USERMOD_FOUR_LINE_DISPLAY
- if (display && display->wakeDisplay()) {
- // Throw away wake up input
- return;
- }
- #endif
- if (increase) effectPaletteIndex = (effectPaletteIndex + 1 >= strip.getPaletteCount()) ? 0 : (effectPaletteIndex + 1);
- else effectPaletteIndex = (effectPaletteIndex - 1 < 0) ? (strip.getPaletteCount() - 1) : (effectPaletteIndex - 1);
- effectPalette = palettes_alpha_indexes[effectPaletteIndex];
- lampUdated();
- #ifdef USERMOD_FOUR_LINE_DISPLAY
- display->showCurrentEffectOrPalette(effectPalette, JSON_palette_names, 2);
- #endif
+ #ifdef USERMOD_FOUR_LINE_DISPLAY
+ if (display && display->wakeDisplay()) {
+ display->redraw(true);
+ // 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; ishowCurrentEffectOrPalette(effectPalette, JSON_palette_names, 2);
+ #endif
}
void changeHue(bool increase){
- #ifdef USERMOD_FOUR_LINE_DISPLAY
- if (display && display->wakeDisplay()) {
- // Throw away wake up input
- return;
- }
- #endif
-
- if(increase) currentHue1 += 321;
- else currentHue1 -= 321;
- colorHStoRGB(currentHue1, currentSat1, col);
- lampUdated();
- #ifdef USERMOD_FOUR_LINE_DISPLAY
- display->updateRedrawTime();
- #endif
+ #ifdef USERMOD_FOUR_LINE_DISPLAY
+ if (display && display->wakeDisplay()) {
+ display->redraw(true);
+ // Throw away wake up input
+ return;
+ }
+ display->updateRedrawTime();
+ #endif
+ currentHue1 = max(min((increase ? currentHue1+fadeAmount : currentHue1-fadeAmount), 255), 0);
+ colorHStoRGB(currentHue1*256, currentSat1, col);
+ colorChanged = true;
+ if (applyToAll) {
+ for (byte i=0; iwakeDisplay()) {
- // Throw away wake up input
- return;
- }
- #endif
+ #ifdef USERMOD_FOUR_LINE_DISPLAY
+ if (display && display->wakeDisplay()) {
+ display->redraw(true);
+ // Throw away wake up input
+ return;
+ }
+ display->updateRedrawTime();
+ #endif
+ currentSat1 = max(min((increase ? currentSat1+fadeAmount : currentSat1-fadeAmount), 255), 0);
+ colorHStoRGB(currentHue1*256, currentSat1, col);
+ if (applyToAll) {
+ for (byte i=0; i= 0 ? (currentSat1 - 5) : 0);
- colorHStoRGB(currentHue1, currentSat1, col);
- lampUdated();
- #ifdef USERMOD_FOUR_LINE_DISPLAY
- display->updateRedrawTime();
- #endif
+ void changePreset(bool increase) {
+ #ifdef USERMOD_FOUR_LINE_DISPLAY
+ if (display && display->wakeDisplay()) {
+ display->redraw(true);
+ // Throw away wake up input
+ return;
+ }
+ display->updateRedrawTime();
+ #endif
+ if (presetHigh && presetLow && presetHigh > presetLow) {
+ String apireq = F("win&PL=~");
+ if (!increase) apireq += '-';
+ apireq += F("&P1=");
+ apireq += presetLow;
+ apireq += F("&P2=");
+ apireq += presetHigh;
+ 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