mirror of
https://github.com/wled/WLED.git
synced 2025-07-24 11:16:37 +00:00
Merge branch '0_15' into blending-styles
This commit is contained in:
commit
f3891c305d
BIN
usermods/Internal_Temperature_v2/assets/screenshot_info.png
Normal file
BIN
usermods/Internal_Temperature_v2/assets/screenshot_info.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 132 KiB |
BIN
usermods/Internal_Temperature_v2/assets/screenshot_settings.png
Normal file
BIN
usermods/Internal_Temperature_v2/assets/screenshot_settings.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 59 KiB |
@ -1,17 +1,44 @@
|
|||||||
# Internal Temperature Usermod
|
# 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.
|
|
||||||
|
|
||||||
ESP8266 does not have a internal temp sensor
|

|
||||||
|
|
||||||
|
|
||||||
|
## 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
|
## 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
|
## Authors
|
||||||
Soeren Willrodt [@lost-hope](https://github.com/lost-hope)
|
- Soeren Willrodt [@lost-hope](https://github.com/lost-hope)
|
||||||
|
- Dimitry Zhemkov [@dima-zhemkov](https://github.com/dima-zhemkov)
|
||||||
Dimitry Zhemkov [@dima-zhemkov](https://github.com/dima-zhemkov)
|
- Adam Matthews [@adamsthws](https://github.com/adamsthws)
|
||||||
|
@ -6,14 +6,23 @@ class InternalTemperatureUsermod : public Usermod
|
|||||||
{
|
{
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static constexpr unsigned long minLoopInterval = 1000; // minimum allowable interval (ms)
|
||||||
unsigned long loopInterval = 10000;
|
unsigned long loopInterval = 10000;
|
||||||
unsigned long lastTime = 0;
|
unsigned long lastTime = 0;
|
||||||
bool isEnabled = false;
|
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 _name[];
|
||||||
static const char _enabled[];
|
static const char _enabled[];
|
||||||
static const char _loopInterval[];
|
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)
|
// 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
|
void publishMqtt(const char *state, bool retain = false); // example for publishing MQTT message
|
||||||
@ -32,6 +41,7 @@ public:
|
|||||||
|
|
||||||
lastTime = millis();
|
lastTime = millis();
|
||||||
|
|
||||||
|
// Measure the temperature
|
||||||
#ifdef ESP8266 // ESP8266
|
#ifdef ESP8266 // ESP8266
|
||||||
// does not seem possible
|
// does not seem possible
|
||||||
temperature = -1;
|
temperature = -1;
|
||||||
@ -41,6 +51,57 @@ public:
|
|||||||
temperature = roundf(temperatureRead() * 10) / 10;
|
temperature = roundf(temperatureRead() * 10) / 10;
|
||||||
#endif
|
#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
|
#ifndef WLED_DISABLE_MQTT
|
||||||
if (WLED_MQTT_CONNECTED)
|
if (WLED_MQTT_CONNECTED)
|
||||||
{
|
{
|
||||||
@ -80,15 +141,30 @@ public:
|
|||||||
JsonObject top = root.createNestedObject(FPSTR(_name));
|
JsonObject top = root.createNestedObject(FPSTR(_name));
|
||||||
top[FPSTR(_enabled)] = isEnabled;
|
top[FPSTR(_enabled)] = isEnabled;
|
||||||
top[FPSTR(_loopInterval)] = loopInterval;
|
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)
|
bool readFromConfig(JsonObject &root)
|
||||||
{
|
{
|
||||||
JsonObject top = root[FPSTR(_name)];
|
JsonObject top = root[FPSTR(_name)];
|
||||||
bool configComplete = !top.isNull();
|
bool configComplete = !top.isNull();
|
||||||
configComplete &= getJsonValue(top[FPSTR(_enabled)], isEnabled);
|
configComplete &= getJsonValue(top[FPSTR(_enabled)], isEnabled);
|
||||||
configComplete &= getJsonValue(top[FPSTR(_loopInterval)], loopInterval);
|
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;
|
return configComplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,6 +177,8 @@ public:
|
|||||||
const char InternalTemperatureUsermod::_name[] PROGMEM = "Internal Temperature";
|
const char InternalTemperatureUsermod::_name[] PROGMEM = "Internal Temperature";
|
||||||
const char InternalTemperatureUsermod::_enabled[] PROGMEM = "Enabled";
|
const char InternalTemperatureUsermod::_enabled[] PROGMEM = "Enabled";
|
||||||
const char InternalTemperatureUsermod::_loopInterval[] PROGMEM = "Loop Interval";
|
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)
|
void InternalTemperatureUsermod::publishMqtt(const char *state, bool retain)
|
||||||
{
|
{
|
||||||
|
@ -336,14 +336,14 @@ static const char _data_FX_MODE_DYNAMIC_SMOOTH[] PROGMEM = "Dynamic Smooth@!,!;;
|
|||||||
*/
|
*/
|
||||||
uint16_t mode_breath(void) {
|
uint16_t mode_breath(void) {
|
||||||
unsigned var = 0;
|
unsigned var = 0;
|
||||||
unsigned counter = (strip.now * ((SEGMENT.speed >> 3) +10));
|
unsigned counter = (strip.now * ((SEGMENT.speed >> 3) +10)) & 0xFFFFU;
|
||||||
counter = ((counter >> 2) + (counter >> 4)) & 0xFFFFU; //0-16384 + 0-2048
|
counter = (counter >> 2) + (counter >> 4); //0-16384 + 0-2048
|
||||||
if (counter < 16384) {
|
if (counter < 16384) {
|
||||||
if (counter > 8192) counter = 8192 - (counter - 8192);
|
if (counter > 8192) counter = 8192 - (counter - 8192);
|
||||||
var = sin16(counter) / 103; //close to parabolic in range 0-8192, max val. 23170
|
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++) {
|
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));
|
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) {
|
uint16_t mode_fade(void) {
|
||||||
unsigned counter = (strip.now * ((SEGMENT.speed >> 3) +10));
|
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++) {
|
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));
|
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
|
/ Oscillating bars of color, updated with standard framerate
|
||||||
*/
|
*/
|
||||||
uint16_t mode_oscillate(void) {
|
uint16_t mode_oscillate(void) {
|
||||||
unsigned numOscillators = 3;
|
constexpr unsigned numOscillators = 3;
|
||||||
unsigned dataSize = sizeof(oscillator) * numOscillators;
|
constexpr unsigned dataSize = sizeof(oscillator) * numOscillators;
|
||||||
|
|
||||||
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
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);
|
unsigned counter = strip.now * ((SEGMENT.speed >> 2) +8);
|
||||||
uint16_t in = counter * SEGLEN >> 16;
|
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
|
// fade all leds to colors[1] in LEDs one step
|
||||||
for (int i = 0; i < SEGLEN; i++) {
|
for (int i = 0; i < SEGLEN; i++) {
|
||||||
if (random8() <= 255 - SEGMENT.intensity) {
|
if (random8() <= 255 - SEGMENT.intensity) {
|
||||||
byte meteorTrailDecay = 162 + random8(92);
|
int meteorTrailDecay = 128 + random8(127);
|
||||||
trail[i] = scale8(trail[i], meteorTrailDecay);
|
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);
|
SEGMENT.setPixelColor(i, col);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw meteor
|
// draw meteor
|
||||||
for (unsigned j = 0; j < meteorSize; j++) {
|
for (int j = 0; j < meteorSize; j++) {
|
||||||
unsigned index = in + j;
|
int index = (in + j) % SEGLEN;
|
||||||
if (index >= SEGLEN) {
|
int idx = 255;
|
||||||
index -= SEGLEN;
|
int i = trail[index] = max;
|
||||||
|
if (!SEGMENT.check1) {
|
||||||
|
i = map(index,0,SEGLEN,0,max);
|
||||||
|
idx = 0;
|
||||||
}
|
}
|
||||||
trail[index] = max;
|
uint32_t col = SEGMENT.color_from_palette(i, false, false, idx, 255); // full brightness
|
||||||
uint32_t col = SEGMENT.check1 ? SEGMENT.color_from_palette(index, true, false, 0, trail[index]) : SEGMENT.color_from_palette(trail[index], false, true, 255);
|
|
||||||
SEGMENT.setPixelColor(index, col);
|
SEGMENT.setPixelColor(index, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
return FRAMETIME;
|
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
|
// smooth meteor effect
|
||||||
|
@ -2843,7 +2843,7 @@ function search(field, listId = null) {
|
|||||||
if (gId("filters").querySelectorAll("input[type=checkbox]:checked").length) return;
|
if (gId("filters").querySelectorAll("input[type=checkbox]:checked").length) return;
|
||||||
|
|
||||||
// filter list items but leave (Default & Solid) always visible
|
// 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)=>{
|
listItems.forEach((listItem,i)=>{
|
||||||
if (listId!=='pcont' && i===0) return;
|
if (listId!=='pcont' && i===0) return;
|
||||||
const listItemName = listItem.querySelector('.lstIname').innerText.toUpperCase();
|
const listItemName = listItem.querySelector('.lstIname').innerText.toUpperCase();
|
||||||
|
@ -290,7 +290,7 @@
|
|||||||
// do we have a led count field
|
// do we have a led count field
|
||||||
if (nm=="LC") {
|
if (nm=="LC") {
|
||||||
let c = parseInt(LC.value,10); //get LED count
|
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
|
if (!customStarts || !startsDirty[n]) gId("ls"+n).value=sLC; //update start value
|
||||||
gId("ls"+n).disabled = !customStarts; //enable/disable field editing
|
gId("ls"+n).disabled = !customStarts; //enable/disable field editing
|
||||||
if (c) {
|
if (c) {
|
||||||
@ -864,7 +864,7 @@ Swap: <select id="xw${s}" name="XW${s}">
|
|||||||
<hr class="sml">
|
<hr class="sml">
|
||||||
<h3>Defaults</h3>
|
<h3>Defaults</h3>
|
||||||
Turn LEDs on after power up/reset: <input type="checkbox" name="BO"><br>
|
Turn LEDs on after power up/reset: <input type="checkbox" name="BO"><br>
|
||||||
Default brightness: <input name="CA" type="number" class="m" min="0" max="255" required> (0-255)<br><br>
|
Default brightness: <input name="CA" type="number" class="m" min="1" max="255" required> (1-255)<br><br>
|
||||||
Apply preset <input name="BP" type="number" class="m" min="0" max="250" required> at boot (0 uses defaults)
|
Apply preset <input name="BP" type="number" class="m" min="0" max="250" required> at boot (0 uses defaults)
|
||||||
<br><br>
|
<br><br>
|
||||||
Use Gamma correction for color: <input type="checkbox" name="GC"> (strongly recommended)<br>
|
Use Gamma correction for color: <input type="checkbox" name="GC"> (strongly recommended)<br>
|
||||||
|
@ -84,11 +84,11 @@ static void changeEffect(uint8_t fx)
|
|||||||
for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
|
for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
|
||||||
Segment& seg = strip.getSegment(i);
|
Segment& seg = strip.getSegment(i);
|
||||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||||
strip.setMode(i, fx);
|
seg.setMode(fx);
|
||||||
}
|
}
|
||||||
setValuesFromFirstSelectedSeg();
|
setValuesFromFirstSelectedSeg();
|
||||||
} else {
|
} else {
|
||||||
strip.setMode(strip.getMainSegmentId(), fx);
|
strip.getSegment(strip.getMainSegmentId()).setMode(fx);
|
||||||
setValuesFromMainSeg();
|
setValuesFromMainSeg();
|
||||||
}
|
}
|
||||||
stateChanged = true;
|
stateChanged = true;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user