Issue with Usermods in WLED for Controlling an LED Strip with a Distance Sensor
Hello everyone,
I’m new to WLED, but I have experience programming on ESP32 and VS code. I’m trying to implement a custom effect using a usermod, but with the limited documentation available, I haven’t been able to make much progress, even with AI assistance.
My goal is to control the progressive lighting of an LED strip using a VCNL4200 distance sensor. When the sensor detects an object, I want the LEDs to turn on one by one until the entire strip is illuminated. Then, after 1 minute, the strip should turn off until the next detection. The LED strip would be installed on a staircase, with sensors at both ends.
I have the sensor working correctly, but my issue is figuring out how to force the LED strip to be controlled exclusively from the usermod code instead of the WLED UI. When I send a command to turn off the LEDs from my code, they briefly turn off but then immediately turn back on (I assume because the web interface had them previously enabled).
What I want is that when the “enable” option of my usermod is activated, no other effects or features should work. But when this option is disabled, I should be able to use and play with the effects freely.
I also tried running the VL53L0X_gestures example from the usermods folder, but I don’t know how to activate it or make it work. Is there a detailed guide or a step-by-step example for this? When activated, should something appear in the WLED UI under usermods?
I’m attaching all the modified code I’ve written so far and will try to provide as much detail as possible.
Feel free to point out any issues with my code if it’s incorrect, as I’m not a professional.
This is the .h file inside the usermods folder.
#pragma once
#include "wled.h"
#include <Adafruit_VCNL4200.h>
class UsermodVCNL4200Staircase : public Usermod {
private:
// Private variables
Adafruit_VCNL4200 vcnl4200;
unsigned long lastTime = 0;
bool enabled = true;
bool isMotionDetected = false;
bool allLedsOn = false; // Indicates if all LEDs are turned on
bool animationRunning = false; // Indicates if the animation is running
unsigned long motionStartTime = 0;
unsigned long animationStartTime = 0; // Animation start time
unsigned long ledDelay = 100; // Delay between each LED turning on (in ms)
unsigned long onTime = 60000; // Time the LEDs remain on (in ms)
uint16_t proximityThreshold = 100; // Proximity threshold for motion detection
uint16_t proximity;
uint16_t currentLed = 0; // Current LED being turned on
float proximityCM;
uint32_t staircaseColor = RGBW32(255, 255, 255, 0); // Default pure white color (RGB)
public:
void setup() override {
if (!vcnl4200.begin()) {
DEBUG_PRINTLN(F("Failed to initialize VCNL4200 sensor!"));
enabled = false;
} else {
DEBUG_PRINTLN(F("VCNL4200 sensor initialized successfully!"));
}
vcnl4200.setALSshutdown(true);
vcnl4200.setProxShutdown(false);
vcnl4200.setProxHD(false);
vcnl4200.setProxLEDCurrent(VCNL4200_LED_I_200MA);
vcnl4200.setProxIntegrationTime(VCNL4200_PS_IT_8T);
}
void loop() override {
proximity = vcnl4200.readProxData(); // Read proximity data
// Print proximity reading every 500 ms
if (millis() - lastTime >= 500) {
proximityCM = 558.62 / pow(proximity + 91.30, 0.61);
DEBUG_PRINT(F("Raw Proximity: ")); DEBUG_PRINT(proximity);
DEBUG_PRINT(F(" | Proximity CM: ")); DEBUG_PRINTLN(proximityCM);
lastTime = millis();
}
// Start animation if proximity is detected
if ((proximityCM < proximityThreshold) && !animationRunning) {
DEBUG_PRINTLN(F("Motion detected! Starting animation..."));
animationRunning = true;
currentLed = 0; // Reset LED counter
allLedsOn = false; // Reset LED state
animationStartTime = millis(); // Store animation start time
}
// Check if animation is running
if (animationRunning) {
startStaircaseEffect();
}
// Turn off LEDs after 1 minute
stopStaircaseEffect();
}
void startStaircaseEffect(){
if (millis() - motionStartTime >= ledDelay) {
motionStartTime = millis();
// Turn on the next LED
strip.setPixelColor(currentLed, staircaseColor); // Use configurable color
strip.show();
currentLed++;
// Check if all LEDs are on
if (currentLed >= strip.getLengthTotal()) {
allLedsOn = true;
animationStartTime = millis(); // Store animation start time
}
}
}
void stopStaircaseEffect(){
if (allLedsOn){ // } && (millis() - animationStartTime >= onTime)) { //test
DEBUG_PRINTLN(F("Animation complete. Turning off LEDs..."));
for (int i = 0; i < strip.getLengthTotal(); i++) {
strip.setPixelColor(i, 0); // Turn off all LEDs
}
strip.show();
animationRunning = false; // Stop animation
allLedsOn = false;
currentLed = 0;
}
}
// Method to save configuration
void addToConfig(JsonObject& root) override {
JsonObject top = root.createNestedObject("Staircase Light"); // Create an object for the usermod
top["enabled"] = enabled; // Save enabled/disabled state
top["ledDelay"] = ledDelay; // Save LED delay time
top["onTime"] = onTime; // Save LED on-time
top["proximityThreshold"] = proximityThreshold; // Save proximity threshold
top["color"] = staircaseColor; // Save color
}
// Method to load configuration
bool readFromConfig(JsonObject& root) override {
JsonObject top = root["Staircase Light"]; // Access the usermod object
if (!top.isNull()) { // If the object exists
enabled = top["enabled"] | enabled; // Load enabled/disabled state
ledDelay = top["ledDelay"] | ledDelay; // Load LED delay time
onTime = top["onTime"] | onTime; // Load LED on-time
proximityThreshold = top["proximityThreshold"] | proximityThreshold; // Load proximity threshold
staircaseColor = top["color"] | staircaseColor; // Load color
return true; // Indicate successful configuration load
}
return false; // Indicate that no configuration was found
}
uint16_t getId() override {
return USERMOD_ID_VCNL4200_STAIRCASE;
}
};
I added this line to const.h
//Usermod IDs
#define USERMOD_ID_VCNL4200_STAIRCASE 1234 //Usermod "usermod_VCNL4200.h"
I added these lines to usermods_list.h
#include "../usermods/usermod_vcnl4200_staircase/usermod_vcnl4200_staircase.h"
void registerUsermods()
{
UsermodManager::add(new UsermodVCNL4200Staircase());
...
If you need any details or clarification, I’d be glad to provide them.
I would really appreciate any help, as I’ve been struggling with this for days without a satisfactory solution.