Merge remote-tracking branch 'upstream/main' into usermod-libs

This commit is contained in:
Will Miles
2025-01-31 02:51:56 +00:00
58 changed files with 2263 additions and 1358 deletions

View File

@@ -9,7 +9,7 @@ Very loosely based on the existing usermod "seven segment display".
Add the compile-time option `-D USERMOD_SSDR` to your `platformio.ini` (or `platformio_override.ini`) or use `#define USERMOD_SSDR` in `my_config.h`.
For the auto brightness option, the usermod SN_Photoresistor has to be installed as well. See SN_Photoresistor/readme.md for instructions.
For the auto brightness option, the usermod SN_Photoresistor or BH1750_V2 has to be installed as well. See SN_Photoresistor/readme.md or BH1750_V2/readme.md for instructions.
## Settings
All settings can be controlled via the usermod settings page.
@@ -28,10 +28,10 @@ Enables the blinking colon(s) if they are defined
Shows the leading zero of the hour if it exists (i.e. shows `07` instead of `7`)
### enable-auto-brightness
Enables the auto brightness feature. Can be used only when the usermod SN_Photoresistor is installed.
Enables the auto brightness feature. Can be used only when the usermods SN_Photoresistor or BH1750_V2 are installed.
### auto-brightness-min / auto-brightness-max
The lux value calculated from usermod SN_Photoresistor will be mapped to the values defined here.
The lux value calculated from usermod SN_Photoresistor or BH1750_V2 will be mapped to the values defined here.
The mapping, 0 - 1000 lux, will be mapped to auto-brightness-min and auto-brightness-max
WLED current protection will override the calculated value if it is too high.

View File

