Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 194 additions & 16 deletions usermods/Animated_Staircase/Animated_Staircase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,38 @@
* See the accompanying README.md file for more info.
*/
#include "wled.h"
#include "Wire.h"
#include <VL53L0X.h>

class Animated_Staircase : public Usermod {
private:

/* configuration (available in API and stored in flash) */
bool enabled = false; // Enable this usermod

bool useV53l0x = false; // Use VL53L0X Laser Range Sensors
// address we will assign if dual sensor is present
#define LOXTOP_ADDRESS 0x41 // Default address should be 0x41
#define LOXBOT_ADDRESS 0x42

Comment on lines +22 to +24
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix VL53L0X address constants (default is 0x29) and pick unique addresses for dual sensors.

The comment “Default address should be 0x41” is incorrect; VL53L0X default is 0x29. Also, define two non-conflicting addresses explicitly for dual setup.

Apply this diff:

-    #define LOXTOP_ADDRESS 0x41 // Default address should be 0x41
-    #define LOXBOT_ADDRESS 0x42
+    // Default VL53L0X I2C address is 0x29; reassign to unique addresses when using two sensors
+    #define LOXTOP_ADDRESS 0x30
+    #define LOXBOT_ADDRESS 0x31
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#define LOXTOP_ADDRESS 0x41 // Default address should be 0x41
#define LOXBOT_ADDRESS 0x42
// Default VL53L0X I2C address is 0x29; reassign to unique addresses when using two sensors
#define LOXTOP_ADDRESS 0x30
#define LOXBOT_ADDRESS 0x31
🤖 Prompt for AI Agents
In usermods/Animated_Staircase/Animated_Staircase.cpp around lines 22 to 24, the
VL53L0X address comment and defines are wrong — the sensor's default 7-bit I2C
address is 0x29 and you must pick two unique addresses for a dual-sensor setup;
update the comment to state the correct default (0x29) and change the defines to
two distinct addresses (for example LOX_TOP_ADDRESS 0x29 and LOX_BOTTOM_ADDRESS
0x2A) so the sensors do not conflict on the bus.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like @coderabbitai is correct. Examples I have use the same I2C addresses as suggested.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@blazoncek Thank you for confirming! It's great to have validation from your examples that the VL53L0X default address is indeed 0x29, and that using distinct addresses like 0x30 and 0x31 (or similar non-conflicting pairs) is the correct approach for dual-sensor setups. This will ensure the sensors don't interfere with each other on the I2C bus.

// set the pins to shutdown
//#define SHT_LOX1 13 //Top D7
//#define SHT_LOX2 12 //Bottom D6

// objects for the vl53l0x
VL53L0X loxBot;
VL53L0X loxTop;

int16_t v53l0x_bottom_distance = -1;
int16_t v53l0x_top_distance = -1;
int8_t topXSHUTPin = -1; // disabled
int8_t botXSHUTPin = -1; // disabled
bool topSensorFailed = false;
bool botSensorFailed = false;
unsigned long showDebugData = 0;
//VL53L0X_RangingMeasurementData_t measureBot;


unsigned long segment_delay_ms = 150; // Time between switching each segment
unsigned long on_time_ms = 30000; // The time for the light to stay on
int8_t topPIRorTriggerPin = -1; // disabled
Expand Down Expand Up @@ -91,6 +117,9 @@ class Animated_Staircase : public Usermod {
static const char _topEchoCm[];
static const char _bottomEchoCm[];
static const char _togglePower[];
static const char _useV53l0x[];
static const char _topXSHUT_pin[];
static const char _botXSHUT_pin[];

void publishMqtt(bool bottom, const char* state) {
#ifndef WLED_DISABLE_MQTT
Expand Down Expand Up @@ -156,22 +185,69 @@ class Animated_Staircase : public Usermod {
return pulseIn(echoPin, HIGH, maxTimeUs) > 0;
}

bool vl53l0xRead(bool top, unsigned int maxDist) {
int16_t mm = 0;

if (top) {
v53l0x_top_distance = loxTop.readRangeContinuousMillimeters();
if (loxTop.timeoutOccurred()) {
v53l0x_top_distance = 999;
return false;
} else {
mm = v53l0x_top_distance;
}
} else {
v53l0x_bottom_distance = loxBot.readRangeContinuousMillimeters();
if (loxBot.timeoutOccurred()) {
v53l0x_bottom_distance = 999;
return false;
} else {
mm = v53l0x_bottom_distance;
}
}

if (mm>20) {
return (mm < maxDist ? true : false);
} else {
return false;
}

}

bool checkSensors() {

if (millis() - showDebugData > 9999) {
DEBUG_PRINTLN(F("---VL53L0X INFO---"));
DEBUG_PRINT(F("Top Distance: ")); DEBUG_PRINTLN(v53l0x_top_distance);
DEBUG_PRINT(F("Bot Distance: ")); DEBUG_PRINTLN(v53l0x_bottom_distance);
showDebugData = millis();
}

bool sensorChanged = false;

if ((millis() - lastScanTime) > scanDelay) {
lastScanTime = millis();

bottomSensorRead = bottomSensorWrite ||
if (useV53l0x && (!(botSensorFailed)) ) {
bottomSensorRead = bottomSensorWrite || vl53l0xRead(false, (bottomMaxDist * 10));
} else {
bottomSensorRead = bottomSensorWrite ||
(!useUSSensorBottom ?
(bottomPIRorTriggerPin<0 ? false : digitalRead(bottomPIRorTriggerPin)) :
ultrasoundRead(bottomPIRorTriggerPin, bottomEchoPin, bottomMaxDist*59) // cm to us
);
topSensorRead = topSensorWrite ||

}

if (useV53l0x && (!(topSensorFailed))) {
topSensorRead = topSensorWrite || vl53l0xRead(true, (topMaxDist * 10));
} else {
topSensorRead = topSensorWrite ||
(!useUSSensorTop ?
(topPIRorTriggerPin<0 ? false : digitalRead(topPIRorTriggerPin)) :
ultrasoundRead(topPIRorTriggerPin, topEchoPin, topMaxDist*59) // cm to us
);
}
Comment on lines +231 to +250
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider degrading to fallback on repeated timeouts.

If timeoutOccurred() happens repeatedly, mark the sensor failed to avoid continual I2C churn and rely on PIR/US fallback.

If agreeable, I can draft a small rolling timeout counter and trip *_SensorFailed after N consecutive timeouts.

🤖 Prompt for AI Agents
In usermods/Animated_Staircase/Animated_Staircase.cpp around lines 231 to 250,
the VL53L0X reads can repeatedly time out causing continual I2C churn; add a
small rolling timeout counter per sensor (e.g., topTimeoutCount and
bottomTimeoutCount) that increments when timeoutOccurred() or vl53l0xRead
indicates a timeout, resets to zero on a successful read, and when the counter
reaches a configurable threshold N set the corresponding
topSensorFailed/bottomSensorFailed to true so code falls back to PIR/US; ensure
counters are checked/updated immediately after each vl53l0xRead call and persist
across loop iterations (initialize near sensor state), and consider resetting
the failed flag and the counter if a future manual reinitialization or
successful probe is performed.


if (bottomSensorRead != bottomSensorState) {
bottomSensorState = bottomSensorRead; // change previous state
Expand Down Expand Up @@ -279,19 +355,24 @@ class Animated_Staircase : public Usermod {
DEBUG_PRINT(on_time_ms / 1000);
DEBUG_PRINTLN(F(" seconds."));

if (!useUSSensorBottom)
pinMode(bottomPIRorTriggerPin, INPUT_PULLUP);
else {
pinMode(bottomPIRorTriggerPin, OUTPUT);
pinMode(bottomEchoPin, INPUT);
}
if (!useV53l0x) {
if (!useUSSensorBottom)
pinMode(bottomPIRorTriggerPin, INPUT_PULLUP);
else {
pinMode(bottomPIRorTriggerPin, OUTPUT);
pinMode(bottomEchoPin, INPUT);
}

if (!useUSSensorTop)
pinMode(topPIRorTriggerPin, INPUT_PULLUP);
else {
pinMode(topPIRorTriggerPin, OUTPUT);
pinMode(topEchoPin, INPUT);
}
} else {

if (!useUSSensorTop)
pinMode(topPIRorTriggerPin, INPUT_PULLUP);
else {
pinMode(topPIRorTriggerPin, OUTPUT);
pinMode(topEchoPin, INPUT);
}

onIndex = minSegmentId = strip.getMainSegmentId(); // it may not be the best idea to start with main segment as it may not be the first one
offIndex = maxSegmentId = strip.getLastActiveSegmentId() + 1;

Expand All @@ -315,29 +396,90 @@ class Animated_Staircase : public Usermod {
enabled = enable;
}

void setID() {
// all reset
digitalWrite(topXSHUTPin, LOW);
digitalWrite(botXSHUTPin, LOW);
delay(10);
// all unreset
digitalWrite(topXSHUTPin, HIGH);
digitalWrite(botXSHUTPin, HIGH);
delay(10);

digitalWrite(botXSHUTPin, LOW);
delay(10);

//Only one address needs to be set, other sensor will use default I2C address
loxBot.setAddress(LOXBOT_ADDRESS);
delay(10);
digitalWrite(botXSHUTPin, HIGH);
// initing LOX1
if(!loxBot.init(false)) {
DEBUG_PRINTLN(F("VL053L0X: Failed to boot Bottom VL53L0X"));
botSensorFailed = true;
}
delay(10);

// activating LOX2

//initing LOX2
if(!loxTop.init(false)) {
DEBUG_PRINTLN(F("VL053L0X: Failed to boot Top VL53L0X"));
topSensorFailed = true;
}

loxTop.setTimeout(500);
loxBot.setTimeout(500);
loxTop.startContinuous();
loxBot.startContinuous();
}
Comment on lines +399 to +435
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Address-assignment sequence is incorrect (setAddress before init; device held in reset; bus collision risk).

  • Both sensors are released to default 0x29 simultaneously, then one is put back to reset. This can cause brief address collision.
  • setAddress() is called before init(), and while the target sensor is in reset — likely readdressing the wrong device.

Reorder: hold both in reset; bring up one sensor, init it, readdress it; then bring up the other, init and readdress; start continuous only on successful init.

Apply this diff:

 void setID() {
-      // all reset
-      digitalWrite(topXSHUTPin, LOW);    
-      digitalWrite(botXSHUTPin, LOW);
-      delay(10);
-      // all unreset
-      digitalWrite(topXSHUTPin, HIGH);
-      digitalWrite(botXSHUTPin, HIGH);
-      delay(10);
-
-      digitalWrite(botXSHUTPin, LOW);
-      delay(10);
-      
-      //Only one address needs to be set, other sensor will use default I2C address
-      loxBot.setAddress(LOXBOT_ADDRESS);
-      delay(10);
-      digitalWrite(botXSHUTPin, HIGH);
-      // initing LOX1
-      if(!loxBot.init(false)) {
-         DEBUG_PRINTLN(F("VL053L0X: Failed to boot Bottom VL53L0X"));
-         botSensorFailed = true;
-      }
-      delay(10);
-
-      // activating LOX2
-     
-      //initing LOX2
-      if(!loxTop.init(false)) {
-         DEBUG_PRINTLN(F("VL053L0X: Failed to boot Top VL53L0X"));
-         topSensorFailed = true;
-      }
-
-      loxTop.setTimeout(500);
-      loxBot.setTimeout(500);
-      loxTop.startContinuous();
-      loxBot.startContinuous();
+      // Guard pins
+      if (topXSHUTPin < 0 || botXSHUTPin < 0) {
+        DEBUG_PRINTLN(F("VL53L0X: XSHUT pins not configured."));
+        topSensorFailed = botSensorFailed = true;
+        return;
+      }
+      // Reset both
+      digitalWrite(topXSHUTPin, LOW);
+      digitalWrite(botXSHUTPin, LOW);
+      delay(10);
+
+      // Bring up TOP first, init, then readdress
+      digitalWrite(topXSHUTPin, HIGH);
+      delay(10);
+      if (!loxTop.init(false)) {
+        DEBUG_PRINTLN(F("VL53L0X: Failed to boot Top VL53L0X"));
+        topSensorFailed = true;
+      } else {
+        loxTop.setAddress(LOXTOP_ADDRESS); // move away from default 0x29
+        loxTop.setTimeout(500);
+        loxTop.startContinuous();
+      }
+
+      // Bring up BOTTOM, init, then readdress
+      digitalWrite(botXSHUTPin, HIGH);
+      delay(10);
+      if (!loxBot.init(false)) {
+        DEBUG_PRINTLN(F("VL53L0X: Failed to boot Bottom VL53L0X"));
+        botSensorFailed = true;
+      } else {
+        loxBot.setAddress(LOXBOT_ADDRESS);
+        loxBot.setTimeout(500);
+        loxBot.startContinuous();
+      }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void setID() {
// all reset
digitalWrite(topXSHUTPin, LOW);
digitalWrite(botXSHUTPin, LOW);
delay(10);
// all unreset
digitalWrite(topXSHUTPin, HIGH);
digitalWrite(botXSHUTPin, HIGH);
delay(10);
digitalWrite(botXSHUTPin, LOW);
delay(10);
//Only one address needs to be set, other sensor will use default I2C address
loxBot.setAddress(LOXBOT_ADDRESS);
delay(10);
digitalWrite(botXSHUTPin, HIGH);
// initing LOX1
if(!loxBot.init(false)) {
DEBUG_PRINTLN(F("VL053L0X: Failed to boot Bottom VL53L0X"));
botSensorFailed = true;
}
delay(10);
// activating LOX2
//initing LOX2
if(!loxTop.init(false)) {
DEBUG_PRINTLN(F("VL053L0X: Failed to boot Top VL53L0X"));
topSensorFailed = true;
}
loxTop.setTimeout(500);
loxBot.setTimeout(500);
loxTop.startContinuous();
loxBot.startContinuous();
}
void setID() {
// Guard pins
if (topXSHUTPin < 0 || botXSHUTPin < 0) {
DEBUG_PRINTLN(F("VL53L0X: XSHUT pins not configured."));
topSensorFailed = botSensorFailed = true;
return;
}
// Reset both
digitalWrite(topXSHUTPin, LOW);
digitalWrite(botXSHUTPin, LOW);
delay(10);
// Bring up TOP first, init, then readdress
digitalWrite(topXSHUTPin, HIGH);
delay(10);
if (!loxTop.init(false)) {
DEBUG_PRINTLN(F("VL53L0X: Failed to boot Top VL53L0X"));
topSensorFailed = true;
} else {
loxTop.setAddress(LOXTOP_ADDRESS); // move away from default 0x29
loxTop.setTimeout(500);
loxTop.startContinuous();
}
// Bring up BOTTOM, init, then readdress
digitalWrite(botXSHUTPin, HIGH);
delay(10);
if (!loxBot.init(false)) {
DEBUG_PRINTLN(F("VL53L0X: Failed to boot Bottom VL53L0X"));
botSensorFailed = true;
} else {
loxBot.setAddress(LOXBOT_ADDRESS);
loxBot.setTimeout(500);
loxBot.startContinuous();
}
}
🤖 Prompt for AI Agents
In usermods/Animated_Staircase/Animated_Staircase.cpp around lines 399-435, the
I2C address sequence is wrong: both sensors are briefly released together,
setAddress is called before init while the device may still be in reset, risking
address collision and readdressing the wrong device. Fix by holding both XSHUT
LOW, then release only the first sensor (e.g., bot) HIGH, init it and only after
successful init call setAddress(LOXBOT_ADDRESS); then release the second sensor
(top) HIGH, init it and set its address if needed; set timeouts and call
startContinuous() only for sensors that successfully initialized, and mark
failures accordingly.


public:
void setup() {
// standardize invalid pin numbers to -1
if (topPIRorTriggerPin < 0) topPIRorTriggerPin = -1;
if (topEchoPin < 0) topEchoPin = -1;
if (bottomPIRorTriggerPin < 0) bottomPIRorTriggerPin = -1;
if (bottomEchoPin < 0) bottomEchoPin = -1;

//V53L0X
if (botXSHUTPin < 0) botXSHUTPin = -1;
if (topXSHUTPin < 0) topXSHUTPin = -1;

// allocate pins
PinManagerPinType pins[4] = {
PinManagerPinType pins[6] = {
{ topPIRorTriggerPin, useUSSensorTop },
{ topEchoPin, false },
{ bottomPIRorTriggerPin, useUSSensorBottom },
{ bottomEchoPin, false },
{ topXSHUTPin, false},
{ botXSHUTPin, false},
};
// NOTE: this *WILL* return TRUE if all the pins are set to -1.
// this is *BY DESIGN*.
if (!PinManager::allocateMultiplePins(pins, 4, PinOwner::UM_AnimatedStaircase)) {
if (!PinManager::allocateMultiplePins(pins, 6, PinOwner::UM_AnimatedStaircase)) {
topPIRorTriggerPin = -1;
topEchoPin = -1;
bottomPIRorTriggerPin = -1;
bottomEchoPin = -1;
enabled = false;
//V53L0X
topXSHUTPin = -1;
botXSHUTPin = -1;
}
Comment on lines +460 to +469
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Return early on pin allocation failure to avoid using invalid pins.

After failure you proceed to configure pins (potentially -1). Bail out from setup.

Apply this diff:

       if (!PinManager::allocateMultiplePins(pins, 6, PinOwner::UM_AnimatedStaircase)) {
         topPIRorTriggerPin = -1;
         topEchoPin = -1;
         bottomPIRorTriggerPin = -1;
         bottomEchoPin = -1;
         enabled = false;
         //V53L0X
         topXSHUTPin = -1;
         botXSHUTPin = -1;
+        DEBUG_PRINTLN(F("Animated Staircase: pin allocation failed, disabling."));
+        return;
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!PinManager::allocateMultiplePins(pins, 6, PinOwner::UM_AnimatedStaircase)) {
topPIRorTriggerPin = -1;
topEchoPin = -1;
bottomPIRorTriggerPin = -1;
bottomEchoPin = -1;
enabled = false;
//V53L0X
topXSHUTPin = -1;
botXSHUTPin = -1;
}
if (!PinManager::allocateMultiplePins(pins, 6, PinOwner::UM_AnimatedStaircase)) {
topPIRorTriggerPin = -1;
topEchoPin = -1;
bottomPIRorTriggerPin = -1;
bottomEchoPin = -1;
enabled = false;
//V53L0X
topXSHUTPin = -1;
botXSHUTPin = -1;
DEBUG_PRINTLN(F("Animated Staircase: pin allocation failed, disabling."));
return;
}


if (useV53l0x) {
pinMode(topXSHUTPin, OUTPUT);
pinMode(botXSHUTPin, OUTPUT);
DEBUG_PRINTLN(F("VL053L0X: Shutdown pins inited..."));
digitalWrite(topXSHUTPin, LOW);
digitalWrite(botXSHUTPin, LOW);
DEBUG_PRINTLN(F("VL053L0X: Both in reset mode...(pins are low)"));
DEBUG_PRINTLN(F("VL053L0X: Starting..."));
setID();
showDebugData = millis();
}
Comment on lines +471 to 481
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Guard pinMode/digitalWrite against invalid XSHUT pins when VL53L0X is enabled.

Calling pinMode/digitalWrite with -1 is undefined on some MCUs.

Apply this diff:

-      if (useV53l0x) {
-        pinMode(topXSHUTPin, OUTPUT);
-        pinMode(botXSHUTPin, OUTPUT);
+      if (useV53l0x) {
+        if (topXSHUTPin < 0 || botXSHUTPin < 0) {
+          DEBUG_PRINTLN(F("VL53L0X: Missing XSHUT pins, disabling VL53L0X path."));
+          topSensorFailed = botSensorFailed = true;
+        } else {
+          pinMode(topXSHUTPin, OUTPUT);
+          pinMode(botXSHUTPin, OUTPUT);
           DEBUG_PRINTLN(F("VL053L0X: Shutdown pins inited..."));
           digitalWrite(topXSHUTPin, LOW);
           digitalWrite(botXSHUTPin, LOW);
           DEBUG_PRINTLN(F("VL053L0X: Both in reset mode...(pins are low)"));
           DEBUG_PRINTLN(F("VL053L0X: Starting..."));
           setID();
           showDebugData = millis();
+        }
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (useV53l0x) {
pinMode(topXSHUTPin, OUTPUT);
pinMode(botXSHUTPin, OUTPUT);
DEBUG_PRINTLN(F("VL053L0X: Shutdown pins inited..."));
digitalWrite(topXSHUTPin, LOW);
digitalWrite(botXSHUTPin, LOW);
DEBUG_PRINTLN(F("VL053L0X: Both in reset mode...(pins are low)"));
DEBUG_PRINTLN(F("VL053L0X: Starting..."));
setID();
showDebugData = millis();
}
if (useV53l0x) {
if (topXSHUTPin < 0 || botXSHUTPin < 0) {
DEBUG_PRINTLN(F("VL53L0X: Missing XSHUT pins, disabling VL53L0X path."));
topSensorFailed = botSensorFailed = true;
} else {
pinMode(topXSHUTPin, OUTPUT);
pinMode(botXSHUTPin, OUTPUT);
DEBUG_PRINTLN(F("VL053L0X: Shutdown pins inited..."));
digitalWrite(topXSHUTPin, LOW);
digitalWrite(botXSHUTPin, LOW);
DEBUG_PRINTLN(F("VL053L0X: Both in reset mode...(pins are low)"));
DEBUG_PRINTLN(F("VL053L0X: Starting..."));
setID();
showDebugData = millis();
}
}
🤖 Prompt for AI Agents
In usermods/Animated_Staircase/Animated_Staircase.cpp around lines 471 to 481,
the code calls pinMode() and digitalWrite() for topXSHUTPin and botXSHUTPin
unconditionally which is undefined if a pin is -1; guard each pin operation with
a check (e.g., pin >= 0) before calling pinMode/digitalWrite, perform the LOW
writes only for pins that pass the check, and keep or adjust the DEBUG_PRINTLN
messages to reflect whether each pin was initialized or skipped.


enable(enabled);
initDone = true;
}
Expand Down Expand Up @@ -451,6 +593,9 @@ class Animated_Staircase : public Usermod {
staircase[FPSTR(_topEchoCm)] = topMaxDist;
staircase[FPSTR(_bottomEchoCm)] = bottomMaxDist;
staircase[FPSTR(_togglePower)] = togglePower;
staircase[FPSTR(_useV53l0x)] = useV53l0x;
staircase[FPSTR(_topXSHUT_pin)] = topXSHUTPin;
staircase[FPSTR(_botXSHUT_pin)] = botXSHUTPin;
DEBUG_PRINTLN(F("Staircase config saved."));
}

Expand All @@ -467,6 +612,11 @@ class Animated_Staircase : public Usermod {
int8_t oldBottomAPin = bottomPIRorTriggerPin;
int8_t oldBottomBPin = bottomEchoPin;

//V53L0X
bool oldUseV53l0x = useV53l0x;
int8_t oldBotXSHUTPin = botXSHUTPin;
int8_t oldTopXSHUTPin = topXSHUTPin;

JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
DEBUG_PRINT(FPSTR(_name));
Expand Down Expand Up @@ -497,6 +647,10 @@ class Animated_Staircase : public Usermod {

togglePower = top[FPSTR(_togglePower)] | togglePower; // staircase toggles power on/off

useV53l0x = top[FPSTR(_useV53l0x)] | useV53l0x;
topXSHUTPin = top[FPSTR(_topXSHUT_pin)] | topXSHUTPin;
botXSHUTPin = top[FPSTR(_botXSHUT_pin)] | botXSHUTPin;

DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
// first run: reading from cfg.json
Expand All @@ -510,12 +664,17 @@ class Animated_Staircase : public Usermod {
(oldTopAPin != topPIRorTriggerPin) ||
(oldTopBPin != topEchoPin) ||
(oldBottomAPin != bottomPIRorTriggerPin) ||
(oldBottomBPin != bottomEchoPin)) {
(oldBottomBPin != bottomEchoPin) ||
(oldUseV53l0x != useV53l0x) ||
(oldBotXSHUTPin != botXSHUTPin) ||
(oldTopXSHUTPin != topXSHUTPin)) {
changed = true;
PinManager::deallocatePin(oldTopAPin, PinOwner::UM_AnimatedStaircase);
PinManager::deallocatePin(oldTopBPin, PinOwner::UM_AnimatedStaircase);
PinManager::deallocatePin(oldBottomAPin, PinOwner::UM_AnimatedStaircase);
PinManager::deallocatePin(oldBottomBPin, PinOwner::UM_AnimatedStaircase);
PinManager::deallocatePin(oldBotXSHUTPin, PinOwner::UM_AnimatedStaircase);
PinManager::deallocatePin(oldTopXSHUTPin, PinOwner::UM_AnimatedStaircase);
}
if (changed) setup();
}
Expand Down Expand Up @@ -544,6 +703,22 @@ class Animated_Staircase : public Usermod {
uiDomString += enabled ? "on" : "off";
uiDomString += F("\">&#xe08f;</i></button>");
infoArr.add(uiDomString);

if (useV53l0x) {
JsonArray topRange = user.createNestedArray(F("V53L0X Top Range"));
JsonArray botRange = user.createNestedArray(F("V53L0X Bottom Range"));
if (topSensorFailed) {
topRange.add(F("Sensor Failed!"));
} else {
topRange.add(v53l0x_top_distance);
}
if (botSensorFailed) {
botRange.add(F("Bottom Sensor Failed!"));
} else {
botRange.add(v53l0x_bottom_distance);
}

}
}
};

Expand All @@ -561,6 +736,9 @@ const char Animated_Staircase::_bottomEcho_pin[] PROGMEM = "bottomEch
const char Animated_Staircase::_topEchoCm[] PROGMEM = "top-dist-cm";
const char Animated_Staircase::_bottomEchoCm[] PROGMEM = "bottom-dist-cm";
const char Animated_Staircase::_togglePower[] PROGMEM = "toggle-on-off";
const char Animated_Staircase::_useV53l0x[] PROGMEM = "useV53l0x";
const char Animated_Staircase::_topXSHUT_pin[] PROGMEM = "topXSHUTPin";
const char Animated_Staircase::_botXSHUT_pin[] PROGMEM = "botXSHUTPin";


static Animated_Staircase animated_staircase;
Expand Down