diff --git a/CHANGELOG.md b/CHANGELOG.md index 63fa9c69..3d792e73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,38 @@ ## WLED changelog +#### Build 2307130 +- larger `oappend()` stack buffer (3.5k) for ESP32 +- Preset cycle bugfix (#3262) +- Rotary encoder ALT fix for large LED count (#3276) +- effect updates (2D Plasmaball), `blur()` speedup +- On/Off toggle from nodes view (may show unknow device type on older versions) (#3291) +- various fixes and improvements (ABL, crashes when changing presets with different segments) + +#### Build 2306270 +- ESP-NOW remote support (#3237) +- Pixel Magic tool (display pixel art) (#3249) +- Websocket (peek) fallback when connection cannot be established, WS retries (#3267) +- Add WiFi network scan RPC command to Improv Serial (#3271) +- Longer (custom option available) segment name for ESP32 +- various fixes and improvements + +#### Build 2306210 +- 0.14.0-b3 release +- respect global I2C in all usermods (no local initilaisation of I2C bus) +- Multi relay usermod compile-time enabled option (-D MULTI_RELAY_ENABLED=true|false) + +#### 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 diff --git a/package-lock.json b/package-lock.json index a1155473..c4fe9b1c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1409,9 +1409,9 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" }, "semver-diff": { "version": "2.1.0", diff --git a/platformio.ini b/platformio.ini index ae0e4abd..d3b71d3c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,7 +10,7 @@ # ------------------------------------------------------------------------------ # CI binaries -;; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth # ESP32 variant builds are temporarily excluded from CI due to toolchain issues on the GitHub Actions Linux environment +; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth # ESP32 variant builds are temporarily excluded from CI due to toolchain issues on the GitHub Actions Linux environment default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB # Release binaries @@ -40,6 +40,8 @@ default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_ ; default_envs = esp32dev_qio80 ; default_envs = esp32_eth_ota1mapp ; default_envs = esp32s2_saola +; default_envs = esp32c3dev +; default_envs = lolin_s2_mini src_dir = ./wled00 data_dir = ./wled00/data @@ -60,13 +62,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 @@ -459,8 +462,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} @@ -572,10 +575,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 011cedd8..90619ba6 100644 --- a/tools/cdata.js +++ b/tools/cdata.js @@ -390,12 +390,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 8e52612c..5e597d01 100644 --- a/usermods/BH1750_v2/usermod_bh1750.h +++ b/usermods/BH1750_v2/usermod_bh1750.h @@ -113,8 +113,7 @@ private: public: void setup() { - PinManagerPinType pins[2] = { { i2c_sda, true }, { i2c_scl, true } }; // allocate pins - if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) return; + if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; } sensorFound = lightMeter.begin(); initDone = true; } @@ -174,7 +173,9 @@ public: 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")); diff --git a/usermods/BME280_v2/usermod_bme280.h b/usermods/BME280_v2/usermod_bme280.h index f4fcb5f0..c7d25ec1 100644 --- a/usermods/BME280_v2/usermod_bme280.h +++ b/usermods/BME280_v2/usermod_bme280.h @@ -184,8 +184,7 @@ private: public: void setup() { - PinManagerPinType pins[2] = { { i2c_sda, true }, { i2c_scl, true } }; // allocate pins - if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { sensorType=0; return; } + if (i2c_scl<0 || i2c_sda<0) { enabled = false; sensorType = 0; return; } if (!bme.begin()) { diff --git a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h index 79983dc4..8a4b9a60 100644 --- a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h +++ b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h @@ -271,10 +271,9 @@ 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 subuf[64]; - strcpy(subuf, mqttDeviceTopic); - strcat_P(subuf, PSTR("/motion")); - mqtt->publish(subuf, 0, false, state); + char buf[64]; + sprintf_P(buf, PSTR("%s/motion"), mqttDeviceTopic); //max length: 33 + 7 = 40 + mqtt->publish(buf, 0, false, state); } #endif } diff --git a/usermods/RTC/usermod_rtc.h b/usermods/RTC/usermod_rtc.h index fd9a4054..42965e3a 100644 --- a/usermods/RTC/usermod_rtc.h +++ b/usermods/RTC/usermod_rtc.h @@ -12,8 +12,7 @@ class RTCUsermod : public Usermod { public: void setup() { - PinManagerPinType pins[2] = { { i2c_scl, true }, { i2c_sda, true } }; - if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { disabled = true; return; } + if (i2c_scl<0 || i2c_sda<0) { disabled = true; return; } RTC.begin(); time_t rtcTime = RTC.get(); if (rtcTime) { @@ -25,8 +24,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 (t != RTC.get()) RTC.set(t); //set RTC to NTP/UI-provided value } diff --git a/usermods/VL53L0X_gestures/usermod_vl53l0x_gestures.h b/usermods/VL53L0X_gestures/usermod_vl53l0x_gestures.h index 80e73b53..fe6b958f 100644 --- a/usermods/VL53L0X_gestures/usermod_vl53l0x_gestures.h +++ b/usermods/VL53L0X_gestures/usermod_vl53l0x_gestures.h @@ -50,8 +50,7 @@ class UsermodVL53L0XGestures : public Usermod { public: void setup() { - PinManagerPinType pins[2] = { { i2c_scl, true }, { i2c_sda, true } }; - if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; } + if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; } sensor.setTimeout(150); if (!sensor.init()) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 837668ee..357e612c 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -1149,6 +1149,13 @@ class AudioReactive : public Usermod { if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin); break; #endif + case 6: + DEBUGSR_PRINTLN(F("AR: ES8388 Source")); + audioSource = new ES8388Source(SAMPLE_RATE, BLOCK_SIZE); + delay(100); + if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin); + break; + #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) // ADC over I2S is only possible on "classic" ESP32 case 0: @@ -1738,6 +1745,8 @@ class AudioReactive : public Usermod { #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) oappend(SET_F("addOption(dd,'Generic I2S PDM',5);")); #endif + oappend(SET_F("addOption(dd,'ES8388',6);")); + oappend(SET_F("dd=addDropdown('AudioReactive','config:AGC');")); oappend(SET_F("addOption(dd,'Off',0);")); oappend(SET_F("addOption(dd,'Normal',1);")); diff --git a/usermods/audioreactive/audio_source.h b/usermods/audioreactive/audio_source.h index 4aa05716..0ba24e8b 100644 --- a/usermods/audioreactive/audio_source.h +++ b/usermods/audioreactive/audio_source.h @@ -427,6 +427,122 @@ public: } }; +/* ES8388 Sound Modude + This is an I2S sound processing unit that requires ininitialization over + I2C before I2S data can be received. +*/ +class ES8388Source : public I2SSource { + private: + + void _es8388I2cWrite(uint8_t reg, uint8_t val) { +#ifndef ES8388_ADDR + Wire.beginTransmission(0x10); + #define ES8388_ADDR 0x10 // default address +#else + Wire.beginTransmission(ES8388_ADDR); +#endif + Wire.write((uint8_t)reg); + Wire.write((uint8_t)val); + uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK + if (i2cErr != 0) { + DEBUGSR_PRINTF("AR: ES8388 I2C write failed with error=%d (addr=0x%X, reg 0x%X, val 0x%X).\n", i2cErr, ES8388_ADDR, reg, val); + } + } + + void _es8388InitAdc() { + // https://dl.radxa.com/rock2/docs/hw/ds/ES8388%20user%20Guide.pdf Section 10.1 + // http://www.everest-semi.com/pdf/ES8388%20DS.pdf Better spec sheet, more clear. + // https://docs.google.com/spreadsheets/d/1CN3MvhkcPVESuxKyx1xRYqfUit5hOdsG45St9BCUm-g/edit#gid=0 generally + // Sets ADC to around what AudioReactive expects, and loops line-in to line-out/headphone for monitoring. + // Registries are decimal, settings are binary as that's how everything is listed in the docs + // ...which makes it easier to reference the docs. + // + _es8388I2cWrite( 8,0b00000000); // I2S to slave + _es8388I2cWrite( 2,0b11110011); // Power down DEM and STM + _es8388I2cWrite(43,0b10000000); // Set same LRCK + _es8388I2cWrite( 0,0b00000101); // Set chip to Play & Record Mode + _es8388I2cWrite(13,0b00000010); // Set MCLK/LRCK ratio to 256 + _es8388I2cWrite( 1,0b01000000); // Power up analog and lbias + _es8388I2cWrite( 3,0b00000000); // Power up ADC, Analog Input, and Mic Bias + _es8388I2cWrite( 4,0b11111100); // Power down DAC, Turn on LOUT1 and ROUT1 and LOUT2 and ROUT2 power + _es8388I2cWrite( 2,0b01000000); // Power up DEM and STM and undocumented bit for "turn on line-out amp" + + // #define use_es8388_mic + + #ifdef use_es8388_mic + // The mics *and* line-in are BOTH connected to LIN2/RIN2 on the AudioKit + // so there's no way to completely eliminate the mics. It's also hella noisy. + // Line-in works OK on the AudioKit, generally speaking, as the mics really need + // amplification to be noticable in a quiet room. If you're in a very loud room, + // the mics on the AudioKit WILL pick up sound even in line-in mode. + // TL;DR: Don't use the AudioKit for anything, use the LyraT. + // + // The LyraT does a reasonable job with mic input as configured below. + + // Pick one of these. If you have to use the mics, use a LyraT over an AudioKit if you can: + _es8388I2cWrite(10,0b00000000); // Use Lin1/Rin1 for ADC input (mic on LyraT) + //_es8388I2cWrite(10,0b01010000); // Use Lin2/Rin2 for ADC input (mic *and* line-in on AudioKit) + + _es8388I2cWrite( 9,0b10001000); // Select Analog Input PGA Gain for ADC to +24dB (L+R) + _es8388I2cWrite(16,0b00000000); // Set ADC digital volume attenuation to 0dB (left) + _es8388I2cWrite(17,0b00000000); // Set ADC digital volume attenuation to 0dB (right) + _es8388I2cWrite(38,0b00011011); // Mixer - route LIN1/RIN1 to output after mic gain + + _es8388I2cWrite(39,0b01000000); // Mixer - route LIN to mixL, +6dB gain + _es8388I2cWrite(42,0b01000000); // Mixer - route RIN to mixR, +6dB gain + _es8388I2cWrite(46,0b00100001); // LOUT1VOL - 0b00100001 = +4.5dB + _es8388I2cWrite(47,0b00100001); // ROUT1VOL - 0b00100001 = +4.5dB + _es8388I2cWrite(48,0b00100001); // LOUT2VOL - 0b00100001 = +4.5dB + _es8388I2cWrite(49,0b00100001); // ROUT2VOL - 0b00100001 = +4.5dB + + // Music ALC - the mics like Auto Level Control + // You can also use this for line-in, but it's not really needed. + // + _es8388I2cWrite(18,0b11111000); // ALC: stereo, max gain +35.5dB, min gain -12dB + _es8388I2cWrite(19,0b00110000); // ALC: target -1.5dB, 0ms hold time + _es8388I2cWrite(20,0b10100110); // ALC: gain ramp up = 420ms/93ms, gain ramp down = check manual for calc + _es8388I2cWrite(21,0b00000110); // ALC: use "ALC" mode, no zero-cross, window 96 samples + _es8388I2cWrite(22,0b01011001); // ALC: noise gate threshold, PGA gain constant, noise gate enabled + #else + _es8388I2cWrite(10,0b01010000); // Use Lin2/Rin2 for ADC input ("line-in") + _es8388I2cWrite( 9,0b00000000); // Select Analog Input PGA Gain for ADC to 0dB (L+R) + _es8388I2cWrite(16,0b01000000); // Set ADC digital volume attenuation to -32dB (left) + _es8388I2cWrite(17,0b01000000); // Set ADC digital volume attenuation to -32dB (right) + _es8388I2cWrite(38,0b00001001); // Mixer - route LIN2/RIN2 to output + + _es8388I2cWrite(39,0b01010000); // Mixer - route LIN to mixL, 0dB gain + _es8388I2cWrite(42,0b01010000); // Mixer - route RIN to mixR, 0dB gain + _es8388I2cWrite(46,0b00011011); // LOUT1VOL - 0b00011110 = +0dB, 0b00011011 = LyraT balance fix + _es8388I2cWrite(47,0b00011110); // ROUT1VOL - 0b00011110 = +0dB + _es8388I2cWrite(48,0b00011110); // LOUT2VOL - 0b00011110 = +0dB + _es8388I2cWrite(49,0b00011110); // ROUT2VOL - 0b00011110 = +0dB + #endif + + } + + public: + ES8388Source(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f, bool i2sMaster=true) : + I2SSource(sampleRate, blockSize, sampleScale) { + _config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT; + }; + + void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) { + + if ((i2sckPin < 0) || (mclkPin < 0)) { + DEBUGSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin); + return; + } + + // First route mclk, then configure ADC over I2C, then configure I2S + _es8388InitAdc(); + I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin); + } + + void deinitialize() { + I2SSource::deinitialize(); + } + +}; #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) #if !defined(SOC_I2S_SUPPORTS_ADC) && !defined(SOC_I2S_SUPPORTS_ADC_DAC) diff --git a/usermods/mpu6050_imu/usermod_mpu6050_imu.h b/usermods/mpu6050_imu/usermod_mpu6050_imu.h index 8a6c3dc2..748ddf1a 100644 --- a/usermods/mpu6050_imu/usermod_mpu6050_imu.h +++ b/usermods/mpu6050_imu/usermod_mpu6050_imu.h @@ -85,8 +85,7 @@ class MPU6050Driver : public Usermod { * setup() is called once at boot. WiFi is not yet connected at this point. */ void setup() { - PinManagerPinType pins[2] = { { i2c_scl, true }, { i2c_sda, true } }; - if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; } + if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; } #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE Wire.setClock(400000U); // 400kHz I2C clock. Comment this line if having compilation difficulties #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE diff --git a/usermods/multi_relay/usermod_multi_relay.h b/usermods/multi_relay/usermod_multi_relay.h index 615bf8c0..7234df90 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) @@ -45,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 @@ -315,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(); } @@ -323,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(); } @@ -335,7 +339,7 @@ byte MultiRelay::IOexpanderRead(int address) { MultiRelay::MultiRelay() : _switchTimerStart(0) - , enabled(false) + , enabled(MULTI_RELAY_ENABLED) , initDone(false) , usePcf8574(USE_PCF8574) , addrPcf8574(PCF8574_ADDRESS) @@ -347,7 +351,7 @@ MultiRelay::MultiRelay() 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; } @@ -476,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; } @@ -519,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; } } @@ -635,7 +640,7 @@ void MultiRelay::addToJsonInfo(JsonObject &root) { String uiDomString; for (int i=0; i(not hex!)','address');")); + oappend(SET_F("addInfo('MultiRelay:PCF8574-address',1,'(not hex!)');")); 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]]}});")); } /** @@ -762,7 +768,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; @@ -771,14 +777,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 @@ -792,22 +798,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 @@ -825,4 +820,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/sht/usermod_sht.h b/usermods/sht/usermod_sht.h index 56f3dd41..56cea219 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 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 @@ -94,7 +93,7 @@ void ShtUsermod::initShtTempHumiditySensor() case USERMOD_SHT_TYPE_SHT85: shtTempHumidSensor = (SHT *) new SHT85(); break; } - shtTempHumidSensor->begin(shtI2cAddress, i2c_sda, i2c_scl); + shtTempHumidSensor->begin(shtI2cAddress); // uses &Wire if (shtTempHumidSensor->readStatus() == 0xFFFF) { DEBUG_PRINTF("[%s] SHT init failed!\n", _name); cleanup(); @@ -132,13 +131,6 @@ void ShtUsermod::cleanupShtTempHumiditySensor() void ShtUsermod::cleanup() { cleanupShtTempHumiditySensor(); - - if (pinAllocDone) { - PinManagerPinType pins[2] = { { i2c_sda, true }, { i2c_scl, true } }; - pinManager.deallocateMultiplePins(pins, 2, PinOwner::HW_I2C); - pinAllocDone = false; - } - enabled = false; } @@ -237,14 +229,12 @@ void ShtUsermod::appendDeviceToMqttDiscoveryMessage(JsonDocument& root) { void ShtUsermod::setup() { if (enabled) { - PinManagerPinType pins[2] = { { i2c_sda, true }, { i2c_scl, true } }; - // GPIOs can be set to -1 and allocateMultiplePins() will return true, so check they're gt zero - if (i2c_sda < 0 || i2c_scl < 0 || !pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { - DEBUG_PRINTF("[%s] SHT pin allocation failed!\n", _name); + // GPIOs can be set to -1 , so check they're gt zero + if (i2c_sda < 0 || i2c_scl < 0) { + DEBUG_PRINTF("[%s] I2C bus not initialised!\n", _name); cleanup(); return; } - pinAllocDone = true; initShtTempHumiditySensor(); 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 afc1bb63..5a99c3cd 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 @@ -82,13 +82,14 @@ void DisplayTaskCode(void * parameter); typedef enum { NONE = 0, - SSD1306, // U8X8_SSD1306_128X32_UNIVISION_HW_I2C - SH1106, // U8X8_SH1106_128X64_WINSTAR_HW_I2C - SSD1306_64, // U8X8_SSD1306_128X64_NONAME_HW_I2C - SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C - SSD1305_64, // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C - SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI - SSD1306_SPI64 // U8X8_SSD1306_128X64_NONAME_HW_SPI + SSD1306, // U8X8_SSD1306_128X32_UNIVISION_HW_I2C + SH1106, // U8X8_SH1106_128X64_WINSTAR_HW_I2C + SSD1306_64, // U8X8_SSD1306_128X64_NONAME_HW_I2C + SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C + SSD1305_64, // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C + SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI + SSD1306_SPI64, // U8X8_SSD1306_128X64_NONAME_HW_SPI + SSD1309_SPI64 // U8X8_SSD1309_128X64_NONAME0_4W_HW_SPI } DisplayType; @@ -533,24 +534,18 @@ void FourLineDisplayUsermod::sleepOrClock(bool enabled) { // gets called once at boot. Do all initialization that doesn't depend on // network here void FourLineDisplayUsermod::setup() { - if (type == NONE || !enabled) return; - - bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64); + bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64 || type == SSD1309_SPI64); // check if pins are -1 and disable usermod as PinManager::allocateMultiplePins() will accept -1 as a valid pin if (isSPI) { - PinManagerPinType cspins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } }; - if (ioPin[0]==-1 || ioPin[1]==-1 || ioPin[1]==-1) { type=NONE; return; } - if (!pinManager.allocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay)) { type=NONE; return; } - PinManagerPinType pins[2] = { { spi_sclk, true }, { spi_mosi, true } }; - if (spi_sclk==-1 || spi_mosi==-1 || !pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_SPI)) { - pinManager.deallocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay); + if (spi_sclk<0 || spi_mosi<0 || ioPin[0]<0 || ioPin[1]<0 || ioPin[1]<0) { type = NONE; - return; + } else { + PinManagerPinType cspins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } }; + if (!pinManager.allocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay)) { type = NONE; } } } else { - PinManagerPinType pins[2] = { {i2c_scl, true }, { i2c_sda, true } }; - if (i2c_scl==-1 || i2c_sda==-1 || !pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { type=NONE; return; } + if (i2c_scl<0 || i2c_sda<0) { type=NONE; } } DEBUG_PRINTLN(F("Allocating display.")); @@ -563,20 +558,16 @@ void FourLineDisplayUsermod::setup() { case SSD1305_64: u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(); break; // U8X8 uses global SPI variable that is attached to VSPI bus on ESP32 case SSD1306_SPI: u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset - case SSD1306_SPI64: u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset + case SSD1306_SPI64: u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset + case SSD1309_SPI64: u8x8 = (U8X8 *) new U8X8_SSD1309_128X64_NONAME0_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset // catchall - default: u8x8 = (U8X8 *) new U8X8_NULL(); break; + default: u8x8 = (U8X8 *) new U8X8_NULL(); enabled = false; break; // catchall to create U8x8 instance } if (nullptr == u8x8) { DEBUG_PRINTLN(F("Display init failed.")); if (isSPI) { - int8_t pins[] = {spi_sclk, spi_mosi}; - pinManager.deallocateMultiplePins((const uint8_t*)pins, 2, PinOwner::HW_SPI); pinManager.deallocateMultiplePins((const uint8_t*)ioPin, 3, PinOwner::UM_FourLineDisplay); - } else { - int8_t pins[] = {i2c_scl, i2c_sda}; - pinManager.deallocateMultiplePins((const uint8_t*)pins, 2, PinOwner::HW_I2C); } type = NONE; return; @@ -1215,6 +1206,7 @@ void FourLineDisplayUsermod::appendConfigData() { oappend(SET_F("addOption(dd,'SSD1305 128x64',5);")); oappend(SET_F("addOption(dd,'SSD1306 SPI',6);")); oappend(SET_F("addOption(dd,'SSD1306 SPI 128x64',7);")); + oappend(SET_F("addOption(dd,'SSD1309 SPI 128x64',8);")); oappend(SET_F("addInfo('4LineDisplay:type',1,'
Change may require reboot','');")); oappend(SET_F("addInfo('4LineDisplay:pin[]',0,'','SPI CS');")); oappend(SET_F("addInfo('4LineDisplay:pin[]',1,'','SPI DC');")); @@ -1306,38 +1298,30 @@ bool FourLineDisplayUsermod::readFromConfig(JsonObject& root) { bool pinsChanged = false; for (byte i=0; i<3; i++) if (ioPin[i] != oldPin[i]) { pinsChanged = true; break; } if (pinsChanged || type!=newType) { - bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64); - bool newSPI = (newType == SSD1306_SPI || newType == SSD1306_SPI64); + bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64 || type == SSD1309_SPI64); + bool newSPI = (newType == SSD1306_SPI || newType == SSD1306_SPI64 || newType == SSD1309_SPI64); if (isSPI) { if (pinsChanged || !newSPI) pinManager.deallocateMultiplePins((const uint8_t*)oldPin, 3, PinOwner::UM_FourLineDisplay); if (!newSPI) { // was SPI but is no longer SPI - int8_t oldPins[] = {spi_sclk, spi_mosi}; - pinManager.deallocateMultiplePins((const uint8_t*)oldPins, 2, PinOwner::HW_SPI); - PinManagerPinType pins[2] = { {i2c_scl, true }, { i2c_sda, true } }; - if (i2c_scl==-1 || i2c_sda==-1 || !pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { newType=NONE; } + if (i2c_scl<0 || i2c_sda<0) { newType=NONE; } } else { // still SPI but pins changed PinManagerPinType cspins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } }; - if (ioPin[0]==-1 || ioPin[1]==-1 || ioPin[1]==-1) { newType=NONE; } + if (ioPin[0]<0 || ioPin[1]<0 || ioPin[1]<0) { newType=NONE; } else if (!pinManager.allocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay)) { newType=NONE; } } } else if (newSPI) { // was I2C but is now SPI - int8_t oldPins[] = {i2c_scl, i2c_sda}; - pinManager.deallocateMultiplePins((const uint8_t*)oldPins, 2, PinOwner::HW_I2C); - PinManagerPinType pins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } }; - if (ioPin[0]==-1 || ioPin[1]==-1 || ioPin[1]==-1) { newType=NONE; } - else if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_FourLineDisplay)) { newType=NONE; } - else { - PinManagerPinType pins[2] = { { spi_sclk, true }, { spi_mosi, true } }; - if (spi_sclk==-1 || spi_mosi==-1 || !pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_SPI)) { - pinManager.deallocateMultiplePins(pins, 3, PinOwner::UM_FourLineDisplay); - newType = NONE; - } + if (spi_sclk<0 || spi_mosi<0) { + newType=NONE; + } else { + PinManagerPinType pins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } }; + if (ioPin[0]<0 || ioPin[1]<0 || ioPin[1]<0) { newType=NONE; } + else if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_FourLineDisplay)) { newType=NONE; } } } else { - // just I2C tye changed + // just I2C type changed } type = newType; switch (type) { @@ -1369,8 +1353,12 @@ bool FourLineDisplayUsermod::readFromConfig(JsonObject& root) { u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1306_128x64_noname, u8x8_cad_001, u8x8_byte_arduino_hw_spi, u8x8_gpio_and_delay_arduino); u8x8_SetPin_4Wire_HW_SPI(u8x8->getU8x8(), ioPin[0], ioPin[1], ioPin[2]); // Pins are cs, dc, reset break; + case SSD1309_SPI64: + u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1309_128x64_noname0, u8x8_cad_001, u8x8_byte_arduino_hw_spi, u8x8_gpio_and_delay_arduino); + u8x8_SetPin_4Wire_HW_SPI(u8x8->getU8x8(), ioPin[0], ioPin[1], ioPin[2]); // Pins are cs, dc, reset default: u8x8_Setup(u8x8->getU8x8(), u8x8_d_null_cb, u8x8_cad_empty, u8x8_byte_empty, u8x8_dummy_cb); + enabled = false; break; } startDisplay(); 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 cdb4f04c..b142f903 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 @@ -45,6 +45,10 @@ #define ENCODER_SW_PIN 19 #endif +#ifndef ENCODER_MAX_DELAY_MS // max delay between polling encoder pins +#define ENCODER_MAX_DELAY_MS 8 // 8 milliseconds => max 120 change impulses in 1 second, for full turn of a 30/30 encoder (4 changes per segment, 30 segments for one turn) +#endif + #ifndef USERMOD_USE_PCF8574 #undef USE_PCF8574 #define USE_PCF8574 false @@ -343,7 +347,7 @@ class RotaryEncoderUIUsermod : public Usermod { */ void addToConfig(JsonObject &root); - //void appendConfigData(); + void appendConfigData(); /** * restore the changeable values @@ -376,6 +380,7 @@ class RotaryEncoderUIUsermod : public Usermod { */ byte RotaryEncoderUIUsermod::readPin(uint8_t pin) { if (usePcf8574) { + if (pin >= 100) pin -= 100; // PCF I/O ports return (pcfPortData>>pin) & 1; } else { return digitalRead(pin); @@ -473,7 +478,7 @@ void RotaryEncoderUIUsermod::setup() DEBUG_PRINTLN(F("Usermod Rotary Encoder init.")); if (usePcf8574) { - if ((i2c_sda == i2c_scl && i2c_sda == -1) || pinA<0 || pinB<0 || pinC<0) { + if (i2c_sda < 0 || i2c_scl < 0 || pinA < 0 || pinB < 0 || pinC < 0) { DEBUG_PRINTLN(F("I2C and/or PCF8574 pins unused, disabling.")); enabled = false; return; @@ -538,8 +543,9 @@ void RotaryEncoderUIUsermod::setup() */ void RotaryEncoderUIUsermod::loop() { - if (!enabled || strip.isUpdating()) return; + if (!enabled) return; unsigned long currentTime = millis(); // get the current elapsed time + if (strip.isUpdating() && ((currentTime - loopTime) < ENCODER_MAX_DELAY_MS)) return; // be nice, but not too nice // Initialize effectCurrentIndex and effectPaletteIndex to // current state. We do it here as (at least) effectCurrent @@ -1071,9 +1077,10 @@ void RotaryEncoderUIUsermod::addToConfig(JsonObject &root) { DEBUG_PRINTLN(F("Rotary Encoder config saved.")); } -//void RotaryEncoderUIUsermod::appendConfigData() { -// oappend(SET_F("addInfo('RotaryEncoderUIUsermod:PCF8574-address',1,'(not hex!)');")); -//} +void RotaryEncoderUIUsermod::appendConfigData() { + oappend(SET_F("addInfo('Rotary-Encoder:PCF8574-address',1,'(not hex!)');")); + oappend(SET_F("d.extra.push({'Rotary-Encoder':{pin:[['P0',100],['P1',101],['P2',102],['P3',103],['P4',104],['P5',105],['P6',106],['P7',107]]}});")); +} /** * readFromConfig() is called before setup() to populate properties from values stored in cfg.json @@ -1122,7 +1129,7 @@ bool RotaryEncoderUIUsermod::readFromConfig(JsonObject &root) { pinManager.deallocatePin(pinIRQ, PinOwner::UM_RotaryEncoderUI); DEBUG_PRINTLN(F("Deallocated old IRQ pin.")); } - pinIRQ = newIRQpin; + pinIRQ = newIRQpin<100 ? newIRQpin : -1; // ignore PCF8574 pins } else { pinManager.deallocatePin(pinA, PinOwner::UM_RotaryEncoderUI); pinManager.deallocatePin(pinB, PinOwner::UM_RotaryEncoderUI); diff --git a/wled00.sln b/wled00.sln deleted file mode 100644 index b2f12b83..00000000 --- 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 6428c8a0..533da32b 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -278,7 +278,7 @@ uint16_t mode_random_color(void) { SEGMENT.fill(color_blend(SEGMENT.color_wheel(SEGENV.aux1), SEGMENT.color_wheel(SEGENV.aux0), fade)); return FRAMETIME; } -static const char _data_FX_MODE_RANDOM_COLOR[] PROGMEM = "Random Colors@!,Fade time;;!"; +static const char _data_FX_MODE_RANDOM_COLOR[] PROGMEM = "Random Colors@!,Fade time;;!;01"; /* @@ -433,7 +433,7 @@ uint16_t mode_rainbow(void) { return FRAMETIME; } -static const char _data_FX_MODE_RAINBOW[] PROGMEM = "Colorloop@!,Saturation;;!"; +static const char _data_FX_MODE_RAINBOW[] PROGMEM = "Colorloop@!,Saturation;;!;01"; /* @@ -5095,21 +5095,22 @@ uint16_t mode_2DLissajous(void) { // By: Andrew Tuline const uint16_t rows = SEGMENT.virtualHeight(); SEGMENT.fadeToBlackBy(SEGMENT.intensity); + uint_fast16_t phase = (millis() * (1 + SEGENV.custom3)) /32; // allow user to control rotation speed //for (int i=0; i < 4*(cols+rows); i ++) { for (int i=0; i < 256; i ++) { //float xlocn = float(sin8(now/4+i*(SEGMENT.speed>>5))) / 255.0f; //float ylocn = float(cos8(now/4+i*2)) / 255.0f; - uint8_t xlocn = sin8(millis()/4+i*(SEGMENT.speed>>5)); - uint8_t ylocn = cos8(millis()/4+i*2); - xlocn = map(xlocn,0,255,0,cols-1); - ylocn = map(ylocn,0,255,0,rows-1); - SEGMENT.setPixelColorXY(xlocn, ylocn, SEGMENT.color_from_palette(millis()/100+i, false, PALETTE_SOLID_WRAP, 0)); + uint_fast8_t xlocn = sin8(phase/2 + (i*SEGMENT.speed)/32); + uint_fast8_t ylocn = cos8(phase/2 + i*2); + xlocn = (cols < 2) ? 1 : (map(2*xlocn, 0,511, 0,2*(cols-1)) +1) /2; // softhack007: "(2* ..... +1) /2" for proper rounding + ylocn = (rows < 2) ? 1 : (map(2*ylocn, 0,511, 0,2*(rows-1)) +1) /2; // "rows > 1" is needed to avoid div/0 in map() + SEGMENT.setPixelColorXY((uint8_t)xlocn, (uint8_t)ylocn, SEGMENT.color_from_palette(millis()/100+i, false, PALETTE_SOLID_WRAP, 0)); } return FRAMETIME; } // mode_2DLissajous() -static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,Fade rate;!;!;2"; +static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,Fade rate,,,Speed;!;!;2;;c3=15"; /////////////////////// @@ -5273,7 +5274,7 @@ uint16_t mode_2DPlasmaball(void) { // By: Stepko https://edito SEGMENT.fadeToBlackBy(SEGMENT.custom1>>2); - float t = millis() / (33 - SEGMENT.speed/8); + uint_fast32_t t = (millis() * 8) / (256 - SEGMENT.speed); // optimized to avoid float for (int i = 0; i < cols; i++) { uint16_t thisVal = inoise8(i * 30, t, t); uint16_t thisMax = map(thisVal, 0, 255, 0, cols-1); @@ -5876,11 +5877,17 @@ 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'}; + char text[WLED_MAX_SEGNAME_LEN+1] = {'\0'}; if (SEGMENT.name) for (size_t i=0,j=0; i31 && SEGMENT.name[i]<128) text[j++] = SEGMENT.name[i]; - if (!strlen(text) || !strncmp_P(text,PSTR("#DATE"),5) || !strncmp_P(text,PSTR("#DDMM"),5) || !strncmp_P(text,PSTR("#MMDD"),5) || !strncmp_P(text,PSTR("#TIME"),5) || !strncmp_P(text,PSTR("#HHMM"),5)) { // fallback if empty segment name: display date and time + if (!strlen(text) + || !strncmp_P(text,PSTR("#DATE"),5) + || !strncmp_P(text,PSTR("#DDMM"),5) + || !strncmp_P(text,PSTR("#MMDD"),5) + || !strncmp_P(text,PSTR("#TIME"),5) + || !strncmp_P(text,PSTR("#HHMM"),5)) { // fallback if empty segment name: display date and time char sec[5]; byte AmPmHour = hour(localTime); boolean isitAM = true; @@ -5890,12 +5897,12 @@ uint16_t mode_2Dscrollingtext(void) { } if (useAMPM) sprintf_P(sec, PSTR(" %2s"), (isitAM ? "AM" : "PM")); else sprintf_P(sec, PSTR(":%02d"), second(localTime)); - if (!strncmp_P(text,PSTR("#DATE"),5)) sprintf_P(text, PSTR("%d.%d.%d"), day(localTime), month(localTime), year(localTime)); - else if (!strncmp_P(text,PSTR("#DDMM"),5)) sprintf_P(text, PSTR("%d.%d"), day(localTime), month(localTime)); - else if (!strncmp_P(text,PSTR("#MMDD"),5)) sprintf_P(text, PSTR("%d/%d"), month(localTime), day(localTime)); - else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec); - else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, PSTR("%2d:%02d"), AmPmHour, minute(localTime)); - else sprintf_P(text, PSTR("%s %d, %d %2d:%02d%s"), monthShortStr(month(localTime)), day(localTime), year(localTime), AmPmHour, minute(localTime), sec); + if (!strncmp_P(text,PSTR("#DATE"),5)) sprintf_P(text, zero?PSTR("%02d.%02d.%04d"):PSTR("%d.%d.%d"), day(localTime), month(localTime), year(localTime)); + else if (!strncmp_P(text,PSTR("#DDMM"),5)) sprintf_P(text, zero?PSTR("%02d.%02d"):PSTR("%d.%d"), day(localTime), month(localTime)); + else if (!strncmp_P(text,PSTR("#MMDD"),5)) sprintf_P(text, zero?PSTR("%02d/%02d"):PSTR("%d/%d"), month(localTime), day(localTime)); + else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, zero?PSTR("%02d:%02d%s"):PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec); + else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, zero?PSTR("%02d:%02d"):PSTR("%d:%02d"), AmPmHour, minute(localTime)); + else sprintf_P(text, zero?PSTR("%s %02d, %04d %02d:%02d%s"):PSTR("%s %d, %d %d:%02d%s"), monthShortStr(month(localTime)), day(localTime), year(localTime), AmPmHour, minute(localTime), sec); } const int numberOfLetters = strlen(text); @@ -5922,7 +5929,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"; //////////////////////////// @@ -6833,7 +6840,7 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch return FRAMETIME; } // mode_freqmatrix() -static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Time delay,Sound effect,Low bin,High bin,Sensivity;;;1f;m12=3,si=0"; // Corner, Beatsin +static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Speed,Sound effect,Low bin,High bin,Sensivity;;;1f;m12=3,si=0"; // Corner, Beatsin ////////////////////// @@ -6937,7 +6944,7 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun return FRAMETIME; } // mode_freqwave() -static const char _data_FX_MODE_FREQWAVE[] PROGMEM = "Freqwave@Time delay,Sound effect,Low bin,High bin,Pre-amp;;;1f;m12=2,si=0"; // Circle, Beatsin +static const char _data_FX_MODE_FREQWAVE[] PROGMEM = "Freqwave@Speed,Sound effect,Low bin,High bin,Pre-amp;;;1f;m12=2,si=0"; // Circle, Beatsin /////////////////////// diff --git a/wled00/FX.h b/wled00/FX.h index 19b1fc4a..4f7bcb88 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -487,7 +487,7 @@ typedef struct Segment { //if (leds) Serial.printf(" [%u]", length()*sizeof(CRGB)); //Serial.println(); //#endif - if (!Segment::_globalLeds && leds) free(leds); + if (!Segment::_globalLeds && leds) { free(leds); leds = nullptr;} // reset to nullptr, to avoid race conditions if (name) delete[] name; if (_t) delete _t; deallocateData(); @@ -507,9 +507,9 @@ typedef struct Segment { inline bool hasRGB(void) const { return _isRGB; } inline bool hasWhite(void) const { return _hasW; } inline bool isCCT(void) const { return _isCCT; } - inline uint16_t width(void) const { return stop - start; } // segment width in physical pixels (length if 1D) - inline uint16_t height(void) const { return stopY - startY; } // segment height (if 2D) in physical pixels - inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels + inline uint16_t width(void) const { return (stop > start) ? (stop - start) : 0; } // segment width in physical pixels (length if 1D) + inline uint16_t height(void) const { return (stopY > startY) ? (stopY - startY) : 0; } // segment height (if 2D) in physical pixels // softhack007: make sure its always > 0 + inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels inline uint16_t groupLength(void) const { return grouping + spacing; } inline uint8_t getLightCapabilities(void) const { return _capabilities; } @@ -710,7 +710,7 @@ class WS2812FX { // 96 bytes panel.clear(); #endif customPalettes.clear(); - if (useLedsArray && Segment::_globalLeds) free(Segment::_globalLeds); + if (useLedsArray && Segment::_globalLeds) {free(Segment::_globalLeds); Segment::_globalLeds = nullptr;} // reset to nullptr, to avoid race conditions } static WS2812FX* getInstance(void) { return instance; } diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 8b4c70ef..28c35fa4 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -191,6 +191,8 @@ uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) { uint16_t /*IRAM_ATTR*/ Segment::XY(uint16_t x, uint16_t y) { uint16_t width = virtualWidth(); // segment width in logical pixels uint16_t height = virtualHeight(); // segment height in logical pixels + if (width == 0) return 0; // softhack007 avoid div/0 + if (height == 0) return (x%width); // softhack007 avoid div/0 return (x%width) + (y%height) * width; } @@ -202,7 +204,6 @@ void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col) if (leds) leds[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); @@ -305,6 +306,7 @@ void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t // Adds the specified color with the existing pixel color perserving color balance. void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) { + if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit uint32_t col = getPixelColorXY(x,y); uint8_t r = R(col); uint8_t g = G(col); @@ -329,50 +331,54 @@ void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { // blurRow: perform a blur on a row of a rectangular matrix void Segment::blurRow(uint16_t row, fract8 blur_amount) { - const uint16_t cols = virtualWidth(); - const uint16_t rows = virtualHeight(); + const uint_fast16_t cols = virtualWidth(); + const uint_fast16_t rows = virtualHeight(); if (row >= rows) return; // blur one row uint8_t keep = 255 - blur_amount; uint8_t seep = blur_amount >> 1; CRGB carryover = CRGB::Black; - for (uint16_t x = 0; x < cols; x++) { + for (uint_fast16_t x = 0; x < cols; x++) { CRGB cur = getPixelColorXY(x, row); + CRGB before = cur; // remember color before blur CRGB part = cur; part.nscale8(seep); cur.nscale8(keep); cur += carryover; - if (x) { + if (x>0) { CRGB prev = CRGB(getPixelColorXY(x-1, row)) + part; setPixelColorXY(x-1, row, prev); } - setPixelColorXY(x, row, cur); + if (before != cur) // optimization: only set pixel if color has changed + setPixelColorXY(x, row, cur); carryover = part; } } // blurCol: perform a blur on a column of a rectangular matrix void Segment::blurCol(uint16_t col, fract8 blur_amount) { - const uint16_t cols = virtualWidth(); - const uint16_t rows = virtualHeight(); + const uint_fast16_t cols = virtualWidth(); + const uint_fast16_t rows = virtualHeight(); if (col >= cols) return; // blur one column uint8_t keep = 255 - blur_amount; uint8_t seep = blur_amount >> 1; CRGB carryover = CRGB::Black; - for (uint16_t i = 0; i < rows; i++) { - CRGB cur = getPixelColorXY(col, i); + for (uint_fast16_t y = 0; y < rows; y++) { + CRGB cur = getPixelColorXY(col, y); CRGB part = cur; + CRGB before = cur; // remember color before blur part.nscale8(seep); cur.nscale8(keep); cur += carryover; - if (i) { - CRGB prev = CRGB(getPixelColorXY(col, i-1)) + part; - setPixelColorXY(col, i-1, prev); + if (y>0) { + CRGB prev = CRGB(getPixelColorXY(col, y-1)) + part; + setPixelColorXY(col, y-1, prev); } - setPixelColorXY(col, i, cur); + if (before != cur) // optimization: only set pixel if color has changed + setPixelColorXY(col, y, cur); carryover = part; } } @@ -391,8 +397,8 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) { for (uint16_t j = 0; j < dim1; j++) { uint16_t x = vertical ? i : j; uint16_t y = vertical ? j : i; - uint16_t xp = vertical ? x : x-1; - uint16_t yp = vertical ? y-1 : y; + int16_t xp = vertical ? x : x-1; // "signed" to prevent underflow + int16_t yp = vertical ? y-1 : y; // "signed" to prevent underflow uint16_t xn = vertical ? x : x+1; uint16_t yn = vertical ? y+1 : y; CRGB curr = getPixelColorXY(x,y); diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 31345138..8d91c056 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -90,18 +90,18 @@ Segment::Segment(const Segment &orig) { if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); } - if (orig.leds && !Segment::_globalLeds) { leds = (CRGB*)malloc(sizeof(CRGB)*length()); if (leds) memcpy(leds, orig.leds, sizeof(CRGB)*length()); } + if (orig.leds && !Segment::_globalLeds && length() > 0) { leds = (CRGB*)malloc(sizeof(CRGB)*length()); if (leds) memcpy(leds, orig.leds, sizeof(CRGB)*length()); } } // move constructor Segment::Segment(Segment &&orig) noexcept { //DEBUG_PRINTLN(F("-- Move segment constructor --")); memcpy((void*)this, (void*)&orig, sizeof(Segment)); + orig.leds = nullptr; orig.name = nullptr; orig.data = nullptr; orig._dataLen = 0; orig._t = nullptr; - orig.leds = nullptr; } // copy assignment @@ -111,7 +111,7 @@ Segment& Segment::operator= (const Segment &orig) { // clean destination if (name) delete[] name; if (_t) delete _t; - if (leds && !Segment::_globalLeds) free(leds); + if (leds && !Segment::_globalLeds) {free(leds); leds=nullptr;} deallocateData(); // copy source memcpy((void*)this, (void*)&orig, sizeof(Segment)); @@ -125,7 +125,7 @@ Segment& Segment::operator= (const Segment &orig) { if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); } - if (orig.leds && !Segment::_globalLeds) { leds = (CRGB*)malloc(sizeof(CRGB)*length()); if (leds) memcpy(leds, orig.leds, sizeof(CRGB)*length()); } + if (orig.leds && !Segment::_globalLeds && length() > 0) { leds = (CRGB*)malloc(sizeof(CRGB)*length()); if (leds) memcpy(leds, orig.leds, sizeof(CRGB)*length()); } } return *this; } @@ -137,7 +137,7 @@ Segment& Segment::operator= (Segment &&orig) noexcept { if (name) delete[] name; // free old name deallocateData(); // free old runtime data if (_t) delete _t; - if (leds && !Segment::_globalLeds) free(leds); + if (leds && !Segment::_globalLeds) {free(leds); leds=nullptr;} memcpy((void*)this, (void*)&orig, sizeof(Segment)); orig.name = nullptr; orig.data = nullptr; @@ -305,23 +305,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); } @@ -334,10 +337,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; } @@ -368,6 +371,7 @@ CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal void Segment::handleTransition() { if (!transitional) return; 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) { @@ -375,7 +379,6 @@ void Segment::handleTransition() { _t = nullptr; } } - if (_progress == 0xFFFFU) transitional = false; // finish transitioning segment } void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y) { @@ -544,6 +547,7 @@ uint16_t Segment::virtualLength() const { } #endif uint16_t groupLen = groupLength(); + if (groupLen < 1) groupLen = 1; // prevent division by zero - better safe than sorry ... uint16_t vLength = (length() + groupLen - 1) / groupLen; if (mirror) vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED return vLength; @@ -622,7 +626,6 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col) 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); @@ -727,7 +730,7 @@ uint32_t Segment::getPixelColor(int i) i += start; /* offset/phase */ i += offset; - if (i >= stop) i -= length(); + if ((i >= stop) && (stop>0)) i -= length(); // avoids negative pixel index (stop = 0 is a possible value) return strip.getPixelColor(i); } @@ -884,6 +887,7 @@ void Segment::fade_out(uint8_t rate) { // fades all pixels to black using nscale8() void Segment::fadeToBlackBy(uint8_t fadeBy) { + if (fadeBy == 0) return; // optimization - no scaling to apply const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); const uint16_t rows = virtualHeight(); // will be 1 for 1D @@ -898,23 +902,26 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) { */ void Segment::blur(uint8_t blur_amount) { + if (blur_amount == 0) return; // optimization: 0 means "don't blur" #ifndef WLED_DISABLE_2D if (is2D()) { // compatibility with 2D - const uint16_t cols = virtualWidth(); - const uint16_t rows = virtualHeight(); - for (uint16_t i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows - for (uint16_t k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns + const uint_fast16_t cols = virtualWidth(); + const uint_fast16_t rows = virtualHeight(); + for (uint_fast16_t i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows + for (uint_fast16_t k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns return; } #endif uint8_t keep = 255 - blur_amount; uint8_t seep = blur_amount >> 1; CRGB carryover = CRGB::Black; - for(uint16_t i = 0; i < virtualLength(); i++) + uint_fast16_t vlength = virtualLength(); + for(uint_fast16_t i = 0; i < vlength; i++) { CRGB cur = CRGB(getPixelColor(i)); CRGB part = cur; + CRGB before = cur; // remember color before blur part.nscale8(seep); cur.nscale8(keep); cur += carryover; @@ -923,9 +930,10 @@ void Segment::blur(uint8_t blur_amount) uint8_t r = R(c); uint8_t g = G(c); uint8_t b = B(c); - setPixelColor(i-1, qadd8(r, part.red), qadd8(g, part.green), qadd8(b, part.blue)); + setPixelColor((uint16_t)(i-1), qadd8(r, part.red), qadd8(g, part.green), qadd8(b, part.blue)); } - setPixelColor(i,cur.red, cur.green, cur.blue); + if (before != cur) // optimization: only set pixel if color has changed + setPixelColor((uint16_t)i,cur.red, cur.green, cur.blue); carryover = part; } } @@ -1075,7 +1083,7 @@ void WS2812FX::finalizeInit(void) //else //#endif Segment::_globalLeds = (CRGB*) malloc(arrSize); - memset(Segment::_globalLeds, 0, arrSize); + if (Segment::_globalLeds && (arrSize > 0)) memset(Segment::_globalLeds, 0, arrSize); } //segments are created in makeAutoSegments(); @@ -1220,7 +1228,8 @@ void WS2812FX::estimateCurrentAndLimitBri() { } uint32_t powerSum0 = powerSum; - powerSum *= _brightness; + //powerSum *= _brightness; // for NPBrightnessBus + powerSum *= 255; // no need to scale down powerSum - NPB-LG getPixelColor returns colors scaled down by brightness if (powerSum > powerBudget) //scale brightness down to stay in current limit { @@ -1228,11 +1237,14 @@ void WS2812FX::estimateCurrentAndLimitBri() { uint16_t scaleI = scale * 255; uint8_t scaleB = (scaleI > 255) ? 255 : scaleI; uint8_t newBri = scale8(_brightness, scaleB); - busses.setBrightness(newBri); //to keep brightness uniform, sets virtual busses too - currentMilliamps = (powerSum0 * newBri) / puPerMilliamp; + // to keep brightness uniform, sets virtual busses too - softhack007: apply reductions immediately + if (scaleB < 255) busses.setBrightness(scaleB, true); // NPB-LG has already applied brightness, so its suffifient to post-apply scaling ==> use scaleB instead of newBri + busses.setBrightness(newBri, false); // set new brightness for next frame + //currentMilliamps = (powerSum0 * newBri) / puPerMilliamp; // for NPBrightnessBus + currentMilliamps = (powerSum0 * scaleB) / puPerMilliamp; // for NPBus-LG } else { currentMilliamps = powerSum / puPerMilliamp; - busses.setBrightness(_brightness); + busses.setBrightness(_brightness, false); // set new brightness for next frame } currentMilliamps += MA_FOR_ESP; //add power of ESP back to estimate currentMilliamps += pLen; //add standby power back to estimate @@ -1590,7 +1602,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/NodeStruct.h b/wled00/NodeStruct.h index 916c5a9a..2d4d3609 100644 --- a/wled00/NodeStruct.h +++ b/wled00/NodeStruct.h @@ -9,9 +9,9 @@ #include #define NODE_TYPE_ID_UNDEFINED 0 -#define NODE_TYPE_ID_ESP8266 82 -#define NODE_TYPE_ID_ESP32 32 -#define NODE_TYPE_ID_ESP32S2 33 +#define NODE_TYPE_ID_ESP8266 82 // should be 1 +#define NODE_TYPE_ID_ESP32 32 // should be 2 +#define NODE_TYPE_ID_ESP32S2 33 // etc #define NODE_TYPE_ID_ESP32S3 34 #define NODE_TYPE_ID_ESP32C3 35 @@ -23,7 +23,13 @@ struct NodeStruct String nodeName; IPAddress ip; uint8_t age; - uint8_t nodeType; + union { + uint8_t nodeType; // a waste of space as we only have 5 types + struct { + uint8_t type : 7; // still a waste of space (4 bits would be enough and future-proof) + bool on : 1; + }; + }; uint32_t build; NodeStruct() : age(0), nodeType(0), build(0) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 4b96060e..d11182aa 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -126,15 +126,15 @@ bool BusDigital::canShow() { return PolyBus::canShow(_busPtr, _iType); } -void BusDigital::setBrightness(uint8_t b) { +void BusDigital::setBrightness(uint8_t b, bool immediate) { //Fix for turning off onboard LED breaking bus #ifdef LED_BUILTIN if (_bri == 0 && b > 0) { if (_pins[0] == LED_BUILTIN || _pins[1] == LED_BUILTIN) PolyBus::begin(_busPtr, _iType, _pins); } #endif - Bus::setBrightness(b); - PolyBus::setBrightness(_busPtr, _iType, b); + Bus::setBrightness(b, immediate); + PolyBus::setBrightness(_busPtr, _iType, b, immediate); } //If LEDs are skipped, it is possible to use the first as a status LED. @@ -515,9 +515,9 @@ void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c, int16_t cct) } } -void BusManager::setBrightness(uint8_t b) { +void BusManager::setBrightness(uint8_t b, bool immediate) { for (uint8_t i = 0; i < numBusses; i++) { - busses[i]->setBrightness(b); + busses[i]->setBrightness(b, immediate); } } diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index b6d79d07..4a8da605 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -108,7 +108,7 @@ class Bus { virtual void setStatusPixel(uint32_t c) {} virtual void setPixelColor(uint16_t pix, uint32_t c) = 0; virtual uint32_t getPixelColor(uint16_t pix) { return 0; } - virtual void setBrightness(uint8_t b) { _bri = b; }; + virtual void setBrightness(uint8_t b, bool immediate=false) { _bri = b; }; virtual void cleanup() = 0; virtual uint8_t getPins(uint8_t* pinArray) { return 0; } virtual uint16_t getLength() { return _len; } @@ -181,7 +181,7 @@ class BusDigital : public Bus { bool canShow(); - void setBrightness(uint8_t b); + void setBrightness(uint8_t b, bool immediate); void setStatusPixel(uint32_t c); @@ -345,7 +345,7 @@ class BusManager { void setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1); - void setBrightness(uint8_t b); + void setBrightness(uint8_t b, bool immediate=false); // immediate=true is for use in ABL, it applies brightness immediately (warning: inefficient) void setSegmentCCT(int16_t cct, bool allowWBCorrection = false); diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h index 56495aa8..b19997ce 100644 --- a/wled00/bus_wrapper.h +++ b/wled00/bus_wrapper.h @@ -806,101 +806,101 @@ class PolyBus { case I_SS_P98_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; } }; - static void setBrightness(void* busPtr, uint8_t busType, uint8_t b) { + static void setBrightness(void* busPtr, uint8_t busType, uint8_t b, bool immediate) { // immediate=true is for use in ABL, it applies brightness immediately (warning: inefficient) switch (busType) { case I_NONE: break; #ifdef ESP8266 - case I_8266_U0_NEO_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_U1_NEO_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_DM_NEO_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_BB_NEO_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_U0_NEO_4: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_U1_NEO_4: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_DM_NEO_4: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_BB_NEO_4: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_U0_400_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_U1_400_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_DM_400_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_BB_400_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_U0_TM1_4: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_U1_TM1_4: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_DM_TM1_4: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_BB_TM1_4: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_U0_TM2_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_U1_TM2_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_DM_TM2_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_BB_TM2_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_U0_UCS_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_U1_UCS_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_DM_UCS_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_BB_UCS_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_U0_UCS_4: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_U1_UCS_4: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_DM_UCS_4: (static_cast(busPtr))->SetLuminance(b); break; - case I_8266_BB_UCS_4: (static_cast(busPtr))->SetLuminance(b); break; + case I_8266_U0_NEO_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_U1_NEO_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_DM_NEO_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_BB_NEO_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_U0_NEO_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_U1_NEO_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_DM_NEO_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_BB_NEO_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_U0_400_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_U1_400_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_DM_400_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_BB_400_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_U0_TM1_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_U1_TM1_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_DM_TM1_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_BB_TM1_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_U0_TM2_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_U1_TM2_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_DM_TM2_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_BB_TM2_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_U0_UCS_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_U1_UCS_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_DM_UCS_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_BB_UCS_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_U0_UCS_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_U1_UCS_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_DM_UCS_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_8266_BB_UCS_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; #endif #ifdef ARDUINO_ARCH_ESP32 - case I_32_RN_NEO_3: (static_cast(busPtr))->SetLuminance(b); break; + case I_32_RN_NEO_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; #ifndef WLED_NO_I2S0_PIXELBUS - case I_32_I0_NEO_3: (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I0_NEO_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; #endif #ifndef WLED_NO_I2S1_PIXELBUS - case I_32_I1_NEO_3: (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I1_NEO_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; #endif -// case I_32_BB_NEO_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_32_RN_NEO_4: (static_cast(busPtr))->SetLuminance(b); break; +// case I_32_BB_NEO_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_32_RN_NEO_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; #ifndef WLED_NO_I2S0_PIXELBUS - case I_32_I0_NEO_4: (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I0_NEO_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; #endif #ifndef WLED_NO_I2S1_PIXELBUS - case I_32_I1_NEO_4: (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I1_NEO_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; #endif -// case I_32_BB_NEO_4: (static_cast(busPtr))->SetLuminance(b); break; - case I_32_RN_400_3: (static_cast(busPtr))->SetLuminance(b); break; +// case I_32_BB_NEO_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_32_RN_400_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; #ifndef WLED_NO_I2S0_PIXELBUS - case I_32_I0_400_3: (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I0_400_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; #endif #ifndef WLED_NO_I2S1_PIXELBUS - case I_32_I1_400_3: (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I1_400_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; #endif -// case I_32_BB_400_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_32_RN_TM1_4: (static_cast(busPtr))->SetLuminance(b); break; - case I_32_RN_TM2_3: (static_cast(busPtr))->SetLuminance(b); break; +// case I_32_BB_400_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_32_RN_TM1_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_32_RN_TM2_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; #ifndef WLED_NO_I2S0_PIXELBUS - case I_32_I0_TM1_4: (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I0_TM2_3: (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I0_TM1_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_32_I0_TM2_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; #endif #ifndef WLED_NO_I2S1_PIXELBUS - case I_32_I1_TM1_4: (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I1_TM2_3: (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I1_TM1_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_32_I1_TM2_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; #endif - case I_32_RN_UCS_3: (static_cast(busPtr))->SetLuminance(b); break; + case I_32_RN_UCS_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; #ifndef WLED_NO_I2S0_PIXELBUS - case I_32_I0_UCS_3: (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I0_UCS_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; #endif #ifndef WLED_NO_I2S1_PIXELBUS - case I_32_I1_UCS_3: (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I1_UCS_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; #endif -// case I_32_BB_UCS_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_32_RN_UCS_4: (static_cast(busPtr))->SetLuminance(b); break; +// case I_32_BB_UCS_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_32_RN_UCS_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; #ifndef WLED_NO_I2S0_PIXELBUS - case I_32_I0_UCS_4: (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I0_UCS_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; #endif #ifndef WLED_NO_I2S1_PIXELBUS - case I_32_I1_UCS_4: (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I1_UCS_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; #endif -// case I_32_BB_UCS_4: (static_cast(busPtr))->SetLuminance(b); break; +// case I_32_BB_UCS_4: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; #endif - case I_HS_DOT_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_SS_DOT_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_HS_LPD_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_SS_LPD_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_HS_LPO_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_SS_LPO_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_HS_WS1_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_SS_WS1_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_HS_P98_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_SS_P98_3: (static_cast(busPtr))->SetLuminance(b); break; + case I_HS_DOT_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_SS_DOT_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_HS_LPD_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_SS_LPD_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_HS_LPO_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_SS_LPO_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_HS_WS1_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_SS_WS1_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_HS_P98_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; + case I_SS_P98_3: (static_cast(busPtr))->SetLuminance(b); if (immediate) (static_cast(busPtr))->ApplyPostAdjustments(); break; } }; static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) { diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 0bdf5032..3eac1c56 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -197,6 +197,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { disablePullUp = !pull; JsonArray hw_btn_ins = btn_obj[F("ins")]; if (!hw_btn_ins.isNull()) { + for (uint8_t b = 0; b < WLED_MAX_BUTTONS; b++) { // deallocate existing button pins + pinManager.deallocatePin(btnPin[b], PinOwner::Button); // does nothing if trying to deallocate a pin with PinOwner != Button + } uint8_t s = 0; for (JsonObject btn : hw_btn_ins) { CJSON(buttonType[s], btn["type"]); @@ -264,6 +267,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { int hw_ir_pin = hw["ir"]["pin"] | -2; // 4 if (hw_ir_pin > -2) { + pinManager.deallocatePin(irPin, PinOwner::IR); if (pinManager.allocatePin(hw_ir_pin, false, PinOwner::IR)) { irPin = hw_ir_pin; } else { @@ -276,6 +280,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { JsonObject relay = hw[F("relay")]; int hw_relay_pin = relay["pin"] | -2; if (hw_relay_pin > -2) { + pinManager.deallocatePin(rlyPin, PinOwner::Relay); if (pinManager.allocatePin(hw_relay_pin,true, PinOwner::Relay)) { rlyPin = hw_relay_pin; pinMode(rlyPin, OUTPUT); @@ -443,6 +448,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"]); @@ -895,6 +907,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/const.h b/wled00/const.h index db14e972..91f3fde5 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 @@ -333,12 +348,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 @@ -371,7 +403,7 @@ #ifdef ESP8266 #define SETTINGS_STACK_BUF_SIZE 2048 #else -#define SETTINGS_STACK_BUF_SIZE 3096 +#define SETTINGS_STACK_BUF_SIZE 3608 // warning: quite a large value for stack #endif #ifdef WLED_USE_ETHERNET @@ -443,7 +475,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 803faeb6..ff41fa6e 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 4aa24750..0193305e 100644 --- a/wled00/data/index.css +++ b/wled00/data/index.css @@ -134,7 +134,7 @@ button { .off { color: var(--c-6) !important; - cursor: default !important; + /* cursor: default !important; */ } .top .icons, .bot .icons { @@ -1024,7 +1024,7 @@ textarea { width: 50px !important; } -.segname, .pname { +.segname, .pname, .bname { white-space: nowrap; text-align: center; overflow: hidden; @@ -1034,6 +1034,9 @@ textarea { max-width: 170px; position: relative; } +.bname { + padding: 0 24px; +} .segname .flr, .pname .flr { transform: rotate(0deg); diff --git a/wled00/data/index.htm b/wled00/data/index.htm index b171c658..cd8a92cf 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -380,8 +380,8 @@ -