diff --git a/usermods/Internal_Temperature_v2/assets/screenshot_info.png b/usermods/Internal_Temperature_v2/assets/screenshot_info.png new file mode 100644 index 000000000..7990f2e8f Binary files /dev/null and b/usermods/Internal_Temperature_v2/assets/screenshot_info.png differ diff --git a/usermods/Internal_Temperature_v2/assets/screenshot_settings.png b/usermods/Internal_Temperature_v2/assets/screenshot_settings.png new file mode 100644 index 000000000..d97dc2bea Binary files /dev/null and b/usermods/Internal_Temperature_v2/assets/screenshot_settings.png differ diff --git a/usermods/Internal_Temperature_v2/readme.md b/usermods/Internal_Temperature_v2/readme.md index 58a9e1939..d574f3abf 100644 --- a/usermods/Internal_Temperature_v2/readme.md +++ b/usermods/Internal_Temperature_v2/readme.md @@ -1,17 +1,44 @@ # Internal Temperature Usermod -This usermod adds the temperature readout to the Info tab and also publishes that over the topic `mcutemp` topic. -## Important -A shown temp of 53,33°C might indicate that the internal temp is not supported. +![Screenshot of WLED info page](assets/screenshot_info.png) -ESP8266 does not have a internal temp sensor +![Screenshot of WLED usermod settings page](assets/screenshot_settings.png) + + +## Features + - 🌡️ Adds the internal temperature readout of the chip to the `Info` tab + - 🥵 High temperature indicator/action. (Configurable threshold and preset) + - 📣 Publishes the internal temperature over the MQTT topic: `mcutemp` + + +## Use Examples +- Warn of excessive/damaging temperatures by the triggering of a 'warning' preset +- Activate a cooling fan (when used with the multi-relay usermod) + + +## Compatibility +- A shown temp of 53,33°C might indicate that the internal temp is not supported +- ESP8266 does not have a internal temp sensor -> Disabled (Indicated with a readout of '-1') +- ESP32S2 seems to crash on reading the sensor -> Disabled (Indicated with a readout of '-1') -ESP32S2 seems to crash on reading the sensor -> disabled ## Installation -Add a build flag `-D USERMOD_INTERNAL_TEMPERATURE` to your `platformio.ini` (or `platformio_override.ini`). +- Add a build flag `-D USERMOD_INTERNAL_TEMPERATURE` to your `platformio.ini` (or `platformio_override.ini`). + + +## 📝 Change Log + +2024-06-26 + +- Added "high-temperature-indication" feature +- Documentation updated + +2023-09-01 + +* "Internal Temperature" usermod created + ## Authors -Soeren Willrodt [@lost-hope](https://github.com/lost-hope) - -Dimitry Zhemkov [@dima-zhemkov](https://github.com/dima-zhemkov) \ No newline at end of file +- Soeren Willrodt [@lost-hope](https://github.com/lost-hope) +- Dimitry Zhemkov [@dima-zhemkov](https://github.com/dima-zhemkov) +- Adam Matthews [@adamsthws](https://github.com/adamsthws) diff --git a/usermods/Internal_Temperature_v2/usermod_internal_temperature.h b/usermods/Internal_Temperature_v2/usermod_internal_temperature.h index 3989e7668..2236bfeab 100644 --- a/usermods/Internal_Temperature_v2/usermod_internal_temperature.h +++ b/usermods/Internal_Temperature_v2/usermod_internal_temperature.h @@ -6,14 +6,23 @@ class InternalTemperatureUsermod : public Usermod { private: + static constexpr unsigned long minLoopInterval = 1000; // minimum allowable interval (ms) unsigned long loopInterval = 10000; unsigned long lastTime = 0; bool isEnabled = false; - float temperature = 0; + float temperature = 0.0f; + uint8_t previousPlaylist = 0; // Stores the playlist that was active before high-temperature activation + uint8_t previousPreset = 0; // Stores the preset that was active before high-temperature activation + uint8_t presetToActivate = 0; // Preset to activate when temp goes above threshold (0 = disabled) + float activationThreshold = 95.0f; // Temperature threshold to trigger high-temperature actions + float resetMargin = 2.0f; // Margin below the activation threshold (Prevents frequent toggling when close to threshold) + bool isAboveThreshold = false; // Flag to track if the high temperature preset is currently active static const char _name[]; static const char _enabled[]; static const char _loopInterval[]; + static const char _activationThreshold[]; + static const char _presetToActivate[]; // any private methods should go here (non-inline method should be defined out of class) void publishMqtt(const char *state, bool retain = false); // example for publishing MQTT message @@ -32,6 +41,7 @@ public: lastTime = millis(); +// Measure the temperature #ifdef ESP8266 // ESP8266 // does not seem possible temperature = -1; @@ -41,6 +51,57 @@ public: temperature = roundf(temperatureRead() * 10) / 10; #endif + // Check if temperature has exceeded the activation threshold + if (temperature >= activationThreshold) { + // Update the state flag if not already set + if (!isAboveThreshold) { + isAboveThreshold = true; + } + // Check if a 'high temperature' preset is configured and it's not already active + if (presetToActivate != 0 && currentPreset != presetToActivate) { + // If a playlist is active, store it for reactivation later + if (currentPlaylist > 0) { + previousPlaylist = currentPlaylist; + } + // If a preset is active, store it for reactivation later + else if (currentPreset > 0) { + previousPreset = currentPreset; + // If no playlist or preset is active, save current state for reactivation later + } else { + saveTemporaryPreset(); + } + // Activate the 'high temperature' preset + applyPreset(presetToActivate); + } + } + // Check if temperature is back below the threshold + else if (temperature <= (activationThreshold - resetMargin)) { + // Update the state flag if not already set + if (isAboveThreshold){ + isAboveThreshold = false; + } + // Check if the 'high temperature' preset is active + if (currentPreset == presetToActivate) { + // Check if a previous playlist was stored + if (previousPlaylist > 0) { + // Reactivate the stored playlist + applyPreset(previousPlaylist); + // Clear the stored playlist + previousPlaylist = 0; + } + // Check if a previous preset was stored + else if (previousPreset > 0) { + // Reactivate the stored preset + applyPreset(previousPreset); + // Clear the stored preset + previousPreset = 0; + // If no previous playlist or preset was stored, revert to the stored state + } else { + applyTemporaryPreset(); + } + } + } + #ifndef WLED_DISABLE_MQTT if (WLED_MQTT_CONNECTED) { @@ -80,15 +141,30 @@ public: JsonObject top = root.createNestedObject(FPSTR(_name)); top[FPSTR(_enabled)] = isEnabled; top[FPSTR(_loopInterval)] = loopInterval; + top[FPSTR(_activationThreshold)] = activationThreshold; + top[FPSTR(_presetToActivate)] = presetToActivate; } + // Append useful info to the usermod settings gui + void appendConfigData() + { + // Display 'ms' next to the 'Loop Interval' setting + oappend(SET_F("addInfo('Internal Temperature:Loop Interval', 1, 'ms');")); + // Display '°C' next to the 'Activation Threshold' setting + oappend(SET_F("addInfo('Internal Temperature:Activation Threshold', 1, '°C');")); + // Display '0 = Disabled' next to the 'Preset To Activate' setting + oappend(SET_F("addInfo('Internal Temperature:Preset To Activate', 1, '0 = unused');")); + } + bool readFromConfig(JsonObject &root) { JsonObject top = root[FPSTR(_name)]; bool configComplete = !top.isNull(); configComplete &= getJsonValue(top[FPSTR(_enabled)], isEnabled); configComplete &= getJsonValue(top[FPSTR(_loopInterval)], loopInterval); - + loopInterval = max(loopInterval, minLoopInterval); // Makes sure the loop interval isn't too small. + configComplete &= getJsonValue(top[FPSTR(_presetToActivate)], presetToActivate); + configComplete &= getJsonValue(top[FPSTR(_activationThreshold)], activationThreshold); return configComplete; } @@ -101,6 +177,8 @@ public: const char InternalTemperatureUsermod::_name[] PROGMEM = "Internal Temperature"; const char InternalTemperatureUsermod::_enabled[] PROGMEM = "Enabled"; const char InternalTemperatureUsermod::_loopInterval[] PROGMEM = "Loop Interval"; +const char InternalTemperatureUsermod::_activationThreshold[] PROGMEM = "Activation Threshold"; +const char InternalTemperatureUsermod::_presetToActivate[] PROGMEM = "Preset To Activate"; void InternalTemperatureUsermod::publishMqtt(const char *state, bool retain) { diff --git a/wled00/FX.cpp b/wled00/FX.cpp index c50b9383f..078b87101 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -336,14 +336,14 @@ static const char _data_FX_MODE_DYNAMIC_SMOOTH[] PROGMEM = "Dynamic Smooth@!,!;; */ uint16_t mode_breath(void) { unsigned var = 0; - unsigned counter = (strip.now * ((SEGMENT.speed >> 3) +10)); - counter = ((counter >> 2) + (counter >> 4)) & 0xFFFFU; //0-16384 + 0-2048 + unsigned counter = (strip.now * ((SEGMENT.speed >> 3) +10)) & 0xFFFFU; + counter = (counter >> 2) + (counter >> 4); //0-16384 + 0-2048 if (counter < 16384) { if (counter > 8192) counter = 8192 - (counter - 8192); var = sin16(counter) / 103; //close to parabolic in range 0-8192, max val. 23170 } - uint8_t lum = 30 + var; + unsigned lum = 30 + var; for (int i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum)); } @@ -358,7 +358,7 @@ static const char _data_FX_MODE_BREATH[] PROGMEM = "Breathe@!;!,!;!;01"; */ uint16_t mode_fade(void) { unsigned counter = (strip.now * ((SEGMENT.speed >> 3) +10)); - uint8_t lum = triwave16(counter) >> 8; + unsigned lum = triwave16(counter) >> 8; for (int i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum)); @@ -1779,8 +1779,8 @@ typedef struct Oscillator { / Oscillating bars of color, updated with standard framerate */ uint16_t mode_oscillate(void) { - unsigned numOscillators = 3; - unsigned dataSize = sizeof(oscillator) * numOscillators; + constexpr unsigned numOscillators = 3; + constexpr unsigned dataSize = sizeof(oscillator) * numOscillators; if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed @@ -2347,31 +2347,41 @@ uint16_t mode_meteor() { unsigned counter = strip.now * ((SEGMENT.speed >> 2) +8); uint16_t in = counter * SEGLEN >> 16; - const int max = SEGMENT.palette==5 || !SEGMENT.check1 ? 240 : 255; + const int max = SEGMENT.palette==5 ? 239 : 255; // "* Colors only" palette blends end with start // fade all leds to colors[1] in LEDs one step for (int i = 0; i < SEGLEN; i++) { if (random8() <= 255 - SEGMENT.intensity) { - byte meteorTrailDecay = 162 + random8(92); + int meteorTrailDecay = 128 + random8(127); trail[i] = scale8(trail[i], meteorTrailDecay); - uint32_t col = SEGMENT.check1 ? SEGMENT.color_from_palette(i, true, false, 0, trail[i]) : SEGMENT.color_from_palette(trail[i], false, true, 255); + int index = trail[i]; + int idx = 255; + int bri = SEGMENT.palette==35 || SEGMENT.palette==36 ? 255 : trail[i]; + if (!SEGMENT.check1) { + idx = 0; + index = map(i,0,SEGLEN,0,max); + bri = trail[i]; + } + uint32_t col = SEGMENT.color_from_palette(index, false, false, idx, bri); // full brightness for Fire SEGMENT.setPixelColor(i, col); } } // draw meteor - for (unsigned j = 0; j < meteorSize; j++) { - unsigned index = in + j; - if (index >= SEGLEN) { - index -= SEGLEN; + for (int j = 0; j < meteorSize; j++) { + int index = (in + j) % SEGLEN; + int idx = 255; + int i = trail[index] = max; + if (!SEGMENT.check1) { + i = map(index,0,SEGLEN,0,max); + idx = 0; } - trail[index] = max; - uint32_t col = SEGMENT.check1 ? SEGMENT.color_from_palette(index, true, false, 0, trail[index]) : SEGMENT.color_from_palette(trail[index], false, true, 255); + uint32_t col = SEGMENT.color_from_palette(i, false, false, idx, 255); // full brightness SEGMENT.setPixelColor(index, col); } return FRAMETIME; } -static const char _data_FX_MODE_METEOR[] PROGMEM = "Meteor@!,Trail,,,,Gradient;;!;1"; +static const char _data_FX_MODE_METEOR[] PROGMEM = "Meteor@!,Trail,,,,Gradient;!;!;1"; // smooth meteor effect diff --git a/wled00/data/index.js b/wled00/data/index.js index 7f4afbc04..4bb655e7a 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -2843,7 +2843,7 @@ function search(field, listId = null) { if (gId("filters").querySelectorAll("input[type=checkbox]:checked").length) return; // filter list items but leave (Default & Solid) always visible - const listItems = gId("fxlist").querySelectorAll('.lstI'); + const listItems = gId(listId).querySelectorAll('.lstI'); listItems.forEach((listItem,i)=>{ if (listId!=='pcont' && i===0) return; const listItemName = listItem.querySelector('.lstIname').innerText.toUpperCase(); diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index a3f749bc6..03a4e8464 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -290,7 +290,7 @@ // do we have a led count field if (nm=="LC") { let c = parseInt(LC.value,10); //get LED count - if (c > 300 && i < 8) maxB = oMaxB - max(maxD-7,0); //TODO: hard limit for buses when using ESP32 parallel I2S + if (c > 300 && i < 8) maxB = oMaxB - Math.max(maxD-7,0); //TODO: hard limit for buses when using ESP32 parallel I2S if (!customStarts || !startsDirty[n]) gId("ls"+n).value=sLC; //update start value gId("ls"+n).disabled = !customStarts; //enable/disable field editing if (c) { @@ -864,7 +864,7 @@ Swap:
- Default brightness: (0-255)

+ Default brightness: (1-255)

Apply preset at boot (0 uses defaults)

Use Gamma correction for color: (strongly recommended)
diff --git a/wled00/ir.cpp b/wled00/ir.cpp index e475198f6..9e1974366 100644 --- a/wled00/ir.cpp +++ b/wled00/ir.cpp @@ -84,11 +84,11 @@ static void changeEffect(uint8_t fx) for (unsigned i = 0; i < strip.getSegmentsNum(); i++) { Segment& seg = strip.getSegment(i); if (!seg.isActive() || !seg.isSelected()) continue; - strip.setMode(i, fx); + seg.setMode(fx); } setValuesFromFirstSelectedSeg(); } else { - strip.setMode(strip.getMainSegmentId(), fx); + strip.getSegment(strip.getMainSegmentId()).setMode(fx); setValuesFromMainSeg(); } stateChanged = true;