diff --git a/CHANGELOG.md b/CHANGELOG.md index 594fa97e14..e2900d9f52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,28 @@ ## WLED changelog -#### Build 2306020 +#### Build 2306180 + +- Added client-side option for applying effect defaults from metadata +- Improved ESP8266 stability by reducing WebSocket response resends +- Updated ESP8266 core to 3.1.2 + +#### Build 2306141 +- Lissajous improvements +- Scrolling Text improvements (leading 0) + +#### Build 2306140 +- Add settings PIN (un)locking to JSON post API + +#### Build 2306130 +- Bumped version to 0.14-b3 (beta 3) +- added pin dropdowns in LED preferences (not for LED pins) and usermods +- introduced (unused ATM) NeoGammaWLEDMethod class +- Reverse proxy support +- PCF8754 support for Rotary encoder (requires wiring INT pin to ESP GPIO) +- Rely on global I2C pins for usermods (breaking change) +- various fixes and enhancements +#### Build 2306020 - Support for segment sets (PR #3171) - Reduce sound simulation modes to 2 to facilitiate segment sets - Trigger button immediately on press if all configured presets are the same (PR #3226) diff --git a/platformio.ini b/platformio.ini index 6702b8de2f..f75fe8a3d7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -39,6 +39,8 @@ ; default_envs = esp32dev_qio80 ; default_envs = esp32_eth_ota1mapp ; default_envs = esp32s2_saola +; default_envs = esp32c3dev +; default_envs = lolin_s2_mini ; MoonModules entries ; =================== @@ -99,13 +101,14 @@ arduino_core_2_7_4 = espressif8266@2.6.2 arduino_core_3_0_0 = espressif8266@3.0.0 arduino_core_3_2_0 = espressif8266@3.2.0 arduino_core_4_1_0 = espressif8266@4.1.0 +arduino_core_3_1_2 = espressif8266@4.2.0 # Development platforms arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/stage # Platform to use for ESP8266 -platform_wled_default = ${common.arduino_core_4_1_0} +platform_wled_default = ${common.arduino_core_3_1_2} # We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization #platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7 platform_packages = platformio/framework-arduinoespressif8266 @@ -547,8 +550,8 @@ board = esp32-c3-devkitm-1 board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3 -D WLED_WATCHDOG_TIMEOUT=0 - ; -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB - -DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip + -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB + ;-DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip upload_speed = 460800 build_unflags = ${common.build_unflags} lib_deps = ${esp32c3.lib_deps} @@ -660,10 +663,10 @@ platform = ${esp32s2.platform} platform_packages = ${esp32s2.platform_packages} board = lolin_s2_mini board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv -build_unflags = ${common.build_unflags} -DARDUINO_USB_CDC_ON_BOOT=1 +build_unflags = ${common.build_unflags} #-DARDUINO_USB_CDC_ON_BOOT=1 build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=LolinS2 -DBOARD_HAS_PSRAM - -DARDUINO_USB_CDC_ON_BOOT=0 + -DARDUINO_USB_CDC_ON_BOOT=1 # try disabling and enabling unflag above in case of board-specific issues, will disable Serial -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 -DLOLIN_WIFI_FIX ; seems to work much better with this diff --git a/tools/cdata.js b/tools/cdata.js index 8959a588f6..1df3d60bbc 100644 --- a/tools/cdata.js +++ b/tools/cdata.js @@ -223,6 +223,7 @@ writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h", 'index'); writeHtmlGzipped("wled00/data/simple.htm", "wled00/html_simple.h", 'simple'); writeHtmlGzipped("wled00/data/pixart/pixart.htm", "wled00/html_pixart.h", 'pixart'); writeHtmlGzipped("wled00/data/cpal/cpal.htm", "wled00/html_cpal.h", 'cpal'); +writeHtmlGzipped("wled00/data/pxmagic/pxmagic.htm", "wled00/html_pxmagic.h", 'pxmagic'); /* writeChunks( "wled00/data", @@ -390,12 +391,6 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()====="; method: "gzip", filter: "html-minify", }, - { - file: "liveviewws.htm", - name: "PAGE_liveviewws", - method: "gzip", - filter: "html-minify", - }, { file: "liveviewws2D.htm", name: "PAGE_liveviewws2D", diff --git a/usermods/BH1750_v2/usermod_bh1750.h b/usermods/BH1750_v2/usermod_bh1750.h index 0cb4bf1890..0d7d7dc198 100644 --- a/usermods/BH1750_v2/usermod_bh1750.h +++ b/usermods/BH1750_v2/usermod_bh1750.h @@ -229,7 +229,9 @@ class Usermod_BH1750 : public Usermod user = root.createNestedObject(F("u")); JsonArray lux_json = user.createNestedArray(F("Luminance")); - if (!sensorFound) { + if (!enabled) { + lux_json.add(F("disabled")); + } else if (!sensorFound) { // if no sensor lux_json.add(F("BH1750 ")); lux_json.add(F("Not Found")); @@ -303,8 +305,6 @@ class Usermod_BH1750 : public Usermod DEBUG_PRINT(FPSTR(_name)); if (!initDone) { - // first run: reading from cfg.json - for (byte i=0; i<2; i++) ioPin[i] = newPin[i]; DEBUG_PRINTLN(F(" config loaded.")); } else { DEBUG_PRINTLN(F(" config (re)loaded.")); diff --git a/usermods/BME280_v2/usermod_bme280.h b/usermods/BME280_v2/usermod_bme280.h index 3b9c80ec20..8cb8ad2e4c 100644 --- a/usermods/BME280_v2/usermod_bme280.h +++ b/usermods/BME280_v2/usermod_bme280.h @@ -9,7 +9,6 @@ #include "wled.h" #include -#include #include // BME280 sensor #include // BME280 extended measurements @@ -34,7 +33,6 @@ class UsermodBME280 : public Usermod #ifdef ESP8266 //uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8 #endif - int8_t ioPin[2] = {i2c_scl, i2c_sda}; // I2C pins: SCL, SDA...defaults to Arch hardware pins but overridden at setup() bool initDone = false; // BME280 sensor settings @@ -442,8 +440,6 @@ class UsermodBME280 : public Usermod // default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor // setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed) - int8_t newPin[2]; for (byte i=0; i<2; i++) newPin[i] = ioPin[i]; // prepare to note changed pins - JsonObject top = root[FPSTR(_name)]; if (top.isNull()) { DEBUG_PRINT(F(_name)); @@ -462,27 +458,14 @@ class UsermodBME280 : public Usermod configComplete &= getJsonValue(top[F("PublishAlways")], PublishAlways, false); configComplete &= getJsonValue(top[F("UseCelsius")], UseCelsius, true); configComplete &= getJsonValue(top[F("HomeAssistantDiscovery")], HomeAssistantDiscovery, false); - for (byte i=0; i<2; i++) configComplete &= getJsonValue(top[F("pin")][i], newPin[i], ioPin[i]); DEBUG_PRINT(FPSTR(_name)); if (!initDone) { // first run: reading from cfg.json - for (byte i=0; i<2; i++) ioPin[i] = newPin[i]; DEBUG_PRINTLN(F(" config loaded.")); } else { DEBUG_PRINTLN(F(" config (re)loaded.")); // changing parameters from settings page - bool pinsChanged = false; - for (byte i=0; i<2; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; } // check if any pins changed - if (pinsChanged) { //if pins changed, deallocate old pins and allocate new ones - PinOwner po = PinOwner::UM_BME280; - if (ioPin[0]==i2c_scl && ioPin[1]==i2c_sda) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins - pinManager.deallocateMultiplePins((const uint8_t *)ioPin, 2, po); // deallocate pins - for (byte i=0; i<2; i++) ioPin[i] = newPin[i]; - setup(); - } - // use "return !top["newestParameter"].isNull();" when updating Usermod with new features - return !top[F("pin")].isNull(); } return configComplete; diff --git a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h index 288edb32d9..c32dc45a68 100644 --- a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h +++ b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h @@ -16,7 +16,7 @@ * This usermod handles PIR sensor states. * The strip will be switched on and the off timer will be resetted when the sensor goes HIGH. * When the sensor state goes LOW, the off timer is started and when it expires, the strip is switched off. - * + * Maintained by: @blazoncek * * Usermods allow you to add own functionality to WLED more easily * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality @@ -34,21 +34,21 @@ class PIRsensorSwitch : public Usermod ~PIRsensorSwitch() {} //Enable/Disable the PIR sensor - void EnablePIRsensor(bool en) { enabled = en; } + inline void EnablePIRsensor(bool en) { enabled = en; } // Get PIR sensor enabled/disabled state - bool PIRsensorEnabled() { return enabled; } + inline bool PIRsensorEnabled() { return enabled; } private: byte prevPreset = 0; byte prevPlaylist = 0; - uint32_t offTimerStart = 0; // off timer start time + volatile unsigned long offTimerStart = 0; // off timer start time + volatile bool PIRtriggered = false; // did PIR trigger? byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // notification mode for stateUpdated(): 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 @@ -62,6 +62,7 @@ class PIRsensorSwitch : public Usermod // 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 m_offMode = offMode; + bool m_override = false; // Home Assistant bool HomeAssistantDiscovery = false; // is HA discovery turned on @@ -77,173 +78,33 @@ class PIRsensorSwitch : public Usermod static const char _offOnly[]; static const char _haDiscovery[]; static const char _notify[]; + static const char _override[]; /** * check if it is daytime * if sunrise/sunset is not defined (no NTP or lat/lon) default to nighttime */ - bool isDayTime() { - updateLocalTime(); - uint8_t hr = hour(localTime); - uint8_t mi = minute(localTime); - - if (sunrise && sunset) { - if (hour(sunrise)
hr) { - return true; - } else { - if (hour(sunrise)==hr && minute(sunrise)mi) { - return true; - } - } - } - return false; - } + static bool isDayTime(); /** * switch strip on/off */ - void switchStrip(bool switchOn) - { - if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return; //if lights on and off only, do nothing - if (PIRtriggered && switchOn) return; //if already on and triggered before, do nothing - PIRtriggered = switchOn; - DEBUG_PRINT(F("PIR: strip=")); DEBUG_PRINTLN(switchOn?"on":"off"); - if (switchOn) { - if (m_onPreset) { - if (currentPlaylist>0 && !offMode) { - prevPlaylist = currentPlaylist; - unloadPlaylist(); - } else if (currentPreset>0 && !offMode) { - prevPreset = currentPreset; - } else { - saveTemporaryPreset(); - prevPlaylist = 0; - prevPreset = 255; - } - applyPreset(m_onPreset, NotifyUpdateMode); - return; - } - // preset not assigned - if (bri == 0) { - bri = briLast; - stateUpdated(NotifyUpdateMode); - } - } else { - if (m_offPreset) { - applyPreset(m_offPreset, NotifyUpdateMode); - return; - } else if (prevPlaylist) { - if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(prevPlaylist, NotifyUpdateMode); - prevPlaylist = 0; - return; - } else if (prevPreset) { - if (prevPreset<255) { if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(prevPreset, NotifyUpdateMode); } - else { if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyTemporaryPreset(); } - prevPreset = 0; - return; - } - // preset not assigned - if (bri != 0) { - briLast = bri; - bri = 0; - stateUpdated(NotifyUpdateMode); - } - } - } - - void publishMqtt(const char* state) - { - #ifndef WLED_DISABLE_MQTT - //Check if MQTT Connected, otherwise it will crash the 8266 - if (WLED_MQTT_CONNECTED) { - char subuf[64]; - strcpy(subuf, mqttDeviceTopic); - strcat_P(subuf, PSTR("/motion")); - mqtt->publish(subuf, 0, false, state); - } - #endif - } + void switchStrip(bool switchOn); + void publishMqtt(const char* state); // Create an MQTT Binary Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop. - void publishHomeAssistantAutodiscovery() - { - #ifndef WLED_DISABLE_MQTT - if (WLED_MQTT_CONNECTED) { - StaticJsonDocument<600> doc; - char uid[24], json_str[1024], buf[128]; - - sprintf_P(buf, PSTR("%s Motion"), serverDescription); //max length: 33 + 7 = 40 - doc[F("name")] = buf; - sprintf_P(buf, PSTR("%s/motion"), mqttDeviceTopic); //max length: 33 + 7 = 40 - doc[F("stat_t")] = buf; - doc[F("pl_on")] = "on"; - doc[F("pl_off")] = "off"; - sprintf_P(uid, PSTR("%s_motion"), escapedMac.c_str()); - doc[F("uniq_id")] = uid; - doc[F("dev_cla")] = F("motion"); - doc[F("exp_aft")] = 1800; - - JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device - device[F("name")] = serverDescription; - device[F("ids")] = String(F("wled-sensor-")) + mqttClientID; - device[F("mf")] = "WLED"; - device[F("mdl")] = F("FOSS"); - device[F("sw")] = versionString; - - sprintf_P(buf, PSTR("homeassistant/binary_sensor/%s/config"), uid); - DEBUG_PRINTLN(buf); - size_t payload_size = serializeJson(doc, json_str); - DEBUG_PRINTLN(json_str); - - mqtt->publish(buf, 0, true, json_str, payload_size); // do we really need to retain? - } - #endif - } + void publishHomeAssistantAutodiscovery(); /** * Read and update PIR sensor state. * Initilize/reset switch off timer */ - bool updatePIRsensorState() - { - bool pinState = digitalRead(PIRsensorPin); - if (pinState != sensorPinState) { - sensorPinState = pinState; // change previous state - - if (sensorPinState == HIGH) { - offTimerStart = 0; - if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true); - else if (NotifyUpdateMode != CALL_MODE_NO_NOTIFY) updateInterfaces(CALL_MODE_WS_SEND); - publishMqtt("on"); - } else { - // start switch off timer - offTimerStart = millis(); - if (NotifyUpdateMode != CALL_MODE_NO_NOTIFY) updateInterfaces(CALL_MODE_WS_SEND); - } - return true; - } - return false; - } + bool updatePIRsensorState(); /** * switch off the strip if the delay has elapsed */ - bool handleOffTimer() - { - if (offTimerStart > 0 && millis() - offTimerStart > m_switchOffDelay) { - offTimerStart = 0; - if (enabled == true) { - if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()) || PIRtriggered)) switchStrip(false); - else if (NotifyUpdateMode != CALL_MODE_NO_NOTIFY) updateInterfaces(CALL_MODE_WS_SEND); - publishMqtt("off"); - } - return true; - } - return false; - } + bool handleOffTimer(); public: //Functions called by WLED @@ -252,180 +113,52 @@ class PIRsensorSwitch : public Usermod * 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() - { - if (enabled) { - // pin retrieved from cfg.json (readFromConfig()) prior to running setup() - if (PIRsensorPin >= 0 && pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) { - // PIR Sensor mode INPUT_PULLUP - pinMode(PIRsensorPin, INPUT_PULLUP); - sensorPinState = digitalRead(PIRsensorPin); - } else { - if (PIRsensorPin >= 0) { - DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed.")); - } - PIRsensorPin = -1; // allocation failed - enabled = false; - } - } - initDone = true; - } + void setup(); /** * connected() is called every time the WiFi is (re)connected * Use it to initialize network interfaces */ - void connected() - { - } + //void connected(); /** * onMqttConnect() is called when MQTT connection is established */ - void onMqttConnect(bool sessionPresent) { - if (HomeAssistantDiscovery) { - publishHomeAssistantAutodiscovery(); - } - } + void onMqttConnect(bool sessionPresent); /** * loop() is called continuously. Here you can check for events, read sensors, etc. */ - void loop() - { - // only check sensors 4x/s - if (!enabled || millis() - lastLoop < 250 || strip.isUpdating()) return; - lastLoop = millis(); - - if (!updatePIRsensorState()) { - handleOffTimer(); - } - } + void loop(); /** * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. * * Add PIR sensor state and switch off timer duration to jsoninfo */ - void addToJsonInfo(JsonObject &root) - { - JsonObject user = root["u"]; - if (user.isNull()) user = root.createNestedObject("u"); - - JsonArray infoArr = user.createNestedArray(FPSTR(_name)); - - String uiDomString; - if (enabled) { - if (offTimerStart > 0) - { - uiDomString = ""; - unsigned int offSeconds = (m_switchOffDelay - (millis() - offTimerStart)) / 1000; - if (offSeconds >= 3600) - { - uiDomString += (offSeconds / 3600); - uiDomString += F("h "); - offSeconds %= 3600; - } - if (offSeconds >= 60) - { - uiDomString += (offSeconds / 60); - offSeconds %= 60; - } - else if (uiDomString.length() > 0) - { - uiDomString += 0; - } - if (uiDomString.length() > 0) - { - uiDomString += F("min "); - } - uiDomString += (offSeconds); - infoArr.add(uiDomString + F("s")); - } else { - infoArr.add(sensorPinState ? F("sensor on") : F("inactive")); - } - } else { - infoArr.add(F("disabled")); - } - - uiDomString = F(" "); - infoArr.add(uiDomString); - - JsonObject sensor = root[F("sensor")]; - if (sensor.isNull()) sensor = root.createNestedObject(F("sensor")); - sensor[F("motion")] = sensorPinState || offTimerStart>0 ? true : false; - } + void addToJsonInfo(JsonObject &root); /** * onStateChanged() is used to detect WLED state change */ - void onStateChange(uint8_t mode) { - if (!initDone) return; - DEBUG_PRINT(F("PIR: offTimerStart=")); DEBUG_PRINTLN(offTimerStart); - if (PIRtriggered && offTimerStart) { - // checking PIRtriggered and offTimerStart will prevent cancellation upon On trigger - DEBUG_PRINTLN(F("PIR: Canceled.")); - offTimerStart = 0; - PIRtriggered = false; - } - } + void onStateChange(uint8_t mode); /** * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). * Values in the state object may be modified by connected clients */ -/* - void addToJsonState(JsonObject &root) - { - } -*/ + //void addToJsonState(JsonObject &root); /** * 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(); - } - } - } - + void readFromJsonState(JsonObject &root); /** * provide the changeable values */ - void addToConfig(JsonObject &root) - { - JsonObject top = root.createNestedObject(FPSTR(_name)); - 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[FPSTR(_haDiscovery)] = HomeAssistantDiscovery; - top[FPSTR(_notify)] = (NotifyUpdateMode != CALL_MODE_NO_NOTIFY); - DEBUG_PRINTLN(F("PIR config saved.")); - } + void addToConfig(JsonObject &root); void appendConfigData() { @@ -441,72 +174,13 @@ class PIRsensorSwitch : public Usermod * * The function should return true if configuration was successfully loaded or false if there was no configuration. */ - bool readFromConfig(JsonObject &root) - { - bool oldEnabled = enabled; - int8_t oldPin = PIRsensorPin; - - DEBUG_PRINT(FPSTR(_name)); - JsonObject top = root[FPSTR(_name)]; - if (top.isNull()) { - DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); - return false; - } - - PIRsensorPin = top["pin"] | PIRsensorPin; - - enabled = top[FPSTR(_enabled)] | enabled; - - m_switchOffDelay = (top[FPSTR(_switchOffDelay)] | m_switchOffDelay/1000) * 1000; - - 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)); - - m_nightTimeOnly = top[FPSTR(_nightTime)] | m_nightTimeOnly; - m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly; - m_offOnly = top[FPSTR(_offOnly)] | m_offOnly; - HomeAssistantDiscovery = top[FPSTR(_haDiscovery)] | HomeAssistantDiscovery; - - NotifyUpdateMode = top[FPSTR(_notify)] ? CALL_MODE_DIRECT_CHANGE : CALL_MODE_NO_NOTIFY; - - if (!initDone) { - // reading config prior to setup() - DEBUG_PRINTLN(F(" config loaded.")); - } else { - if (oldPin != PIRsensorPin || oldEnabled != enabled) { - // check if pin is OK - if (oldPin != PIRsensorPin && oldPin >= 0) { - // if we are changing pin in settings page - // deallocate old pin - pinManager.deallocatePin(oldPin, PinOwner::UM_PIR); - if (pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) { - pinMode(PIRsensorPin, INPUT_PULLUP); - } else { - // allocation failed - PIRsensorPin = -1; - enabled = false; - } - } - if (enabled) { - sensorPinState = digitalRead(PIRsensorPin); - } - } - DEBUG_PRINTLN(F(" config (re)loaded.")); - } - // use "return !top["newestParameter"].isNull();" when updating Usermod with new features - return !top[FPSTR(_haDiscovery)].isNull(); - } + bool readFromConfig(JsonObject &root); /** * 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. */ - uint16_t getId() - { - return USERMOD_ID_PIRSWITCH; - } + uint16_t getId() { return USERMOD_ID_PIRSWITCH; } }; // strings to reduce flash memory usage (used more than twice) @@ -520,3 +194,359 @@ const char PIRsensorSwitch::_mqttOnly[] PROGMEM = "mqtt-only"; const char PIRsensorSwitch::_offOnly[] PROGMEM = "off-only"; const char PIRsensorSwitch::_haDiscovery[] PROGMEM = "HA-discovery"; const char PIRsensorSwitch::_notify[] PROGMEM = "notifications"; +const char PIRsensorSwitch::_override[] PROGMEM = "override"; + +bool PIRsensorSwitch::isDayTime() { + updateLocalTime(); + uint8_t hr = hour(localTime); + uint8_t mi = minute(localTime); + + if (sunrise && sunset) { + if (hour(sunrise)
hr) { + return true; + } else { + if (hour(sunrise)==hr && minute(sunrise)mi) { + return true; + } + } + } + return false; +} + +void PIRsensorSwitch::switchStrip(bool switchOn) +{ + if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return; //if lights on and off only, do nothing + if (PIRtriggered && switchOn) return; //if already on and triggered before, do nothing + PIRtriggered = switchOn; + DEBUG_PRINT(F("PIR: strip=")); DEBUG_PRINTLN(switchOn?"on":"off"); + if (switchOn) { + if (m_onPreset) { + if (currentPlaylist>0 && !offMode) { + prevPlaylist = currentPlaylist; + unloadPlaylist(); + } else if (currentPreset>0 && !offMode) { + prevPreset = currentPreset; + } else { + saveTemporaryPreset(); + prevPlaylist = 0; + prevPreset = 255; + } + applyPreset(m_onPreset, NotifyUpdateMode); + return; + } + // preset not assigned + if (bri == 0) { + bri = briLast; + stateUpdated(NotifyUpdateMode); + } + } else { + if (m_offPreset) { + applyPreset(m_offPreset, NotifyUpdateMode); + return; + } else if (prevPlaylist) { + if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(prevPlaylist, NotifyUpdateMode); + prevPlaylist = 0; + return; + } else if (prevPreset) { + if (prevPreset<255) { if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(prevPreset, NotifyUpdateMode); } + else { if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyTemporaryPreset(); } + prevPreset = 0; + return; + } + // preset not assigned + if (bri != 0) { + briLast = bri; + bri = 0; + stateUpdated(NotifyUpdateMode); + } + } +} + +void PIRsensorSwitch::publishMqtt(const char* state) +{ +#ifndef WLED_DISABLE_MQTT + //Check if MQTT Connected, otherwise it will crash the 8266 + if (WLED_MQTT_CONNECTED) { + char buf[64]; + sprintf_P(buf, PSTR("%s/motion"), mqttDeviceTopic); //max length: 33 + 7 = 40 + mqtt->publish(buf, 0, false, state); + } +#endif +} + +void PIRsensorSwitch::publishHomeAssistantAutodiscovery() +{ +#ifndef WLED_DISABLE_MQTT + if (WLED_MQTT_CONNECTED) { + StaticJsonDocument<600> doc; + char uid[24], json_str[1024], buf[128]; + + sprintf_P(buf, PSTR("%s Motion"), serverDescription); //max length: 33 + 7 = 40 + doc[F("name")] = buf; + sprintf_P(buf, PSTR("%s/motion"), mqttDeviceTopic); //max length: 33 + 7 = 40 + doc[F("stat_t")] = buf; + doc[F("pl_on")] = "on"; + doc[F("pl_off")] = "off"; + sprintf_P(uid, PSTR("%s_motion"), escapedMac.c_str()); + doc[F("uniq_id")] = uid; + doc[F("dev_cla")] = F("motion"); + doc[F("exp_aft")] = 1800; + + JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device + device[F("name")] = serverDescription; + device[F("ids")] = String(F("wled-sensor-")) + mqttClientID; + device[F("mf")] = "WLED"; + device[F("mdl")] = F("FOSS"); + device[F("sw")] = versionString; + + sprintf_P(buf, PSTR("homeassistant/binary_sensor/%s/config"), uid); + DEBUG_PRINTLN(buf); + size_t payload_size = serializeJson(doc, json_str); + DEBUG_PRINTLN(json_str); + + mqtt->publish(buf, 0, true, json_str, payload_size); // do we really need to retain? + } +#endif +} + +bool PIRsensorSwitch::updatePIRsensorState() +{ + bool pinState = digitalRead(PIRsensorPin); + if (pinState != sensorPinState) { + sensorPinState = pinState; // change previous state + + if (sensorPinState == HIGH) { + offTimerStart = 0; + if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true); + else if (NotifyUpdateMode != CALL_MODE_NO_NOTIFY) updateInterfaces(CALL_MODE_WS_SEND); + publishMqtt("on"); + } else { + // start switch off timer + offTimerStart = millis(); + if (NotifyUpdateMode != CALL_MODE_NO_NOTIFY) updateInterfaces(CALL_MODE_WS_SEND); + } + return true; + } + return false; +} + +bool PIRsensorSwitch::handleOffTimer() +{ + if (offTimerStart > 0 && millis() - offTimerStart > m_switchOffDelay) { + offTimerStart = 0; + if (enabled == true) { + if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()) || PIRtriggered)) switchStrip(false); + else if (NotifyUpdateMode != CALL_MODE_NO_NOTIFY) updateInterfaces(CALL_MODE_WS_SEND); + publishMqtt("off"); + } + return true; + } + return false; +} + +//Functions called by WLED + +void PIRsensorSwitch::setup() +{ + if (enabled) { + // pin retrieved from cfg.json (readFromConfig()) prior to running setup() + if (PIRsensorPin >= 0 && pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) { + // PIR Sensor mode INPUT_PULLUP + pinMode(PIRsensorPin, INPUT_PULLUP); + sensorPinState = digitalRead(PIRsensorPin); + } else { + if (PIRsensorPin >= 0) { + DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed.")); + } + PIRsensorPin = -1; // allocation failed + enabled = false; + } + } + initDone = true; +} + +void PIRsensorSwitch::onMqttConnect(bool sessionPresent) +{ + if (HomeAssistantDiscovery) { + publishHomeAssistantAutodiscovery(); + } +} + +void PIRsensorSwitch::loop() +{ + // only check sensors 4x/s + if (!enabled || millis() - lastLoop < 250 || strip.isUpdating()) return; + lastLoop = millis(); + + if (!updatePIRsensorState()) { + handleOffTimer(); + } +} + +void PIRsensorSwitch::addToJsonInfo(JsonObject &root) +{ + JsonObject user = root["u"]; + if (user.isNull()) user = root.createNestedObject("u"); + + JsonArray infoArr = user.createNestedArray(FPSTR(_name)); + + String uiDomString; + if (enabled) { + if (offTimerStart > 0) + { + uiDomString = ""; + unsigned int offSeconds = (m_switchOffDelay - (millis() - offTimerStart)) / 1000; + if (offSeconds >= 3600) + { + uiDomString += (offSeconds / 3600); + uiDomString += F("h "); + offSeconds %= 3600; + } + if (offSeconds >= 60) + { + uiDomString += (offSeconds / 60); + offSeconds %= 60; + } + else if (uiDomString.length() > 0) + { + uiDomString += 0; + } + if (uiDomString.length() > 0) + { + uiDomString += F("min "); + } + uiDomString += (offSeconds); + infoArr.add(uiDomString + F("s")); + } else { + infoArr.add(sensorPinState ? F("sensor on") : F("inactive")); + } + } else { + infoArr.add(F("disabled")); + } + + uiDomString = F(" "); + infoArr.add(uiDomString); + + JsonObject sensor = root[F("sensor")]; + if (sensor.isNull()) sensor = root.createNestedObject(F("sensor")); + sensor[F("motion")] = sensorPinState || offTimerStart>0 ? true : false; +} + +void PIRsensorSwitch::onStateChange(uint8_t mode) { + if (!initDone) return; + DEBUG_PRINT(F("PIR: offTimerStart=")); DEBUG_PRINTLN(offTimerStart); + if (m_override && PIRtriggered && offTimerStart) { // debounce + // checking PIRtriggered and offTimerStart will prevent cancellation upon On trigger + DEBUG_PRINTLN(F("PIR: Canceled.")); + offTimerStart = 0; + PIRtriggered = false; + } +} + +void PIRsensorSwitch::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(); + } + } +} + +void PIRsensorSwitch::addToConfig(JsonObject &root) +{ + JsonObject top = root.createNestedObject(FPSTR(_name)); + 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[FPSTR(_override)] = m_override; + top[FPSTR(_haDiscovery)] = HomeAssistantDiscovery; + top[FPSTR(_notify)] = (NotifyUpdateMode != CALL_MODE_NO_NOTIFY); + DEBUG_PRINTLN(F("PIR config saved.")); +} + +void PIRsensorSwitch::appendConfigData() +{ + oappend(SET_F("addInfo('PIRsensorSwitch:HA-discovery',1,'HA=Home Assistant');")); // 0 is field type, 1 is actual field + oappend(SET_F("addInfo('PIRsensorSwitch:notifications',1,'Periodic WS updates');")); // 0 is field type, 1 is actual field + oappend(SET_F("addInfo('PIRsensorSwitch:override',1,'Cancel timer on change');")); // 0 is field type, 1 is actual field +} + +bool PIRsensorSwitch::readFromConfig(JsonObject &root) +{ + bool oldEnabled = enabled; + int8_t oldPin = PIRsensorPin; + + DEBUG_PRINT(FPSTR(_name)); + JsonObject top = root[FPSTR(_name)]; + if (top.isNull()) { + DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); + return false; + } + + PIRsensorPin = top["pin"] | PIRsensorPin; + + enabled = top[FPSTR(_enabled)] | enabled; + + m_switchOffDelay = (top[FPSTR(_switchOffDelay)] | m_switchOffDelay/1000) * 1000; + + 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)); + + m_nightTimeOnly = top[FPSTR(_nightTime)] | m_nightTimeOnly; + m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly; + m_offOnly = top[FPSTR(_offOnly)] | m_offOnly; + m_override = top[FPSTR(_override)] | m_override; + HomeAssistantDiscovery = top[FPSTR(_haDiscovery)] | HomeAssistantDiscovery; + + NotifyUpdateMode = top[FPSTR(_notify)] ? CALL_MODE_DIRECT_CHANGE : CALL_MODE_NO_NOTIFY; + + if (!initDone) { + // reading config prior to setup() + DEBUG_PRINTLN(F(" config loaded.")); + } else { + if (oldPin != PIRsensorPin || oldEnabled != enabled) { + // check if pin is OK + if (oldPin != PIRsensorPin && oldPin >= 0) { + // if we are changing pin in settings page + // deallocate old pin + pinManager.deallocatePin(oldPin, PinOwner::UM_PIR); + if (pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) { + pinMode(PIRsensorPin, INPUT_PULLUP); + } else { + // allocation failed + PIRsensorPin = -1; + enabled = false; + } + } + if (enabled) { + sensorPinState = digitalRead(PIRsensorPin); + } + } + DEBUG_PRINTLN(F(" config (re)loaded.")); + } + // use "return !top["newestParameter"].isNull();" when updating Usermod with new features + return !top[FPSTR(_override)].isNull(); +} diff --git a/usermods/RTC/usermod_rtc.h b/usermods/RTC/usermod_rtc.h index 08b92ef1c1..a445b1f92b 100644 --- a/usermods/RTC/usermod_rtc.h +++ b/usermods/RTC/usermod_rtc.h @@ -45,8 +45,8 @@ class RTCUsermod : public Usermod { } void loop() { - if (strip.isUpdating()) return; - if (!disabled && toki.isTick()) { + if (disabled || strip.isUpdating()) return; + if (toki.isTick()) { time_t t = toki.second(); if (abs(t - RTC.get())> RTC_DELTA) { // WLEDMM only consider time diffs > 2 seconds diff --git a/usermods/ST7789_display/ST7789_display.h b/usermods/ST7789_display/ST7789_display.h index 93700c9182..144cccbfaa 100644 --- a/usermods/ST7789_display/ST7789_display.h +++ b/usermods/ST7789_display/ST7789_display.h @@ -17,12 +17,6 @@ #ifndef TFT_HEIGHT #error Please define TFT_HEIGHT #endif - #ifndef TFT_MOSI - #error Please define TFT_MOSI - #endif - #ifndef TFT_SCLK - #error Please define TFT_SCLK - #endif #ifndef TFT_DC #error Please define TFT_DC #endif @@ -140,8 +134,14 @@ class St7789DisplayUsermod : public Usermod { */ void setup() { - PinManagerPinType pins[] = { { TFT_MOSI, true }, { TFT_MISO, false}, { TFT_SCLK, true }, { TFT_CS, true}, { TFT_DC, true}, { TFT_RST, true }, { TFT_BL, true } }; - if (!pinManager.allocateMultiplePins(pins, 7, PinOwner::UM_FourLineDisplay)) { enabled = false; return; } + PinManagerPinType spiPins[] = { { spi_mosi, true }, { spi_miso, false}, { spi_sclk, true } }; + if (!pinManager.allocateMultiplePins(spiPins, 3, PinOwner::HW_SPI)) { enabled = false; return; } + PinManagerPinType displayPins[] = { { TFT_CS, true}, { TFT_DC, true}, { TFT_RST, true }, { TFT_BL, true } }; + if (!pinManager.allocateMultiplePins(displayPins, sizeof(displayPins)/sizeof(PinManagerPinType), PinOwner::UM_FourLineDisplay)) { + pinManager.deallocateMultiplePins(spiPins, 3, PinOwner::HW_SPI); + enabled = false; + return; + } tft.init(); tft.setRotation(0); //Rotation here is set up for the text to be readable with the port on the left. Use 1 to flip. @@ -365,9 +365,6 @@ class St7789DisplayUsermod : public Usermod { { JsonObject top = root.createNestedObject("ST7789"); JsonArray pins = top.createNestedArray("pin"); - pins.add(TFT_MOSI); - pins.add(TFT_MISO); - pins.add(TFT_SCLK); pins.add(TFT_CS); pins.add(TFT_DC); pins.add(TFT_RST); @@ -376,6 +373,13 @@ class St7789DisplayUsermod : public Usermod { } + void appendConfigData() { + oappend(SET_F("addInfo('ST7789:pin[]',0,'','SPI CS');")); + oappend(SET_F("addInfo('ST7789:pin[]',1,'','SPI DC');")); + oappend(SET_F("addInfo('ST7789:pin[]',2,'','SPI RST');")); + oappend(SET_F("addInfo('ST7789:pin[]',2,'','SPI BL');")); + } + /* * readFromConfig() can be used to read back the custom settings you added with addToConfig(). * This is called by WLED when settings are loaded (currently this only happens once immediately after boot) diff --git a/usermods/Si7021_MQTT_HA/usermod_si7021_mqtt_ha.h b/usermods/Si7021_MQTT_HA/usermod_si7021_mqtt_ha.h index 4a42a7d5af..bdf7848446 100644 --- a/usermods/Si7021_MQTT_HA/usermod_si7021_mqtt_ha.h +++ b/usermods/Si7021_MQTT_HA/usermod_si7021_mqtt_ha.h @@ -13,14 +13,6 @@ Adafruit_Si7021 si7021; -#ifdef ARDUINO_ARCH_ESP32 //ESP32 boards -uint8_t SCL_PIN = 22; -uint8_t SDA_PIN = 21; -#else //ESP8266 boards -uint8_t SCL_PIN = 5; -uint8_t SDA_PIN = 4; -#endif - class Si7021_MQTT_HA : public Usermod { private: @@ -184,7 +176,6 @@ class Si7021_MQTT_HA : public Usermod { if (enabled) { Serial.println("Si7021_MQTT_HA: Starting!"); - Wire.begin(SDA_PIN, SCL_PIN); Serial.println("Si7021_MQTT_HA: Initializing sensors.. "); _initializeSensor(); } diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 86f98025c1..948bbb9d7c 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -2139,8 +2139,6 @@ class AudioReactive : public Usermod { pinArray.add(i2swsPin); pinArray.add(i2sckPin); pinArray.add(mclkPin); - pinArray.add(sdaPin); - pinArray.add(sclPin); JsonObject cfg = top.createNestedObject("config"); cfg[F("squelch")] = soundSquelch; @@ -2212,8 +2210,6 @@ class AudioReactive : public Usermod { configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][1], i2swsPin); configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][2], i2sckPin); configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][3], mclkPin); - configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][4], sdaPin); - configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][5], sclPin); configComplete &= getJsonValue(top["config"][F("squelch")], soundSquelch); configComplete &= getJsonValue(top["config"][F("gain")], sampleGain); diff --git a/usermods/audioreactive/audio_source.h b/usermods/audioreactive/audio_source.h index 044c5cde6b..953034eeaa 100644 --- a/usermods/audioreactive/audio_source.h +++ b/usermods/audioreactive/audio_source.h @@ -1,6 +1,5 @@ #pragma once -#include #include "wled.h" #include #include @@ -417,6 +416,7 @@ class I2SSource : public AudioSource { */ class ES7243 : public I2SSource { private: + //WLEDMM: // I2C initialization functions for ES7243 void _es7243I2cBegin() { bool i2c_initialized = Wire.begin(pin_ES7243_SDA, pin_ES7243_SCL, 100000U); @@ -426,12 +426,10 @@ class ES7243 : public I2SSource { } void _es7243I2cWrite(uint8_t reg, uint8_t val) { -#ifndef ES7243_ADDR - Wire.beginTransmission(0x13); - #define ES7243_ADDR 0x13 // default address -#else + #ifndef ES7243_ADDR + #define ES7243_ADDR 0x13 // default address + #endif Wire.beginTransmission(ES7243_ADDR); -#endif Wire.write((uint8_t)reg); Wire.write((uint8_t)val); uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK @@ -441,7 +439,6 @@ class ES7243 : public I2SSource { } void _es7243InitAdc() { - _es7243I2cBegin(); _es7243I2cWrite(0x00, 0x01); _es7243I2cWrite(0x06, 0x00); _es7243I2cWrite(0x05, 0x1B); @@ -456,6 +453,7 @@ class ES7243 : public I2SSource { _config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT; }; + //WLEDMM void initialize(int8_t sdaPin, int8_t sclPin, int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) { // check that pins are valid if ((sdaPin < 0) || (sclPin < 0)) { @@ -490,7 +488,6 @@ class ES7243 : public I2SSource { pinManager.deallocateMultiplePins(es7243Pins, 2, PinOwner::HW_I2C); I2SSource::deinitialize(); } - private: int8_t pin_ES7243_SDA; int8_t pin_ES7243_SCL; diff --git a/usermods/multi_relay/usermod_multi_relay.h b/usermods/multi_relay/usermod_multi_relay.h index b8cabebbc5..693f24dc30 100644 --- a/usermods/multi_relay/usermod_multi_relay.h +++ b/usermods/multi_relay/usermod_multi_relay.h @@ -5,14 +5,18 @@ #ifndef MULTI_RELAY_MAX_RELAYS #define MULTI_RELAY_MAX_RELAYS 4 #else - #if MULTI_RELAY_MAX_RELAYS>16 + #if MULTI_RELAY_MAX_RELAYS>8 #undef MULTI_RELAY_MAX_RELAYS - #define MULTI_RELAY_MAX_RELAYS 16 + #define MULTI_RELAY_MAX_RELAYS 8 + #warning Maximum relays set to 8 #endif #endif #ifndef MULTI_RELAY_PINS #define MULTI_RELAY_PINS -1 + #define MULTI_RELAY_ENABLED false +#else + #define MULTI_RELAY_ENABLED true #endif #define WLED_DEBOUNCE_THRESHOLD 50 //only consider button input of at least 50ms as valid (debouncing) @@ -20,6 +24,14 @@ #define ON true #define OFF false +#ifndef USERMOD_USE_PCF8574 + #undef USE_PCF8574 + #define USE_PCF8574 false +#else + #undef USE_PCF8574 + #define USE_PCF8574 true +#endif + #ifndef PCF8574_ADDRESS #define PCF8574_ADDRESS 0x20 // some may start at 0x38 #endif @@ -37,7 +49,7 @@ typedef struct relay_t { int8_t pin; struct { // reduces memory footprint bool active : 1; // is the relay waiting to be switched - bool mode : 1; // does On mean 1 or 0 (inverted output) + bool invert : 1; // does On mean 1 or 0 bool state : 1; // 1 relay is On, 0 relay is Off bool external : 1; // is the relay externally controlled int8_t button : 4; // which button triggers relay @@ -50,23 +62,17 @@ class MultiRelay : public Usermod { private: // array of relays - Relay _relay[MULTI_RELAY_MAX_RELAYS]; - - // switch timer start time - uint32_t _switchTimerStart = 0; - // old brightness - bool _oldMode; - - // usermod enabled - bool enabled = false; // needs to be configured (no default config) - // status of initialisation - bool initDone = false; - bool usePcf8574 = false; - uint8_t addrPcf8574 = PCF8574_ADDRESS; - bool HAautodiscovery = false; - - uint16_t periodicBroadcastSec = 60; - unsigned long lastBroadcast = 0; + Relay _relay[MULTI_RELAY_MAX_RELAYS]; + + uint32_t _switchTimerStart; // switch timer start time + bool _oldMode; // old brightness + bool enabled; // usermod enabled + bool initDone; // status of initialisation + bool usePcf8574; + uint8_t addrPcf8574; + bool HAautodiscovery; + uint16_t periodicBroadcastSec; + unsigned long lastBroadcast; // strings to reduce flash memory usage (used more than twice) static const char _name[]; @@ -103,7 +109,7 @@ class MultiRelay : public Usermod { /** * desctructor */ - ~MultiRelay() {} + //~MultiRelay() {} /** * Enable/Disable the usermod @@ -313,7 +319,7 @@ int MultiRelay::getValue(String data, char separator, int index) { //Write a byte to the IO expander byte MultiRelay::IOexpanderWrite(byte address, byte _data ) { - Wire.beginTransmission(addrPcf8574 + address); + Wire.beginTransmission(address); Wire.write(_data); return Wire.endTransmission(); } @@ -321,7 +327,7 @@ byte MultiRelay::IOexpanderWrite(byte address, byte _data ) { //Read a byte from the IO expander byte MultiRelay::IOexpanderRead(int address) { byte _data = 0; - Wire.requestFrom(addrPcf8574 + address, 1); + Wire.requestFrom(address, 1); if (Wire.available()) { _data = Wire.read(); } @@ -331,12 +337,21 @@ byte MultiRelay::IOexpanderRead(int address) { // public methods -MultiRelay::MultiRelay() { +MultiRelay::MultiRelay() + : _switchTimerStart(0) + , enabled(MULTI_RELAY_ENABLED) + , initDone(false) + , usePcf8574(USE_PCF8574) + , addrPcf8574(PCF8574_ADDRESS) + , HAautodiscovery(false) + , periodicBroadcastSec(60) + , lastBroadcast(0) +{ const int8_t defPins[] = {MULTI_RELAY_PINS}; for (size_t i=0; i=MULTI_RELAY_MAX_RELAYS || (_relay[relay].pin<0 && !usePcf8574)) return; + if (relay>=MULTI_RELAY_MAX_RELAYS || _relay[relay].pin<0) return; _relay[relay].state = mode; - if (usePcf8574) { - byte expander = relay/8; - uint16_t state = 0; - for (int i=0; i>(8*expander)); - DEBUG_PRINT(F("PCF8574 Writing to ")); DEBUG_PRINT(addrPcf8574 + expander); DEBUG_PRINT(F(" with data ")); DEBUG_PRINTLN(state>>(8*expander)); - } else { + if (usePcf8574 && _relay[relay].pin >= 100) { + // we need to send all ouputs at the same time + uint8_t state = 0; + for (int i=0; i=0) count++; return count; } @@ -465,29 +482,28 @@ void MultiRelay::publishHomeAssistantAutodiscovery() { void MultiRelay::setup() { // pins retrieved from cfg.json (readFromConfig()) prior to running setup() // if we want PCF8574 expander I2C pins need to be valid - if (i2c_sda == i2c_scl && i2c_sda == -1) usePcf8574 = false; + if (i2c_sda<0 || i2c_scl<0) usePcf8574 = false; - if (usePcf8574) { - uint16_t state = 0; - for (int i=0; i>(8*expander)); // init expander (set all outputs) - delay(1); - } - DEBUG_PRINTLN(F("PCF8574(s) inited.")); - } else { - for (int i=0; i= 100) { + uint8_t pin = _relay[i].pin - 100; + if (!_relay[i].external) _relay[i].state = !offMode; + state |= (uint8_t)(_relay[i].invert ? !_relay[i].state : _relay[i].state) << pin; + } else if (_relay[i].pin<100 && _relay[i].pin>=0) { + if (pinManager.allocatePin(_relay[i].pin,true, PinOwner::UM_MultiRelay)) { if (!_relay[i].external) _relay[i].state = !offMode; switchRelay(i, _relay[i].state); _relay[i].active = false; + } else { + _relay[i].pin = -1; // allocation failed } } } + if (usePcf8574) { + IOexpanderWrite(addrPcf8574, state); // init expander (set all outputs) + DEBUG_PRINTLN(F("PCF8574(s) inited.")); + } _oldMode = offMode; initDone = true; } @@ -508,7 +524,7 @@ void MultiRelay::loop() { _oldMode = offMode; _switchTimerStart = millis(); for (int i=0; i=0 || usePcf8574) && !_relay[i].external) _relay[i].active = true; + if ((_relay[i].pin>=0) && !_relay[i].external) _relay[i].active = true; } } @@ -624,7 +640,7 @@ void MultiRelay::addToJsonInfo(JsonObject &root) { String uiDomString; for (int i=0; i(not hex!)','address');")); oappend(SET_F("addInfo('MultiRelay:broadcast-sec',1,'(MQTT message)');")); - oappend(SET_F("addInfo('MultiRelay:relay-0:pin',1,'(use -1 for PCF8574)');")); + //oappend(SET_F("addInfo('MultiRelay:relay-0:pin',1,'(use -1 for PCF8574)');")); + oappend(SET_F("d.extra.push({'MultiRelay':{pin:[['P0',100],['P1',101],['P2',102],['P3',103],['P4',104],['P5',105],['P6',106],['P7',107]]}});")); } /** @@ -753,7 +770,7 @@ bool MultiRelay::readFromConfig(JsonObject &root) { usePcf8574 = top[FPSTR(_pcf8574)] | usePcf8574; addrPcf8574 = top[FPSTR(_pcfAddress)] | addrPcf8574; // if I2C is not globally initialised just ignore - if (i2c_sda == i2c_scl && i2c_sda == -1) usePcf8574 = false; + if (i2c_sda<0 || i2c_scl<0) usePcf8574 = false; periodicBroadcastSec = top[FPSTR(_broadcast)] | periodicBroadcastSec; periodicBroadcastSec = min(900,max(0,(int)periodicBroadcastSec)); HAautodiscovery = top[FPSTR(_HAautodiscovery)] | HAautodiscovery; @@ -762,14 +779,14 @@ bool MultiRelay::readFromConfig(JsonObject &root) { String parName = FPSTR(_relay_str); parName += '-'; parName += i; oldPin[i] = _relay[i].pin; _relay[i].pin = top[parName]["pin"] | _relay[i].pin; - _relay[i].mode = top[parName][FPSTR(_activeHigh)] | _relay[i].mode; + _relay[i].invert = top[parName][FPSTR(_activeHigh)] | _relay[i].invert; _relay[i].external = top[parName][FPSTR(_external)] | _relay[i].external; _relay[i].delay = top[parName][FPSTR(_delay_str)] | _relay[i].delay; _relay[i].button = top[parName][FPSTR(_button)] | _relay[i].button; // begin backwards compatibility (beta) remove when 0.13 is released parName += '-'; _relay[i].pin = top[parName+"pin"] | _relay[i].pin; - _relay[i].mode = top[parName+FPSTR(_activeHigh)] | _relay[i].mode; + _relay[i].invert = top[parName+FPSTR(_activeHigh)] | _relay[i].invert; _relay[i].external = top[parName+FPSTR(_external)] | _relay[i].external; _relay[i].delay = top[parName+FPSTR(_delay_str)] | _relay[i].delay; // end compatibility @@ -783,22 +800,11 @@ bool MultiRelay::readFromConfig(JsonObject &root) { } else { // deallocate all pins 1st for (int i=0; i=0) { + if (oldPin[i]>=0 && oldPin[i]<100) { pinManager.deallocatePin(oldPin[i], PinOwner::UM_MultiRelay); } // allocate new pins - for (int i=0; i=0 && pinManager.allocatePin(_relay[i].pin, true, PinOwner::UM_MultiRelay)) { - if (!_relay[i].external) { - _relay[i].state = !offMode; - switchRelay(i, _relay[i].state); - _oldMode = offMode; - } - } else { - _relay[i].pin = -1; - } - _relay[i].active = false; - } + setup(); DEBUG_PRINTLN(F(" config (re)loaded.")); } // use "return !top["newestParameter"].isNull();" when updating Usermod with new features @@ -816,4 +822,4 @@ const char MultiRelay::_button[] PROGMEM = "button"; const char MultiRelay::_broadcast[] PROGMEM = "broadcast-sec"; const char MultiRelay::_HAautodiscovery[] PROGMEM = "HA-autodiscovery"; const char MultiRelay::_pcf8574[] PROGMEM = "use-PCF8574"; -const char MultiRelay::_pcfAddress[] PROGMEM = "first-PCF8574"; +const char MultiRelay::_pcfAddress[] PROGMEM = "PCF8574-address"; diff --git a/usermods/sensors_to_mqtt/usermod_v2_SensorsToMqtt.h b/usermods/sensors_to_mqtt/usermod_v2_SensorsToMqtt.h index 972e2c866f..4f51750ac7 100644 --- a/usermods/sensors_to_mqtt/usermod_v2_SensorsToMqtt.h +++ b/usermods/sensors_to_mqtt/usermod_v2_SensorsToMqtt.h @@ -6,7 +6,6 @@ #include "wled.h" #include -#include #include #include #include @@ -16,14 +15,6 @@ Adafruit_BMP280 bmp; Adafruit_Si7021 si7021; Adafruit_CCS811 ccs811; -#ifdef ARDUINO_ARCH_ESP32 //ESP32 boards -uint8_t SCL_PIN = 22; -uint8_t SDA_PIN = 21; -#else //ESP8266 boards -uint8_t SCL_PIN = 5; -uint8_t SDA_PIN = 4; -#endif - class UserMod_SensorsToMQTT : public Usermod { private: @@ -231,7 +222,6 @@ class UserMod_SensorsToMQTT : public Usermod void setup() { Serial.println("Starting!"); - Wire.begin(SDA_PIN, SCL_PIN); Serial.println("Initializing sensors.. "); _initialize(); } diff --git a/usermods/sht/usermod_sht.h b/usermods/sht/usermod_sht.h index bf99afd21b..bb01030cbc 100644 --- a/usermods/sht/usermod_sht.h +++ b/usermods/sht/usermod_sht.h @@ -16,7 +16,6 @@ class ShtUsermod : public Usermod private: //bool enabled = false; // Is usermod enabled or not //WLEDMM use public attribute of class UserMod bool firstRunDone = false; // Remembers if the first config load run had been done - bool pinAllocDone = true; // Remembers if we have allocated pins bool initDone = false; // Remembers if the mod has been completely initialised bool haMqttDiscovery = false; // Is MQTT discovery enabled or not bool haMqttDiscoveryDone = false; // Remembers if we already published the HA discovery topics 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 051f33b525..2a20dac469 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 @@ -7,6 +7,7 @@ //#define WIRE_INTERFACES_COUNT 2 // experimental - tell U8x8Lib that there is a second Wire unit #include "wled.h" +#undef U8X8_NO_HW_I2C // borrowed from WLEDMM: we do want I2C hardware drivers - if possible #include // from https://github.com/olikraus/u8g2/ #include "4LD_wled_fonts.c" @@ -34,6 +35,11 @@ // REQUIREMENT: "lib_deps" within platformio.ini / platformio_override.ini // REQUIREMENT: olikraus/U8g2@ ^2.34.15 (the version already in platformio.ini is fine) // +// If display does not work or looks corrupted check the +// constructor reference: +// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp +// or check the gallery: +// https://github.com/olikraus/u8g2/wiki/gallery //The SCL and SDA pins are defined here. #ifndef FLD_PIN_SCL @@ -121,7 +127,7 @@ class FourLineDisplayUsermod : public Usermod { 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 + int8_t ioPin[3] = {-1, -1, -1}; // I2C pins: SCL, SDA uint32_t ioFrequency = 400000; // in Hz (minimum is 100000, baseline is 400000 and maximum should be 3400000) #else int8_t ioPin[5] = {FLD_PIN_CLOCKSPI, FLD_PIN_MOSISPI, FLD_PIN_CS, FLD_PIN_DC, FLD_PIN_RESET}; // SPI pins: CLK, MOSI, CS, DC, RST @@ -339,27 +345,19 @@ class FourLineDisplayUsermod : public Usermod { * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. * Below it is shown how this could be used for e.g. a light sensor */ - //void addToJsonInfo(JsonObject& root) { - //JsonObject user = root["u"]; - //if (user.isNull()) user = root.createNestedObject("u"); - //JsonArray data = user.createNestedArray(F("4LineDisplay")); - //data.add(F("Loaded.")); - //} + //void addToJsonInfo(JsonObject& root); /* * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). * Values in the state object may be modified by connected clients */ - //void addToJsonState(JsonObject& root) { - //} + //void addToJsonState(JsonObject& root); /* * 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() - //} + //void readFromJsonState(JsonObject& root); void appendConfigData(); diff --git a/wled00.sln b/wled00.sln deleted file mode 100644 index b2f12b8359..0000000000 --- a/wled00.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28010.2046 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wled00", "wled00\wled00.vcxproj", "{C5F80730-F44F-4478-BDAE-6634EFC2CA88}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x86 = Debug|x86 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.ActiveCfg = Debug|Win32 - {C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.Build.0 = Debug|Win32 - {C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.ActiveCfg = Release|Win32 - {C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {9A679C2B-61D3-400B-B96F-06E604E9CED2} - EndGlobalSection -EndGlobal diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 278f2913b7..44b35a5557 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5888,6 +5888,7 @@ uint16_t mode_2Dscrollingtext(void) { case 4: letterWidth = 7; letterHeight = 9; break; case 5: letterWidth = 5; letterHeight = 12; break; } + const bool zero = SEGMENT.check3; const int yoffset = map(SEGMENT.intensity, 0, 255, -rows/2, rows/2) + (rows-letterHeight)/2; char text[33] = {'\0'}; unsigned maxLen = (SEGMENT.name) ? min(32, (int)strlen(SEGMENT.name)) : 0; // WLEDMM make it robust against too long segment names @@ -5937,7 +5938,7 @@ uint16_t mode_2Dscrollingtext(void) { return FRAMETIME; } -static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,,Gradient,Overlay;!,!,Gradient;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0"; +static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text ☾@!,Y Offset,Trail,Font size,,Gradient,Overlay,0;!,!,Gradient;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0"; //////////////////////////// diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 8669644e48..f82fb28ada 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -207,7 +207,6 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM: if (ledsrgb) ledsrgb[XY(x,y)] = col; uint8_t _bri_t = currentBri(on ? opacity : 0); - if (!_bri_t && !transitional) return; if (_bri_t < 255) { byte r = scale8(R(col), _bri_t); byte g = scale8(G(col), _bri_t); diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index ea6e96f994..d769281a95 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -364,23 +364,26 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) { } void Segment::startTransition(uint16_t dur) { - if (transitional || _t) return; // already in transition no need to store anything + if (!dur) { + transitional = false; + if (_t) { + delete _t; + _t = nullptr; + } + return; + } + if (transitional && _t) return; // already in transition no need to store anything // starting a transition has to occur before change so we get current values 1st - uint8_t _briT = currentBri(on ? opacity : 0); - uint8_t _cctT = currentBri(cct, true); - CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette); - uint8_t _modeP = mode; - uint32_t _colorT[NUM_COLORS]; - for (size_t i=0; i_briT = _briT; - _t->_cctT = _cctT; + + CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette); + _t->_briT = on ? opacity : 0; + _t->_cctT = cct; _t->_palT = _palT; - _t->_modeP = _modeP; - for (size_t i=0; i_colorT[i] = _colorT[i]; + _t->_modeP = mode; + for (size_t i=0; i_colorT[i] = colors[i]; transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true); } @@ -393,10 +396,10 @@ uint16_t Segment::progress() { } uint8_t Segment::currentBri(uint8_t briNew, bool useCct) { - if (transitional && _t) { - uint32_t prog = progress() + 1; - if (useCct) return ((briNew * prog) + _t->_cctT * (0x10000 - prog)) >> 16; - else return ((briNew * prog) + _t->_briT * (0x10000 - prog)) >> 16; + uint32_t prog = progress(); + if (transitional && _t && prog < 0xFFFFU) { + if (useCct) return ((briNew * prog) + _t->_cctT * (0xFFFFU - prog)) >> 16; + else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16; } else { return briNew; } @@ -426,15 +429,14 @@ CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal void Segment::handleTransition() { if (!transitional) return; - unsigned long maxWait = millis() + 20; - if (mode == FX_MODE_STATIC && next_time > maxWait) next_time = maxWait; - if (progress() == 0xFFFFU) { - if (_t) { - if (_t->_modeP != mode) markForReset(); + uint16_t _progress = progress(); + if (_progress == 0xFFFFU) transitional = false; // finish transitioning segment + if (_t) { // thanks to @nXm AKA https://github.com/NMeirer + if (_progress >= 32767U && _t->_modeP != mode) markForReset(); + if (_progress == 0xFFFFU) { delete _t; _t = nullptr; } - transitional = false; // finish transitioning segment } } @@ -923,7 +925,6 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATT uint16_t len = length(); uint8_t _bri_t = currentBri(on ? opacity : 0); - if (!_bri_t && !transitional && fadeTransition) return; // if _bri_t == 0 && segment is not transitionig && transitions are enabled then save a few CPU cycles if (_bri_t < 255) { byte r = scale8(R(col), _bri_t); byte g = scale8(G(col), _bri_t); @@ -1569,6 +1570,8 @@ void WS2812FX::service() { _isServicing = true; _segment_index = 0; for (segment &seg : _segments) { + // process transition (mode changes in the middle of transition) + seg.handleTransition(); // reset the segment runtime data if needed seg.resetIfRequired(); @@ -1579,7 +1582,7 @@ void WS2812FX::service() { { if (seg.grouping == 0) seg.grouping = 1; //sanity check doShow = true; - uint16_t frameDelay = FRAMETIME; // WLEDMM avoid name clash with "delay" function + uint16_t delay = FRAMETIME; // WLEDMM avoid name clash with "delay" function if (!seg.freeze) { //only run effect function if not frozen _virtualSegmentLength = seg.virtualLength(); @@ -1594,14 +1597,12 @@ void WS2812FX::service() { // effect blending (execute previous effect) // actual code may be a bit more involved as effects have runtime data including allocated memory //if (seg.transitional && seg._modeP) (*_mode[seg._modeP])(progress()); - frameDelay = (*_mode[seg.currentMode(seg.mode)])(); + delay = (*_mode[seg.currentMode(seg.mode)])(); if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++; - if (seg.transitional && frameDelay > FRAMETIME) frameDelay = FRAMETIME; // force faster updates during transition - - seg.handleTransition(); + if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition } - seg.next_time = nowUp + frameDelay; + seg.next_time = nowUp + delay; } _segment_index++; } @@ -2096,7 +2097,7 @@ void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) { } void WS2812FX::setTransitionMode(bool t) { - for (segment &seg : _segments) if (!seg.transitional) seg.startTransition(t ? _transitionDur : 0); + for (segment &seg : _segments) seg.startTransition(t ? _transitionDur : 0); } #ifdef WLED_DEBUG diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h index 60949c49e4..408c052659 100644 --- a/wled00/bus_wrapper.h +++ b/wled00/bus_wrapper.h @@ -122,6 +122,9 @@ #define I_SS_LPO_3 48 +// In the following NeoGammaNullMethod can be replaced with NeoGammaWLEDMethod to perform Gamma correction implicitly +// unfortunately that may apply Gamma correction to pre-calculated palettes which is undesired + /*** ESP8266 Neopixel methods ***/ #ifdef ESP8266 //RGB diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index b570feec8c..42131d903d 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -333,7 +333,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { PinManagerPinType i2c[2] = { { i2c_sda, true }, { i2c_scl, true } }; if (i2c_scl >= 0 && i2c_sda >= 0 && pinManager.allocateMultiplePins(i2c, 2, PinOwner::HW_I2C)) { #ifdef ESP32 - Wire.setPins(i2c_sda, i2c_scl); // this will fail if Wire is initilised (Wire.begin() called prior) + if (!Wire.setPins(i2c_sda, i2c_scl)) { i2c_scl = i2c_sda = -1; } // this will fail if Wire is initilised (Wire.begin() called prior) + else Wire.begin(); + #else + Wire.begin(i2c_sda, i2c_scl); #endif // Wire.begin(); // WLEDMM moved into pinManager DEBUG_PRINTF("pinmgr success for global i2c %d %d\n", i2c_sda, i2c_scl); @@ -371,7 +374,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { if (light_gc_col > 1.0f) gammaCorrectCol = true; else gammaCorrectCol = false; if (gammaCorrectVal > 1.0f && gammaCorrectVal <= 3) { - if (gammaCorrectVal != 2.8f) calcGammaTable(gammaCorrectVal); + if (gammaCorrectVal != 2.8f) NeoGammaWLEDMethod::calcGammaTable(gammaCorrectVal); } else { gammaCorrectVal = 1.0f; // no gamma correction gammaCorrectBri = false; @@ -483,6 +486,13 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { CJSON(retainMqttMsg, if_mqtt[F("rtn")]); #endif +#ifndef WLED_DISABLE_ESPNOW + JsonObject remote = doc["remote"]; + CJSON(enable_espnow_remote, remote[F("remote_enabled")]); + getStringFromJson(linked_remote, remote[F("linked_remote")], 13); +#endif + + #ifndef WLED_DISABLE_HUESYNC JsonObject if_hue = interfaces["hue"]; CJSON(huePollingEnabled, if_hue["en"]); @@ -964,6 +974,13 @@ void serializeConfig() { if_mqtt_topics[F("group")] = mqttGroupTopic; #endif +#ifndef WLED_DISABLE_ESPNOW + JsonObject remote = doc.createNestedObject(F("remote")); + remote[F("remote_enabled")] = enable_espnow_remote; + remote[F("linked_remote")] = linked_remote; +#endif + + #ifndef WLED_DISABLE_HUESYNC JsonObject if_hue = interfaces.createNestedObject("hue"); if_hue["en"] = huePollingEnabled; diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 61457ebaeb..ca91b8e24c 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -303,7 +303,7 @@ uint16_t approximateKelvinFromRGB(uint32_t rgb) { #if !defined(WLED_USE_CIE_BRIGHTNESS_TABLE) //gamma 2.8 lookup table used for color correction -static byte gammaT[256] = { +uint8_t NeoGammaWLEDMethod::gammaT[256] = { 0, 0, 0, 0, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, @@ -346,29 +346,22 @@ static const byte gammaT[256] = { 245, 247, 250, 252, 255 }; #endif -uint8_t gamma8_cal(uint8_t b, float gamma) -{ - return (int)(powf((float)b / 255.0f, gamma) * 255.0f + 0.5f); -} - // re-calculates & fills gamma table -void calcGammaTable(float gamma) +void NeoGammaWLEDMethod::calcGammaTable(float gamma) { -#if !defined(WLED_USE_CIE_BRIGHTNESS_TABLE) // WLEDMM not possible when using the CIE table - for (uint16_t i = 0; i < 256; i++) { - gammaT[i] = gamma8_cal(i, gamma); + for (size_t i = 0; i < 256; i++) { + gammaT[i] = (int)(powf((float)i / 255.0f, gamma) * 255.0f + 0.5f); } -#endif } -// used for individual channel or brightness gamma correction -uint8_t gamma8(uint8_t b) +uint8_t NeoGammaWLEDMethod::Correct(uint8_t value) { - return gammaT[b]; + if (!gammaCorrectCol) return value; + return gammaT[value]; } // used for color gamma correction -uint32_t gamma32(uint32_t color) +uint32_t NeoGammaWLEDMethod::Correct32(uint32_t color) { if (!gammaCorrectCol) return color; uint8_t w = W(color); diff --git a/wled00/const.h b/wled00/const.h index c7ba011f6b..ef23efc599 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -91,6 +91,21 @@ #endif #endif +#ifndef WLED_MAX_SEGNAME_LEN + #ifdef ESP8266 + #define WLED_MAX_SEGNAME_LEN 32 + #else + #define WLED_MAX_SEGNAME_LEN 64 + #endif +#else + #if WLED_MAX_SEGNAME_LEN<32 + #undef WLED_MAX_SEGNAME_LEN + #define WLED_MAX_SEGNAME_LEN 32 + #else + #warning WLED UI does not support modified maximum segment name length! + #endif +#endif + //Usermod IDs #define USERMOD_ID_RESERVED 0 //Unused. Might indicate no usermod present #define USERMOD_ID_UNSPECIFIED 1 //Default value for a general user mod that does not specify a custom ID @@ -327,7 +342,8 @@ // WLED Error modes #define ERR_NONE 0 // All good :) -#define ERR_EEP_COMMIT 2 // Could not commit to EEPROM (wrong flash layout?) +#define ERR_DENIED 1 // Permission denied +#define ERR_EEP_COMMIT 2 // Could not commit to EEPROM (wrong flash layout?) OBSOLETE #define ERR_NOBUF 3 // JSON buffer was not released in time, request cannot be handled at this time #define ERR_JSON 9 // JSON parsing failed (input too large?) #define ERR_FS_BEGIN 10 // Could not init filesystem (no partition?) @@ -339,12 +355,29 @@ #define ERR_OVERCURRENT 31 // An attached current sensor has measured a current above the threshold (not implemented) #define ERR_UNDERVOLT 32 // An attached voltmeter has measured a voltage below the threshold (not implemented) -//Timer mode types +// Timer mode types #define NL_MODE_SET 0 //After nightlight time elapsed, set to target brightness #define NL_MODE_FADE 1 //Fade to target brightness gradually #define NL_MODE_COLORFADE 2 //Fade to target brightness and secondary color gradually #define NL_MODE_SUN 3 //Sunrise/sunset. Target brightness is set immediately, then Sunrise effect is started. Max 60 min. +// Settings sub page IDs +#define SUBPAGE_MENU 0 +#define SUBPAGE_WIFI 1 +#define SUBPAGE_LEDS 2 +#define SUBPAGE_UI 3 +#define SUBPAGE_SYNC 4 +#define SUBPAGE_TIME 5 +#define SUBPAGE_SEC 6 +#define SUBPAGE_DMX 7 +#define SUBPAGE_UM 8 +#define SUBPAGE_UPDATE 9 +#define SUBPAGE_2D 10 +#define SUBPAGE_LOCK 251 +#define SUBPAGE_PINREQ 252 +#define SUBPAGE_CSS 253 +#define SUBPAGE_JS 254 +#define SUBPAGE_WELCOME 255 #define NTP_PACKET_SIZE 48 @@ -457,7 +490,10 @@ #define DEFAULT_LED_COUNT 30 #endif -#define INTERFACE_UPDATE_COOLDOWN 2000 //time in ms to wait between websockets, alexa, and MQTT updates +#define INTERFACE_UPDATE_COOLDOWN 1000 // time in ms to wait between websockets, alexa, and MQTT updates + +#define PIN_RETRY_COOLDOWN 3000 // time in ms after an incorrect attempt PIN and OTA pass will be rejected even if correct +#define PIN_TIMEOUT 900000 // time in ms after which the PIN will be required again, 15 minutes // HW_PIN_SCL & HW_PIN_SDA are used for information in usermods settings page and usermods themselves // which GPIO pins are actually used in a hardwarea layout (controller board) diff --git a/wled00/data/404.htm b/wled00/data/404.htm index 260253a387..ff41fa6e03 100644 --- a/wled00/data/404.htm +++ b/wled00/data/404.htm @@ -42,6 +42,6 @@

404 Not Found

Akemi does not know where you are headed...

- + \ No newline at end of file diff --git a/wled00/data/index.css b/wled00/data/index.css index 8eac75ece4..fd2db0d3a6 100644 --- a/wled00/data/index.css +++ b/wled00/data/index.css @@ -410,6 +410,7 @@ button { position: -webkit-sticky; position: sticky; bottom: 0; + max-width: 300px; } /* WLEDMM */ @@ -425,7 +426,7 @@ button { } .slider { - max-width: 300px; + /*max-width: 300px;*/ /* margin: 5px auto; add 5px; if you want some vertical space but looks ugly */ border-radius: 24px; position: relative; @@ -441,12 +442,16 @@ button { z-index: 0; } +#sliders .slider { + padding-right: 64px; /* offset for bubble */ +} + #sliders .slider, #info .slider { background-color: var(--c-2); } #sliders .sliderwrap, .sbs .sliderwrap { - left: 16px; /* offset for icon */ + left: 32px; /* offset for icon */ } .filter, .option { @@ -704,18 +709,19 @@ img { .sliderbubble { width: 24px; - position: relative; + position: absolute; display: inline-block; - border-radius: 10px; + border-radius: 16px; background: var(--c-3); color: var(--c-f); - padding: 2px 4px; + padding: 4px; font-size: 14px; - right: 3px; - transition: visibility 0.25s ease, opacity 0.25s ease; + right: 6px; + transition: visibility .25s ease,opacity .25s ease; opacity: 0; visibility: hidden; - left: 8px; + /* left: 8px; */ + top: 4px; } output.sliderbubbleshow { @@ -1041,8 +1047,9 @@ textarea { /* segment power wrapper */ .sbs { - padding: 1px 0 1px 20px; + /*padding: 1px 0 1px 20px;*/ display: var(--sgp); + width: 100%; } .pname { @@ -1557,8 +1564,11 @@ TD .checkmark, TD .radiomark { #sliders .sliderbubble { display: none; } - .sliderwrap { - width: calc(100% - 28px); + #sliders .sliderwrap, .sbs .sliderwrap { + width: calc(100% - 42px); + } + #sliders .slider { + padding-right: 0; } #sliders .sliderwrap { left: 12px; diff --git a/wled00/data/index.htm b/wled00/data/index.htm index e0c9ca547c..6dd114d4c5 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -69,7 +69,7 @@ - +
@@ -204,7 +204,7 @@
- +
@@ -381,7 +381,7 @@
- + @@ -406,8 +406,8 @@
Loading...

-