@@ -95,6 +95,11 @@ private:
#else
void* ptr = nullptr;
#endif
#ifdef USERMOD_BH1750
Usermod_BH1750* bh1750 = nullptr;
#else
void* bh1750 = nullptr;
#endif
void _overlaySevenSegmentDraw() {
int displayMaskLen = static_cast<int>(umSSDRDisplayMask.length());
@@ -385,6 +390,9 @@ public:
#ifdef USERMOD_SN_PHOTORESISTOR
ptr = (Usermod_SN_Photoresistor*) UsermodManager::lookup(USERMOD_ID_SN_PHOTORESISTOR);
#endif
#ifdef USERMOD_BH1750
bh1750 = (Usermod_BH1750*) UsermodManager::lookup(USERMOD_ID_BH1750);
#endif
DEBUG_PRINTLN(F("Setup done"));
}
@@ -408,6 +416,20 @@ public:
umSSDRLastRefresh = millis();
}
#endif
#ifdef USERMOD_BH1750
if(bri != 0 && umSSDREnableLDR && (millis() - umSSDRLastRefresh > umSSDRResfreshTime)) {
if (bh1750 != nullptr) {
float lux = bh1750->getIlluminance();
uint16_t brightness = map(lux, 0, 1000, umSSDRBrightnessMin, umSSDRBrightnessMax);
if (bri != brightness) {
DEBUG_PRINTF("Adjusting brightness based on lux value: %.2f lx, new brightness: %d\n", lux, brightness);
bri = brightness;
stateUpdated(1);
}
}
umSSDRLastRefresh = millis();
}
#endif
}
void handleOverlayDraw() {

View File

@@ -1,111 +0,0 @@
/*
* This file allows you to add own functionality to WLED more easily
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
* EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in wled_eeprom.h)
* bytes 2400+ are currently ununsed, but might be used for future wled features
*/
//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t)
byte wipeState = 0; //0: inactive 1: wiping 2: solid
unsigned long timeStaticStart = 0;
uint16_t previousUserVar0 = 0;
//comment this out if you want the turn off effect to be just fading out instead of reverse wipe
#define STAIRCASE_WIPE_OFF
//gets called once at boot. Do all initialization that doesn't depend on network here
void userSetup()
{
//setup PIR sensor here, if needed
}
//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
void userConnected()
{
}
//loop. You can use "if (WLED_CONNECTED)" to check for successful connection
void userLoop()
{
//userVar0 (U0 in HTTP API):
//has to be set to 1 if movement is detected on the PIR that is the same side of the staircase as the ESP8266
//has to be set to 2 if movement is detected on the PIR that is the opposite side
//can be set to 0 if no movement is detected. Otherwise LEDs will turn off after a configurable timeout (userVar1 seconds)
if (userVar0 > 0)
{
if ((previousUserVar0 == 1 && userVar0 == 2) || (previousUserVar0 == 2 && userVar0 == 1)) wipeState = 3; //turn off if other PIR triggered
previousUserVar0 = userVar0;
if (wipeState == 0) {
startWipe();
wipeState = 1;
} else if (wipeState == 1) { //wiping
uint32_t cycleTime = 360 + (255 - effectSpeed)*75; //this is how long one wipe takes (minus 25 ms to make sure we switch in time)
if (millis() + strip.timebase > (cycleTime - 25)) { //wipe complete
effectCurrent = FX_MODE_STATIC;
timeStaticStart = millis();
colorUpdated(CALL_MODE_NOTIFICATION);
wipeState = 2;
}
} else if (wipeState == 2) { //static
if (userVar1 > 0) //if U1 is not set, the light will stay on until second PIR or external command is triggered
{
if (millis() - timeStaticStart > userVar1*1000) wipeState = 3;
}
} else if (wipeState == 3) { //switch to wipe off
#ifdef STAIRCASE_WIPE_OFF
effectCurrent = FX_MODE_COLOR_WIPE;
strip.timebase = 360 + (255 - effectSpeed)*75 - millis(); //make sure wipe starts fully lit
colorUpdated(CALL_MODE_NOTIFICATION);
wipeState = 4;
#else
turnOff();
#endif
} else { //wiping off
if (millis() + strip.timebase > (725 + (255 - effectSpeed)*150)) turnOff(); //wipe complete
}
} else {
wipeState = 0; //reset for next time
if (previousUserVar0) {
#ifdef STAIRCASE_WIPE_OFF
userVar0 = previousUserVar0;
wipeState = 3;
#else
turnOff();
#endif
}
previousUserVar0 = 0;
}
}
void startWipe()
{
bri = briLast; //turn on
transitionDelayTemp = 0; //no transition
effectCurrent = FX_MODE_COLOR_WIPE;
strip.resetTimebase(); //make sure wipe starts from beginning
//set wipe direction
Segment& seg = strip.getSegment(0);
bool doReverse = (userVar0 == 2);
seg.setOption(1, doReverse);
colorUpdated(CALL_MODE_NOTIFICATION);
}
void turnOff()
{
#ifdef STAIRCASE_WIPE_OFF
transitionDelayTemp = 0; //turn off immediately after wipe completed
#else
transitionDelayTemp = 4000; //fade out slowly
#endif
bri = 0;
stateUpdated(CALL_MODE_NOTIFICATION);
wipeState = 0;
userVar0 = 0;
previousUserVar0 = 0;
}

View File

@@ -0,0 +1,6 @@
{
"name:": "usermod_v2_RF433",
"dependencies": {
"sui77/rc-switch":"2.6.4"
}
}

View File

@@ -0,0 +1,18 @@
# RF433 remote usermod
Usermod for controlling WLED using a generic 433 / 315MHz remote and simple 3-pin receiver
See <https://github.com/sui77/rc-switch/> for compatibility details
## Build
- Create a `platformio_override.ini` file at the root of the wled source directory if not already present
- Copy the `433MHz RF remote example for esp32dev` section from `platformio_override.sample.ini` into it
- Duplicate/adjust for other boards
## Usage
- Connect receiver to a free pin
- Set pin in Config->Usermods
- Info pane will show the last received button code
- Upload the remote433.json sample file in this folder to the ESP with the file editor at [http://\[wled-ip\]/edit](http://ip/edit)
- Edit as necessary, the key is the button number retrieved from the info pane, and the "cmd" can be either an [HTTP API](https://kno.wled.ge/interfaces/http-api/) or a [JSON API](https://kno.wled.ge/interfaces/json-api/) command.

View File

@@ -0,0 +1,34 @@
{
"13985576": {
"cmnt": "Toggle Power using HTTP API",
"cmd": "T=2"
},
"3670817": {
"cmnt": "Force Power ON using HTTP API",
"cmd": "T=1"
},
"13985572": {
"cmnt": "Set brightness to 200 using JSON API",
"cmd": {"bri":200}
},
"3670818": {
"cmnt": "Run Preset 1 using JSON API",
"cmd": {"ps":1}
},
"13985570": {
"cmnt": "Increase brightness by 40 using HTTP API",
"cmd": "A=~40"
},
"13985569": {
"cmnt": "Decrease brightness by 40 using HTTP API",
"cmd": "A=~-40"
},
"7608836": {
"cmnt": "Start 1min timer using JSON API",
"cmd": {"nl":{"on":true,"dur":1,"mode":0}}
},
"7608840": {
"cmnt": "Select random effect on all segments using JSON API",
"cmd": {"seg":{"fx":"r"}}
}
}

View File

@@ -0,0 +1,183 @@
#include "wled.h"
#include "Arduino.h"
#include <RCSwitch.h>
#define RF433_BUSWAIT_TIMEOUT 24
class RF433Usermod : public Usermod
{
private:
RCSwitch mySwitch = RCSwitch();
unsigned long lastCommand = 0;
unsigned long lastTime = 0;
bool modEnabled = true;
int8_t receivePin = -1;
static const char _modName[];
static const char _modEnabled[];
static const char _receivePin[];
bool initDone = false;
public:
void setup()
{
mySwitch.disableReceive();
if (modEnabled)
{
mySwitch.enableReceive(receivePin);
}
initDone = true;
}
/*
* connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces
*/
void connected()
{
}
void loop()
{
if (!modEnabled || strip.isUpdating())
return;
if (mySwitch.available())
{
unsigned long receivedCommand = mySwitch.getReceivedValue();
mySwitch.resetAvailable();
// Discard duplicates, limit long press repeat
if (lastCommand == receivedCommand && millis() - lastTime < 800)
return;
lastCommand = receivedCommand;
lastTime = millis();
DEBUG_PRINT(F("RF433 Receive: "));
DEBUG_PRINTLN(receivedCommand);
if(!remoteJson433(receivedCommand))
DEBUG_PRINTLN(F("RF433: unknown button"));
}
}
// Add last received button to info pane
void addToJsonInfo(JsonObject &root)
{
if (!initDone)
return; // prevent crash on boot applyPreset()
JsonObject user = root["u"];
if (user.isNull())
user = root.createNestedObject("u");
JsonArray switchArr = user.createNestedArray("RF433 Last Received"); // name
switchArr.add(lastCommand);
}
void addToConfig(JsonObject &root)
{
JsonObject top = root.createNestedObject(FPSTR(_modName)); // usermodname
top[FPSTR(_modEnabled)] = modEnabled;
JsonArray pinArray = top.createNestedArray("pin");
pinArray.add(receivePin);
DEBUG_PRINTLN(F(" config saved."));
}
bool readFromConfig(JsonObject &root)
{
JsonObject top = root[FPSTR(_modName)];
if (top.isNull())
{
DEBUG_PRINT(FPSTR(_modName));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
getJsonValue(top[FPSTR(_modEnabled)], modEnabled);
getJsonValue(top["pin"][0], receivePin);
DEBUG_PRINTLN(F("config (re)loaded."));
// Redo init on update
if(initDone)
setup();
return true;
}
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
* This could be used in the future for the system to determine whether your usermod is installed.
*/
uint16_t getId()
{
return USERMOD_ID_RF433;
}
// this function follows the same principle as decodeIRJson() / remoteJson()
bool remoteJson433(int button)
{
char objKey[14];
bool parsed = false;
if (!requestJSONBufferLock(22)) return false;
sprintf_P(objKey, PSTR("\"%d\":"), button);
unsigned long start = millis();
while (strip.isUpdating() && millis()-start < RF433_BUSWAIT_TIMEOUT) yield(); // wait for strip to finish updating, accessing FS during sendout causes glitches
// attempt to read command from remote.json
readObjectFromFile(PSTR("/remote433.json"), objKey, pDoc);
JsonObject fdo = pDoc->as<JsonObject>();
if (fdo.isNull()) {
// the received button does not exist
releaseJSONBufferLock();
return parsed;
}
String cmdStr = fdo["cmd"].as<String>();
JsonObject jsonCmdObj = fdo["cmd"]; //object
if (jsonCmdObj.isNull()) // we could also use: fdo["cmd"].is<String>()
{
// HTTP API command
String apireq = "win"; apireq += '&'; // reduce flash string usage
if (!cmdStr.startsWith(apireq)) cmdStr = apireq + cmdStr; // if no "win&" prefix
if (!irApplyToAllSelected && cmdStr.indexOf(F("SS="))<0) {
char tmp[10];
sprintf_P(tmp, PSTR("&SS=%d"), strip.getMainSegmentId());
cmdStr += tmp;
}
fdo.clear(); // clear JSON buffer (it is no longer needed)
handleSet(nullptr, cmdStr, false); // no stateUpdated() call here
stateUpdated(CALL_MODE_BUTTON);
parsed = true;
} else {
// command is JSON object
if (jsonCmdObj[F("psave")].isNull())
deserializeState(jsonCmdObj, CALL_MODE_BUTTON_PRESET);
else {
uint8_t psave = jsonCmdObj[F("psave")].as<int>();
char pname[33];
sprintf_P(pname, PSTR("IR Preset %d"), psave);
fdo.clear();
if (psave > 0 && psave < 251) savePreset(psave, pname, fdo);
}
parsed = true;
}
releaseJSONBufferLock();
return parsed;
}
};
const char RF433Usermod::_modName[] PROGMEM = "RF433 Remote";
const char RF433Usermod::_modEnabled[] PROGMEM = "Enabled";
const char RF433Usermod::_receivePin[] PROGMEM = "RX Pin";
static RF433Usermod usermod_v2_RF433;
REGISTER_USERMOD(usermod_v2_RF433);