2024-11-05 11:45 AM
Hi there,
My first post on here, and I appreciate your time reading it.
I have some VL53L0X time-of-flight boards, and want to interface one with the Espressif ESP32 development board.
My primary objective is to run this set up at low power, so I want to be able put the ESP32 to sleep and wake it up when the VL53L0X detects a "below the low distance threshold" event. To do this I will need the GPIO1 pin to fire an interrupt when the threshold is breached. I have copied the Pololu VL53L0X library source and am currently using that as my library. It works well for basic measuring functions in both continuous and single shot modes. But no matter what I do I have been unable to get it to generate an interrupt. I've tried lots of approaches... the code posted here is working for basic measurement at least.
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <Wire.h>
#include <VL53L0X.h> // Copy of the Pololu VL53L0X library is included. Acknowledgements to the author!
// TFT pins
#define TFT_CS 15
#define TFT_DC 2
#define TFT_RST 4
// VL53L0X I2C pins
#define I2C_SDA 21
#define I2C_SCL 22
#define XSHUT_PIN 26
#define VL53L0X_INTERRUPT_PIN 25 // Pin on ESP32 connected to GPIO1 on VL53L0X
// Initialize TFT and VL53L0X objects
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
VL53L0X sensor;
const uint16_t lowThreshold = 200; // Set low threshold in mm
const uint16_t highThreshold = 400; // Set high threshold in mm
const uint8_t interruptConfig = 0x02; // Range below threshold mode
volatile int counter = 0;
// Minimal interrupt handler
void IRAM_ATTR vl53l0xInterruptHandler() {
// Increment a counter to check that it triggers
counter++;
}
void powerOffVL53L0X() {
digitalWrite(XSHUT_PIN, LOW); // Set XSHUT low to power down
}
void powerOnVL53L0X() {
digitalWrite(XSHUT_PIN, HIGH); // Set XSHUT high to power up
delay(100); // Allow time for initialization
}
void cycleTheVL53L0X()
{
powerOffVL53L0X();
delay(3000);
powerOnVL53L0X();
// Initialize VL53L0X
if (!sensor.init()) {
Serial.println("Failed to detect VL53L0X sensor!");
while (1);
}
sensor.setTimeout(2000);
}
void setup() {
// Initialize Serial for debugging
Serial.begin(115200);
pinMode(XSHUT_PIN, OUTPUT);
pinMode(VL53L0X_INTERRUPT_PIN, INPUT_PULLUP); // Configure interrupt pin as input
attachInterrupt(digitalPinToInterrupt(VL53L0X_INTERRUPT_PIN), vl53l0xInterruptHandler, FALLING);
// Initialize TFT
tft.begin();
tft.setRotation(1);
tft.fillScreen(ILI9341_BLACK);
tft.setCursor(10, 10);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
tft.println("TFT & VL53L0X");
// Initialize I2C with custom pins for VL53L0X
Wire.begin(I2C_SDA, I2C_SCL);
digitalWrite(XSHUT_PIN, HIGH); // Set XSHUT high to enable the sensor
cycleTheVL53L0X();
// Initialize VL53L0X
if (!sensor.init()) {
Serial.println("Failed to detect VL53L0X sensor!");
while (1);
}
sensor.setTimeout(500);
Serial.println("VL53L0X initialized.");
// Configure VL53L0X for "range below threshold" with low polarity interrupt
Serial.println("Configuring interrupt and thresholds...");
sensor.writeReg16Bit(VL53L0X::SYSTEM_THRESH_LOW, lowThreshold); // Set low threshold
sensor.writeReg16Bit(VL53L0X::SYSTEM_THRESH_HIGH, highThreshold); // Set high threshold
//sensor.writeReg(VL53L0X::SYSTEM_INTERRUPT_CONFIG_GPIO, interruptConfig); // Interrupt on range below threshold. This causes problems when included!
// Set GPIO polarity to active low
uint8_t gpioConfig = sensor.readReg(VL53L0X::GPIO_HV_MUX_ACTIVE_HIGH) & ~0x10;
sensor.writeReg(VL53L0X::GPIO_HV_MUX_ACTIVE_HIGH, gpioConfig);
// Clear any existing interrupts
sensor.writeReg(VL53L0X::SYSTEM_INTERRUPT_CLEAR, 0x01);
}
void loop() {
// Read distance from VL53L0X
uint16_t distance = sensor.readRangeSingleMillimeters();
// Print to Serial and display on TFT
Serial.print("Distance: "); Serial.print(distance); Serial.println(" mm");
// Write to the Adafruit TFT
tft.fillScreen(ILI9341_BLACK);
tft.setCursor(10, 30);
tft.print("Distance: ");
tft.print(distance);
tft.println(" mm");
// *** This check works but I need to replace it in the setup() function with a proper SYSTEM_INTERRUPT_CONFIG_GPIO registration ***
if (distance < 130)
{
Serial.print("Distance less than 130mm. Cycling power: "); Serial.print(distance); Serial.println(" mm");
tft.fillScreen(ILI9341_BLACK);
tft.setCursor(10, 30);
tft.print("Distance less than 130mm. Cycling power: ");
tft.print(distance);
tft.println(" mm");
cycleTheVL53L0X();
}
// counter > 0 means that the interrupt must have fired, except that the LED I have attached to GPIO1 does not flicker
if (counter > 0)
{
counter--;
Serial.println("Interrupt fired!");
tft.println("Interrupt fired...");
// Clear any existing interrupts
sensor.writeReg(VL53L0X::SYSTEM_INTERRUPT_CLEAR, 0x01);
}
delay(1000);
}
In terms of hardware, I knocked up a temporary Veroboard set up. It has physical pull-up 4.7k resistors on the SDA and SCK lines, and a 10k pull-up resistor on GPIO1. I've also got an LED on GPIO1 to monitor the logic voltage on that. I have a separate 5v and 3.3v DC supply to the VL53L0X.
I attach a video of my setup. It keeps telling me the interrupt has fired, but I don't believe that. I think my VL53L0X configuration is wrong, but don't know where to start changing it!
Can anyone shed any light on how to get interrupts working, please?
It would be very much appreciated :)
2024-11-05 12:19 PM
If you get a different distance number, you may rest assured that you got a range. And if that range is < 200, you should have gotten an interrupt. Use a white sheet of paper. It's amazingly easy to see, and with your code you should stop getting numbers once you get 200mm away.
Try raising your paper more than 200mm. Do the outputs stop?? They should.
Another way to check is to use an oscilloscope - but I know not everyone has one on their bench.
Your ISR increments 'counter' - so you are getting the interrupt.
Your issue is are you getting interrupts when the target is above 200mm.
So keep moving that paper away until you are past 200mm.
Should tell you.
- johh
2024-11-05 02:46 PM
Hi John,
Thanks for replying so quickly! I really appreciate it.
Wherever I put the paper, from 150 to 800+ the interrupt says it has fired. Only when I go below 130mm, as designed, the board power cycles off and on.
At the same time during the reading loop, my LED never flickers - with it's current limiting resistor it is placed across GPIO1 and earth so I would expect it to flicker if an interrupt was actually firing because that should go LOW.
Is that where you would place the Oscilloscope probes? I do have access to one so will check that tomorrow.
I'm unsure as to whether single shot or continuous reading mode is required for interrupt generation. Does it matter?
I'm also not sure of my config. The line I have commented out at present:
2024-11-05 03:40 PM
OK - so your problem is that you are getting an interrupt every time - when you only wanted them below 200mm.
the SYSTEM_INTERRUPT_CONFIG_GPIO - has to be set to Below the threshold, and of course the lower threshold has to be set. You don't care about the Upper threshold.
I don't know much about the Pololu VL53L0X library, but I'd go to ST.com and search for the VL53L0X and download ST's library. That's the bible.
Check Pololu's code against ours.
We set data to 1 - Low threshold and call the following to tell the sensor to interrupt only on low crossing
2024-11-06 04:06 AM
Hi John,
Thanks again for your quick response!
I did of course download the ST API right at the start but struggled to make it work for me.
I'll have another go at that.
Best wishes
John
2024-11-06 06:39 AM
You don't have to migrate to the ST code. Pololu did a pretty good job of turning our mess into usable code. But use the ST code as a reference to double check Pololu. Compare what they have done to what ST does. Make sure they write the same registers. I'm pretty sure you will figure it out. After all it's just setting the lower threshold, and then setting the interrupt configure register to a 1.
The only real trick is the VL53L0X uses 8-bit registers. And there are a lot of them. So, we need pages. The other VL53 sensors use 16-bit addresses.
That's one of the reasons I try to push the VL53L4CD for short distance applications. It's just an easier sensor to use.
(The VL53L4CD has a narrower Field of View, and limited to 1.3 Meters. But it's newer and easier to use.)
- john
2024-11-06 11:02 AM
Hi again John,
Thanks for that very useful reply!
I'm painstakingly going through the writes to the registers and trying to make sure I not only write to them correctly, but can read them back and that they contain the expected values.
You're right - how hard can it be?
I'll let you know if I get a breakthrough, and will also have a look at the VL53L4CD.
Best wishes
John