From 2b38aa7d36792001b42a9c0a56a5e68fc9879578 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Mon, 5 Apr 2021 16:01:03 +0200 Subject: [PATCH 1/7] [P113] Add VL53L1X Time of Flight sensor to ESPEasy --- lib/SparkFun_VL53L1X/ComponentObject.h | 75 ++ lib/SparkFun_VL53L1X/RangeSensor.h | 60 + lib/SparkFun_VL53L1X/SparkFun_VL53L1X.cpp | 360 ++++++ lib/SparkFun_VL53L1X/SparkFun_VL53L1X.h | 117 ++ lib/SparkFun_VL53L1X/vl53l1_error_codes.h | 251 +++++ lib/SparkFun_VL53L1X/vl53l1x_class.cpp | 1154 ++++++++++++++++++++ lib/SparkFun_VL53L1X/vl53l1x_class.h | 561 ++++++++++ src/_P113_VL53L1X.ino | 148 +++ src/src/CustomBuild/define_plugin_sets.h | 1 + src/src/PluginStructs/P113_data_struct.cpp | 108 ++ src/src/PluginStructs/P113_data_struct.h | 44 + src/src/WebServer/I2C_Scanner.cpp | 2 +- 12 files changed, 2880 insertions(+), 1 deletion(-) create mode 100644 lib/SparkFun_VL53L1X/ComponentObject.h create mode 100644 lib/SparkFun_VL53L1X/RangeSensor.h create mode 100644 lib/SparkFun_VL53L1X/SparkFun_VL53L1X.cpp create mode 100644 lib/SparkFun_VL53L1X/SparkFun_VL53L1X.h create mode 100644 lib/SparkFun_VL53L1X/vl53l1_error_codes.h create mode 100644 lib/SparkFun_VL53L1X/vl53l1x_class.cpp create mode 100644 lib/SparkFun_VL53L1X/vl53l1x_class.h create mode 100644 src/_P113_VL53L1X.ino create mode 100644 src/src/PluginStructs/P113_data_struct.cpp create mode 100644 src/src/PluginStructs/P113_data_struct.h diff --git a/lib/SparkFun_VL53L1X/ComponentObject.h b/lib/SparkFun_VL53L1X/ComponentObject.h new file mode 100644 index 000000000..b5a4ecf10 --- /dev/null +++ b/lib/SparkFun_VL53L1X/ComponentObject.h @@ -0,0 +1,75 @@ +/** + ****************************************************************************** + * @file ComponentObject.h + * @author AST + * @version V1.0.0 + * @date April 13th, 2015 + * @brief This file contains the abstract class describing the interface of a + * generic component. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2015 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + + +/* Define to prevent recursive inclusion -------------------------------------*/ + +#ifndef __COMPONENT_OBJECT_CLASS_H +#define __COMPONENT_OBJECT_CLASS_H + + +/* Includes ------------------------------------------------------------------*/ + +#include + + +/* Classes ------------------------------------------------------------------*/ + +/** An abstract class for Generic components. + */ +class ComponentObject +{ +public: + /** + * @brief Initializing the component. + * @param[in] init pointer to device specific initalization structure. + * @retval "0" in case of success, an error code otherwise. + */ + virtual int Init() = 0; + + /** + * @brief Getting the ID of the component. + * @param[out] id pointer to an allocated variable to store the ID into. + * @retval "0" in case of success, an error code otherwise. + */ + virtual int ReadID() = 0; +}; + +#endif /* __COMPONENT_OBJECT_CLASS_H */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/lib/SparkFun_VL53L1X/RangeSensor.h b/lib/SparkFun_VL53L1X/RangeSensor.h new file mode 100644 index 000000000..119112ea7 --- /dev/null +++ b/lib/SparkFun_VL53L1X/RangeSensor.h @@ -0,0 +1,60 @@ +/** + ****************************************************************************** + * @file RangeSensor.h + * @author AST / EST + * @version V0.0.1 + * @date 13-April-2015 + * @brief This file contains the abstract class describing in general + * the interfaces of a range sensor + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2015 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +/* Define to prevent from recursive inclusion --------------------------------*/ +#ifndef __RANGE_SENSOR_CLASS_H +#define __RANGE_SENSOR_CLASS_H + +/* Includes ------------------------------------------------------------------*/ +#include + +/* Classes ------------------------------------------------------------------*/ +/** An abstract class for range sensors + */ +class RangeSensor : public ComponentObject +{ + public: + /** + * @brief Get current range [mm] + * @param[out] piData Pointer to where to store range to + * @return 0 in case of success, an error code otherwise + */ + virtual int GetDistance(uint32_t *piData) = 0; +}; + +#endif /* __RANGE_SENSOR_CLASS_H */ diff --git a/lib/SparkFun_VL53L1X/SparkFun_VL53L1X.cpp b/lib/SparkFun_VL53L1X/SparkFun_VL53L1X.cpp new file mode 100644 index 000000000..1c9459b25 --- /dev/null +++ b/lib/SparkFun_VL53L1X/SparkFun_VL53L1X.cpp @@ -0,0 +1,360 @@ +/* + This is a library written for the VL53L1X I2C Distance sensor. + + Written by Andy England @ SparkFun Electronics, October 17th, 2017 + + The sensor uses I2C to communicate, as well as a single (optional) + interrupt line that is not currently supported in this driver. + + https://github.com/sparkfun/SparkFun_VL53L1X_Arduino_Library + + Do you like this library? Help support SparkFun. Buy a board! + + Development environment specifics: + Arduino IDE 1.8.1 + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include "Arduino.h" +#include "SparkFun_VL53L1X.h" +#include "vl53l1x_class.h" + +SFEVL53L1X::SFEVL53L1X(TwoWire &i2cPort, int shutdownPin, int interruptPin) +{ + _i2cPort = &i2cPort; + _shutdownPin = shutdownPin; + _interruptPin = interruptPin; + _device = new VL53L1X(&i2cPort, shutdownPin, interruptPin); +} + +bool SFEVL53L1X::init() +{ + return _device->VL53L1X_SensorInit(); +} + +bool SFEVL53L1X::begin() +{ + if (checkID() == false) + return (VL53L1_ERROR_PLATFORM_SPECIFIC_START); + + return _device->VL53L1X_SensorInit(); +} + +/*Checks the ID of the device, returns true if ID is correct*/ + +bool SFEVL53L1X::checkID() +{ + uint16_t sensorId; + _device->VL53L1X_GetSensorId(&sensorId); + return sensorId == 0xEACC; +} + +/*Turns the sensor on if the Shutdown pin is connected*/ + +void SFEVL53L1X::sensorOn() +{ + if (_shutdownPin >= 0) + { + digitalWrite(_shutdownPin, HIGH); + } + delay(10); +} + +/*Turns the sensor off if the Shutdown pin is connected*/ + +void SFEVL53L1X::sensorOff() +{ + if (_shutdownPin >= 0) + { + digitalWrite(_shutdownPin, LOW); + } + delay(10); +} + +/*Gets the software version number of the current library installed.*/ + +VL53L1X_Version_t SFEVL53L1X::getSoftwareVersion() +{ + VL53L1X_Version_t tempVersion; + _device->VL53L1X_GetSWVersion(&tempVersion); + return tempVersion; +} + +void SFEVL53L1X::setI2CAddress(uint8_t addr) +{ + _i2cAddress = addr; + _device->VL53L1X_SetI2CAddress(addr); +} + +int SFEVL53L1X::getI2CAddress() +{ + return _i2cAddress; +} + +void SFEVL53L1X::clearInterrupt() +{ + _device->VL53L1X_ClearInterrupt(); +} + +void SFEVL53L1X::setInterruptPolarityHigh() +{ + _device->VL53L1X_SetInterruptPolarity(1); +} + +void SFEVL53L1X::setInterruptPolarityLow() +{ + _device->VL53L1X_SetInterruptPolarity(0); +} + +/** + * This function gets the interrupt polarity\n + * 1=active high (default), 0=active low + */ + +uint8_t SFEVL53L1X::getInterruptPolarity() +{ + uint8_t tmp; + _device->VL53L1X_GetInterruptPolarity(&tmp); + return tmp; +} + +void SFEVL53L1X::startRanging() +{ + _device->VL53L1X_StartRanging(); +} + +void SFEVL53L1X::stopRanging() +{ + _device->VL53L1X_StopRanging(); +} + +bool SFEVL53L1X::checkForDataReady() +{ + uint8_t dataReady; + _device->VL53L1X_CheckForDataReady(&dataReady); + return (bool)dataReady; +} + +void SFEVL53L1X::setTimingBudgetInMs(uint16_t timingBudget) +{ + _device->VL53L1X_SetTimingBudgetInMs(timingBudget); +} + +uint16_t SFEVL53L1X::getTimingBudgetInMs() +{ + uint16_t timingBudget; + _device->VL53L1X_GetTimingBudgetInMs(&timingBudget); + return timingBudget; +} + +void SFEVL53L1X::setDistanceModeLong() +{ + _device->VL53L1X_SetDistanceMode(2); +} + +void SFEVL53L1X::setDistanceModeShort() +{ + _device->VL53L1X_SetDistanceMode(1); +} + +uint8_t SFEVL53L1X::getDistanceMode() +{ + uint16_t distanceMode; + _device->VL53L1X_GetDistanceMode(&distanceMode); + return distanceMode; +} + +void SFEVL53L1X::setIntermeasurementPeriod(uint16_t intermeasurement) +{ + _device->VL53L1X_SetInterMeasurementInMs(intermeasurement); +} + +uint16_t SFEVL53L1X::getIntermeasurementPeriod() +{ + uint16_t intermeasurement; + _device->VL53L1X_GetInterMeasurementInMs(&intermeasurement); + return intermeasurement; +} + +bool SFEVL53L1X::checkBootState() +{ + uint8_t bootState; + _device->VL53L1X_BootState(&bootState); + return (bool)bootState; +} + +uint16_t SFEVL53L1X::getSensorID() +{ + uint16_t id; + _device->VL53L1X_GetSensorId(&id); + return id; +} + +uint16_t SFEVL53L1X::getDistance() +{ + uint16_t distance; + _device->VL53L1X_GetDistance(&distance); + return (int)distance; +} + +uint16_t SFEVL53L1X::getSignalPerSpad() +{ + uint16_t temp; + _device->VL53L1X_GetSignalPerSpad(&temp); + return temp; +} + +uint16_t SFEVL53L1X::getAmbientPerSpad() +{ + uint16_t temp; + _device->VL53L1X_GetAmbientPerSpad(&temp); + return temp; +} + +uint16_t SFEVL53L1X::getSignalRate() +{ + uint16_t temp; + _device->VL53L1X_GetSignalRate(&temp); + return temp; +} + +uint16_t SFEVL53L1X::getSpadNb() +{ + uint16_t temp; + _device->VL53L1X_GetSpadNb(&temp); + return temp; +} + +uint16_t SFEVL53L1X::getAmbientRate() +{ + uint16_t temp; + _device->VL53L1X_GetAmbientRate(&temp); + return temp; +} + +uint8_t SFEVL53L1X::getRangeStatus() +{ + uint8_t temp; + _device->VL53L1X_GetRangeStatus(&temp); + return temp; +} + +void SFEVL53L1X::setOffset(int16_t offset) +{ + _device->VL53L1X_SetOffset(offset); +} + +int16_t SFEVL53L1X::getOffset() +{ + int16_t temp; + _device->VL53L1X_GetOffset(&temp); + return temp; +} + +void SFEVL53L1X::setXTalk(uint16_t xTalk) +{ + _device->VL53L1X_SetXtalk(xTalk); +} + +uint16_t SFEVL53L1X::getXTalk() +{ + uint16_t temp; + _device->VL53L1X_GetXtalk(&temp); + return temp; +} + +void SFEVL53L1X::setDistanceThreshold(uint16_t lowThresh, uint16_t hiThresh, uint8_t window) +{ + _device->VL53L1X_SetDistanceThreshold(lowThresh, hiThresh, window, 1); +} + +uint16_t SFEVL53L1X::getDistanceThresholdWindow() +{ + uint16_t temp; + _device->VL53L1X_GetDistanceThresholdWindow(&temp); + return temp; +} + +uint16_t SFEVL53L1X::getDistanceThresholdLow() +{ + uint16_t temp; + _device->VL53L1X_GetDistanceThresholdLow(&temp); + return temp; +} + +uint16_t SFEVL53L1X::getDistanceThresholdHigh() +{ + uint16_t temp; + _device->VL53L1X_GetDistanceThresholdHigh(&temp); + return temp; +} + +void SFEVL53L1X::setROI(uint8_t x, uint8_t y, uint8_t opticalCenter) +{ + _device->VL53L1X_SetROI(x, y, opticalCenter); +} + +uint16_t SFEVL53L1X::getROIX() +{ + uint16_t tempX; + uint16_t tempY; + _device->VL53L1X_GetROI_XY(&tempX, &tempY); + return tempX; +} + +uint16_t SFEVL53L1X::getROIY() +{ + uint16_t tempX; + uint16_t tempY; + _device->VL53L1X_GetROI_XY(&tempX, &tempY); + return tempY; +} + +void SFEVL53L1X::setSignalThreshold(uint16_t signalThreshold) +{ + _device->VL53L1X_SetSignalThreshold(signalThreshold); +} + +uint16_t SFEVL53L1X::getSignalThreshold() +{ + uint16_t temp; + _device->VL53L1X_GetSignalThreshold(&temp); + return temp; +} + +void SFEVL53L1X::setSigmaThreshold(uint16_t sigmaThreshold) +{ + _device->VL53L1X_SetSigmaThreshold(sigmaThreshold); +} + +uint16_t SFEVL53L1X::getSigmaThreshold() +{ + uint16_t temp; + _device->VL53L1X_GetSigmaThreshold(&temp); + return temp; +} + +void SFEVL53L1X::startTemperatureUpdate() +{ + _device->VL53L1X_StartTemperatureUpdate(); +} + +void SFEVL53L1X::calibrateOffset(uint16_t targetDistanceInMm) +{ + int16_t offset = getOffset(); + _device->VL53L1X_CalibrateOffset(targetDistanceInMm, &offset); +} + +void SFEVL53L1X::calibrateXTalk(uint16_t targetDistanceInMm) +{ + uint16_t xTalk = getXTalk(); + _device->VL53L1X_CalibrateXtalk(targetDistanceInMm, &xTalk); +}; diff --git a/lib/SparkFun_VL53L1X/SparkFun_VL53L1X.h b/lib/SparkFun_VL53L1X/SparkFun_VL53L1X.h new file mode 100644 index 000000000..712233d62 --- /dev/null +++ b/lib/SparkFun_VL53L1X/SparkFun_VL53L1X.h @@ -0,0 +1,117 @@ +/* + This is a library written for the VL53L1X I2C Distance sensor. + + Written by Andy England @ SparkFun Electronics, October 17th, 2017 + + The sensor uses I2C to communicate, as well as a single (optional) + interrupt line that is not currently supported in this driver. + + https://github.com/sparkfun/SparkFun_VL53L1X_Arduino_Library + + Do you like this library? Help support SparkFun. Buy a board! + + Development environment specifics: + Arduino IDE 1.8.1 + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#pragma once + +#include "Arduino.h" +#include "Wire.h" +#include "RangeSensor.h" +#include "vl53l1_error_codes.h" +#include "vl53l1x_class.h" + +class SFEVL53L1X +{ + public: + SFEVL53L1X(TwoWire &i2cPort = Wire, int shutdownPin = -1, int interruptPin = -1); //Constructs our Distance sensor without an interrupt or shutdown pin + bool init(); //Deprecated version of begin + bool begin(); //Initialization of sensor + bool checkID(); //Check the ID of the sensor, returns true if ID is correct + void sensorOn(); //Toggles shutdown pin to turn sensor on and off + void sensorOff(); //Toggles shutdown pin to turn sensor on and off + VL53L1X_Version_t getSoftwareVersion(); //Get's the current ST software version + void setI2CAddress(uint8_t addr); //Set the I2C address + int getI2CAddress(); //Get the I2C address + void clearInterrupt(); // Clear the interrupt flag + void setInterruptPolarityHigh(); //Set the polarity of an active interrupt to High + void setInterruptPolarityLow(); //Set the polarity of an active interrupt to Low + uint8_t getInterruptPolarity(); //get the current interrupt polarity + void startRanging(); //Begins taking measurements + void stopRanging(); //Stops taking measurements + bool checkForDataReady(); //Checks the to see if data is ready + void setTimingBudgetInMs(uint16_t timingBudget); //Set the timing budget for a measurement + uint16_t getTimingBudgetInMs(); //Get the timing budget for a measurement + void setDistanceModeLong(); //Set to 4M range + void setDistanceModeShort(); //Set to 1.3M range + uint8_t getDistanceMode(); //Get the distance mode, returns 1 for short and 2 for long + void setIntermeasurementPeriod(uint16_t intermeasurement); //Set time between measurements in ms + uint16_t getIntermeasurementPeriod(); //Get time between measurements in ms + bool checkBootState(); //Check if the VL53L1X has been initialized + uint16_t getSensorID(); //Get the sensor ID + uint16_t getDistance(); //Returns distance + uint16_t getSignalPerSpad(); //Returns the average signal rate per SPAD (The sensitive pads that detect light, the VL53L1X has a 16x16 array of these) in kcps/SPAD, or kilo counts per second per SPAD. + uint16_t getAmbientPerSpad(); //Returns the ambient noise when not measuring a signal in kcps/SPAD. + uint16_t getSignalRate(); //Returns the signal rate in kcps. All SPADs combined. + uint16_t getSpadNb(); //Returns the current number of enabled SPADs + uint16_t getAmbientRate(); // Returns the total ambinet rate in kcps. All SPADs combined. + uint8_t getRangeStatus(); //Returns the range status, which can be any of the following. 0 = no error, 1 = signal fail, 2 = sigma fail, 7 = wrapped target fail + void setOffset(int16_t offset); //Manually set an offset in mm + int16_t getOffset(); //Get the current offset in mm + void setXTalk(uint16_t xTalk); //Manually set the value of crosstalk in counts per second (cps), which is interference from any sort of window in front of your sensor. + uint16_t getXTalk(); //Returns the current crosstalk value in cps. + void setDistanceThreshold(uint16_t lowThresh, uint16_t hiThresh, uint8_t window);//Set bounds for the interrupt. lowThresh and hiThresh are the bounds of your interrupt while window decides when the interrupt should fire. The options for window are shown below. + //0: Interrupt triggered on measured distance below lowThresh. + //1: Interrupt triggered on measured distance above hiThresh. + //2: Interrupt triggered on measured distance outside of bounds. + //3: Interrupt triggered on measured distance inside of bounds. + uint16_t getDistanceThresholdWindow(); //Returns distance threshold window option + uint16_t getDistanceThresholdLow(); //Returns lower bound in mm. + uint16_t getDistanceThresholdHigh(); //Returns upper bound in mm + /**Table of Optical Centers** + * + * 128,136,144,152,160,168,176,184, 192,200,208,216,224,232,240,248 + * 129,137,145,153,161,169,177,185, 193,201,209,217,225,233,241,249 + * 130,138,146,154,162,170,178,186, 194,202,210,218,226,234,242,250 + * 131,139,147,155,163,171,179,187, 195,203,211,219,227,235,243,251 + * 132,140,148,156,164,172,180,188, 196,204,212,220,228,236,244,252 + * 133,141,149,157,165,173,181,189, 197,205,213,221,229,237,245,253 + * 134,142,150,158,166,174,182,190, 198,206,214,222,230,238,246,254 + * 135,143,151,159,167,175,183,191, 199,207,215,223,231,239,247,255 + + * 127,119,111,103, 95, 87, 79, 71, 63, 55, 47, 39, 31, 23, 15, 7 + * 126,118,110,102, 94, 86, 78, 70, 62, 54, 46, 38, 30, 22, 14, 6 + * 125,117,109,101, 93, 85, 77, 69, 61, 53, 45, 37, 29, 21, 13, 5 + * 124,116,108,100, 92, 84, 76, 68, 60, 52, 44, 36, 28, 20, 12, 4 + * 123,115,107, 99, 91, 83, 75, 67, 59, 51, 43, 35, 27, 19, 11, 3 + * 122,114,106, 98, 90, 82, 74, 66, 58, 50, 42, 34, 26, 18, 10, 2 + * 121,113,105, 97, 89, 81, 73, 65, 57, 49, 41, 33, 25, 17, 9, 1 + * 120,112,104, 96, 88, 80, 72, 64, 56, 48, 40, 32, 24, 16, 8, 0 Pin 1 + * + * To set the center, set the pad that is to the right and above the exact center of the region you'd like to measure as your opticalCenter*/ + void setROI(uint8_t x, uint8_t y, uint8_t opticalCenter); //Set the height and width of the ROI(region of interest) in SPADs, lowest possible option is 4. Set optical center based on above table + uint16_t getROIX(); //Returns the width of the ROI in SPADs + uint16_t getROIY(); //Returns the height of the ROI in SPADs + void setSignalThreshold(uint16_t signalThreshold); //Programs the necessary threshold to trigger a measurement. Default is 1024 kcps. + uint16_t getSignalThreshold(); //Returns the signal threshold in kcps + void setSigmaThreshold(uint16_t sigmaThreshold); //Programs a new sigma threshold in mm. (default=15 mm) + uint16_t getSigmaThreshold(); //Returns the current sigma threshold. + void startTemperatureUpdate(); //Recalibrates the sensor for temperature changes. Run this any time the temperature has changed by more than 8°C + void calibrateOffset(uint16_t targetDistanceInMm); //Autocalibrate the offset by placing a target a known distance away from the sensor and passing this known distance into the function. + void calibrateXTalk(uint16_t targetDistanceInMm); //Autocalibrate the crosstalk by placing a target a known distance away from the sensor and passing this known distance into the function. + private: + TwoWire *_i2cPort; + int _shutdownPin; + int _interruptPin; + int _i2cAddress = 0x52; + VL53L1X* _device; +}; \ No newline at end of file diff --git a/lib/SparkFun_VL53L1X/vl53l1_error_codes.h b/lib/SparkFun_VL53L1X/vl53l1_error_codes.h new file mode 100644 index 000000000..4a5f4f80a --- /dev/null +++ b/lib/SparkFun_VL53L1X/vl53l1_error_codes.h @@ -0,0 +1,251 @@ +/* +* Copyright (c) 2017, STMicroelectronics - All Rights Reserved +* +* This file is part of VL53L1 Core and is dual licensed, +* either 'STMicroelectronics +* Proprietary license' +* or 'BSD 3-clause "New" or "Revised" License' , at your option. +* +******************************************************************************** +* +* 'STMicroelectronics Proprietary license' +* +******************************************************************************** +* +* License terms: STMicroelectronics Proprietary in accordance with licensing +* terms at www.st.com/sla0081 +* +* STMicroelectronics confidential +* Reproduction and Communication of this document is strictly prohibited unless +* specifically authorized in writing by STMicroelectronics. +* +* +******************************************************************************** +* +* Alternatively, VL53L1 Core may be distributed under the terms of +* 'BSD 3-clause "New" or "Revised" License', in which case the following +* provisions apply instead of the ones mentioned above : +* +******************************************************************************** +* +* License terms: BSD 3-clause "New" or "Revised" License. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* 3. Neither the name of the copyright holder nor the names of its contributors +* may be used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* +******************************************************************************** +* +*/ + +/** + * @file vl53l1_error_codes.h + * + * @brief Error Code definitions for VL53L1 API. + * + */ + +#ifndef _VL53L1_ERROR_CODES_H_ +#define _VL53L1_ERROR_CODES_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/**************************************** + * PRIVATE define do not edit + ****************************************/ + +/** @defgroup VL53L1_define_Error_group Error and Warning code returned by API + * The following DEFINE are used to identify the PAL ERROR + * @{ + */ + +typedef int8_t VL53L1_Error; + +#define VL53L1_ERROR_NONE ((VL53L1_Error) 0) +#define VL53L1_ERROR_CALIBRATION_WARNING ((VL53L1_Error) - 1) + /*!< Warning invalid calibration data may be in used + \a VL53L1_InitData() + \a VL53L1_GetOffsetCalibrationData + \a VL53L1_SetOffsetCalibrationData */ +#define VL53L1_ERROR_MIN_CLIPPED ((VL53L1_Error) - 2) + /*!< Warning parameter passed was clipped to min before to be applied */ + +#define VL53L1_ERROR_UNDEFINED ((VL53L1_Error) - 3) + /*!< Unqualified error */ +#define VL53L1_ERROR_INVALID_PARAMS ((VL53L1_Error) - 4) + /*!< Parameter passed is invalid or out of range */ +#define VL53L1_ERROR_NOT_SUPPORTED ((VL53L1_Error) - 5) + /*!< Function is not supported in current mode or configuration */ +#define VL53L1_ERROR_RANGE_ERROR ((VL53L1_Error) - 6) + /*!< Device report a ranging error interrupt status */ +#define VL53L1_ERROR_TIME_OUT ((VL53L1_Error) - 7) + /*!< Aborted due to time out */ +#define VL53L1_ERROR_MODE_NOT_SUPPORTED ((VL53L1_Error) - 8) + /*!< Asked mode is not supported by the device */ +#define VL53L1_ERROR_BUFFER_TOO_SMALL ((VL53L1_Error) - 9) + /*!< ... */ +#define VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL ((VL53L1_Error) - 10) + /*!< Supplied buffer is larger than I2C supports */ +#define VL53L1_ERROR_GPIO_NOT_EXISTING ((VL53L1_Error) - 11) + /*!< User tried to setup a non-existing GPIO pin */ +#define VL53L1_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED ((VL53L1_Error) - 12) + /*!< unsupported GPIO functionality */ +#define VL53L1_ERROR_CONTROL_INTERFACE ((VL53L1_Error) - 13) + /*!< error reported from IO functions */ +#define VL53L1_ERROR_INVALID_COMMAND ((VL53L1_Error) - 14) + /*!< The command is not allowed in the current device state + * (power down) */ +#define VL53L1_ERROR_DIVISION_BY_ZERO ((VL53L1_Error) - 15) + /*!< In the function a division by zero occurs */ +#define VL53L1_ERROR_REF_SPAD_INIT ((VL53L1_Error) - 16) + /*!< Error during reference SPAD initialization */ +#define VL53L1_ERROR_GPH_SYNC_CHECK_FAIL ((VL53L1_Error) - 17) + /*!< GPH sync interrupt check fail - API out of sync with device*/ +#define VL53L1_ERROR_STREAM_COUNT_CHECK_FAIL ((VL53L1_Error) - 18) + /*!< Stream count check fail - API out of sync with device */ +#define VL53L1_ERROR_GPH_ID_CHECK_FAIL ((VL53L1_Error) - 19) + /*!< GPH ID check fail - API out of sync with device */ +#define VL53L1_ERROR_ZONE_STREAM_COUNT_CHECK_FAIL ((VL53L1_Error) - 20) + /*!< Zone dynamic config stream count check failed - API out of sync */ +#define VL53L1_ERROR_ZONE_GPH_ID_CHECK_FAIL ((VL53L1_Error) - 21) + /*!< Zone dynamic config GPH ID check failed - API out of sync */ + +#define VL53L1_ERROR_XTALK_EXTRACTION_NO_SAMPLE_FAIL ((VL53L1_Error) - 22) + /*!< Thrown when run_xtalk_extraction fn has 0 succesful samples + * when using the full array to sample the xtalk. In this case there is + * not enough information to generate new Xtalk parm info. The function + * will exit and leave the current xtalk parameters unaltered */ +#define VL53L1_ERROR_XTALK_EXTRACTION_SIGMA_LIMIT_FAIL ((VL53L1_Error) - 23) + /*!< Thrown when run_xtalk_extraction fn has found that the + * avg sigma estimate of the full array xtalk sample is > than the + * maximal limit allowed. In this case the xtalk sample is too noisy for + * measurement. The function will exit and leave the current xtalk parameters + * unaltered. */ + + +#define VL53L1_ERROR_OFFSET_CAL_NO_SAMPLE_FAIL ((VL53L1_Error) - 24) + /*!< Thrown if there one of stages has no valid offset calibration + * samples. A fatal error calibration not valid */ +#define VL53L1_ERROR_OFFSET_CAL_NO_SPADS_ENABLED_FAIL ((VL53L1_Error) - 25) + /*!< Thrown if there one of stages has zero effective SPADS + * Traps the case when MM1 SPADs is zero. + * A fatal error calibration not valid */ +#define VL53L1_ERROR_ZONE_CAL_NO_SAMPLE_FAIL ((VL53L1_Error) - 26) + /*!< Thrown if then some of the zones have no valid samples + * A fatal error calibration not valid */ + +#define VL53L1_ERROR_TUNING_PARM_KEY_MISMATCH ((VL53L1_Error) - 27) + /*!< Thrown if the tuning file key table version does not match with + * expected value. The driver expects the key table version to match + * the compiled default version number in the define + * #VL53L1_TUNINGPARM_KEY_TABLE_VERSION_DEFAULT + * */ + +#define VL53L1_WARNING_REF_SPAD_CHAR_NOT_ENOUGH_SPADS ((VL53L1_Error) - 28) + /*!< Thrown if there are less than 5 good SPADs are available. */ +#define VL53L1_WARNING_REF_SPAD_CHAR_RATE_TOO_HIGH ((VL53L1_Error) - 29) + /*!< Thrown if the final reference rate is greater than + the upper reference rate limit - default is 40 Mcps. + Implies a minimum Q3 (x10) SPAD (5) selected */ +#define VL53L1_WARNING_REF_SPAD_CHAR_RATE_TOO_LOW ((VL53L1_Error) - 30) + /*!< Thrown if the final reference rate is less than + the lower reference rate limit - default is 10 Mcps. + Implies maximum Q1 (x1) SPADs selected */ + + +#define VL53L1_WARNING_OFFSET_CAL_MISSING_SAMPLES ((VL53L1_Error) - 31) + /*!< Thrown if there is less than the requested number of + * valid samples. */ +#define VL53L1_WARNING_OFFSET_CAL_SIGMA_TOO_HIGH ((VL53L1_Error) - 32) + /*!< Thrown if the offset calibration range sigma estimate is greater + * than 8.0 mm. This is the recommended min value to yield a stable + * offset measurement */ +#define VL53L1_WARNING_OFFSET_CAL_RATE_TOO_HIGH ((VL53L1_Error) - 33) + /*!< Thrown when VL53L1_run_offset_calibration() peak rate is greater + than that 50.0Mcps. This is the recommended max rate to avoid + pile-up influencing the offset measurement */ +#define VL53L1_WARNING_OFFSET_CAL_SPAD_COUNT_TOO_LOW ((VL53L1_Error) - 34) + /*!< Thrown when VL53L1_run_offset_calibration() when one of stages + range has less that 5.0 effective SPADS. This is the recommended + min value to yield a stable offset */ + + +#define VL53L1_WARNING_ZONE_CAL_MISSING_SAMPLES ((VL53L1_Error) - 35) + /*!< Thrown if one of more of the zones have less than + the requested number of valid samples */ +#define VL53L1_WARNING_ZONE_CAL_SIGMA_TOO_HIGH ((VL53L1_Error) - 36) + /*!< Thrown if one or more zones have sigma estimate value greater + * than 8.0 mm. This is the recommended min value to yield a stable + * offset measurement */ +#define VL53L1_WARNING_ZONE_CAL_RATE_TOO_HIGH ((VL53L1_Error) - 37) + /*!< Thrown if one of more zones have peak rate higher than + that 50.0Mcps. This is the recommended max rate to avoid + pile-up influencing the offset measurement */ + + +#define VL53L1_WARNING_XTALK_MISSING_SAMPLES ((VL53L1_Error) - 38) + /*!< Thrown to notify that some of the xtalk samples did not yield + * valid ranging pulse data while attempting to measure + * the xtalk signal in vl53l1_run_xtalk_extract(). This can signify any of + * the zones are missing samples, for further debug information the + * xtalk_results struct should be referred to. This warning is for + * notification only, the xtalk pulse and shape have still been generated + */ +#define VL53L1_WARNING_XTALK_NO_SAMPLES_FOR_GRADIENT ((VL53L1_Error) - 39) + /*!< Thrown to notify that some of teh xtalk samples used for gradient + * generation did not yield valid ranging pulse data while attempting to + * measure the xtalk signal in vl53l1_run_xtalk_extract(). This can signify + * that any one of the zones 0-3 yielded no successful samples. The + * xtalk_results struct should be referred to for further debug info. + * This warning is for notification only, the xtalk pulse and shape + * have still been generated. + */ +#define VL53L1_WARNING_XTALK_SIGMA_LIMIT_FOR_GRADIENT ((VL53L1_Error) - 40) +/*!< Thrown to notify that some of the xtalk samples used for gradient + * generation did not pass the sigma limit check while attempting to + * measure the xtalk signal in vl53l1_run_xtalk_extract(). This can signify + * that any one of the zones 0-3 yielded an avg sigma_mm value > the limit. + * The xtalk_results struct should be referred to for further debug info. + * This warning is for notification only, the xtalk pulse and shape + * have still been generated. + */ + +#define VL53L1_ERROR_NOT_IMPLEMENTED ((VL53L1_Error) - 41) + /*!< Tells requested functionality has not been implemented yet or + * not compatible with the device */ +#define VL53L1_ERROR_PLATFORM_SPECIFIC_START ((VL53L1_Error) - 60) + /*!< Tells the starting code for platform */ +/** @} VL53L1_define_Error_group */ + + +#ifdef __cplusplus +} +#endif + + +#endif /* _VL53L1_ERROR_CODES_H_ */ diff --git a/lib/SparkFun_VL53L1X/vl53l1x_class.cpp b/lib/SparkFun_VL53L1X/vl53l1x_class.cpp new file mode 100644 index 000000000..4b05cef0f --- /dev/null +++ b/lib/SparkFun_VL53L1X/vl53l1x_class.cpp @@ -0,0 +1,1154 @@ +/** + ****************************************************************************** + * @file vl53l0x_class.cpp + * @author IMG + * @version V0.0.1 + * @date 14-December-2018 + * @brief Implementation file for the VL53L1X driver class + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2018 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** +*/ + +/* Includes */ +#include +#include "Arduino.h" +#include "vl53l1x_class.h" + +#define ALGO__PART_TO_PART_RANGE_OFFSET_MM 0x001E +#define MM_CONFIG__INNER_OFFSET_MM 0x0020 +#define MM_CONFIG__OUTER_OFFSET_MM 0x0022 + +//#define DEBUG_MODE + +const uint8_t VL51L1X_DEFAULT_CONFIGURATION[] = { + 0x00, /* 0x2d : set bit 2 and 5 to 1 for fast plus mode (1MHz I2C), else don't touch */ + 0x01, /* 0x2e : bit 0 if I2C pulled up at 1.8V, else set bit 0 to 1 (pull up at AVDD) */ + 0x01, /* 0x2f : bit 0 if GPIO pulled up at 1.8V, else set bit 0 to 1 (pull up at AVDD) */ + 0x01, /* 0x30 : set bit 4 to 0 for active high interrupt and 1 for active low (bits 3:0 must be 0x1), use SetInterruptPolarity() */ + 0x02, /* 0x31 : bit 1 = interrupt depending on the polarity, use CheckForDataReady() */ + 0x00, /* 0x32 : not user-modifiable */ + 0x02, /* 0x33 : not user-modifiable */ + 0x08, /* 0x34 : not user-modifiable */ + 0x00, /* 0x35 : not user-modifiable */ + 0x08, /* 0x36 : not user-modifiable */ + 0x10, /* 0x37 : not user-modifiable */ + 0x01, /* 0x38 : not user-modifiable */ + 0x01, /* 0x39 : not user-modifiable */ + 0x00, /* 0x3a : not user-modifiable */ + 0x00, /* 0x3b : not user-modifiable */ + 0x00, /* 0x3c : not user-modifiable */ + 0x00, /* 0x3d : not user-modifiable */ + 0xff, /* 0x3e : not user-modifiable */ + 0x00, /* 0x3f : not user-modifiable */ + 0x0F, /* 0x40 : not user-modifiable */ + 0x00, /* 0x41 : not user-modifiable */ + 0x00, /* 0x42 : not user-modifiable */ + 0x00, /* 0x43 : not user-modifiable */ + 0x00, /* 0x44 : not user-modifiable */ + 0x00, /* 0x45 : not user-modifiable */ + 0x20, /* 0x46 : interrupt configuration 0->level low detection, 1-> level high, 2-> Out of window, 3->In window, 0x20-> New sample ready , TBC */ + 0x0b, /* 0x47 : not user-modifiable */ + 0x00, /* 0x48 : not user-modifiable */ + 0x00, /* 0x49 : not user-modifiable */ + 0x02, /* 0x4a : not user-modifiable */ + 0x0a, /* 0x4b : not user-modifiable */ + 0x21, /* 0x4c : not user-modifiable */ + 0x00, /* 0x4d : not user-modifiable */ + 0x00, /* 0x4e : not user-modifiable */ + 0x05, /* 0x4f : not user-modifiable */ + 0x00, /* 0x50 : not user-modifiable */ + 0x00, /* 0x51 : not user-modifiable */ + 0x00, /* 0x52 : not user-modifiable */ + 0x00, /* 0x53 : not user-modifiable */ + 0xc8, /* 0x54 : not user-modifiable */ + 0x00, /* 0x55 : not user-modifiable */ + 0x00, /* 0x56 : not user-modifiable */ + 0x38, /* 0x57 : not user-modifiable */ + 0xff, /* 0x58 : not user-modifiable */ + 0x01, /* 0x59 : not user-modifiable */ + 0x00, /* 0x5a : not user-modifiable */ + 0x08, /* 0x5b : not user-modifiable */ + 0x00, /* 0x5c : not user-modifiable */ + 0x00, /* 0x5d : not user-modifiable */ + 0x01, /* 0x5e : not user-modifiable */ + 0xdb, /* 0x5f : not user-modifiable */ + 0x0f, /* 0x60 : not user-modifiable */ + 0x01, /* 0x61 : not user-modifiable */ + 0xf1, /* 0x62 : not user-modifiable */ + 0x0d, /* 0x63 : not user-modifiable */ + 0x01, /* 0x64 : Sigma threshold MSB (mm in 14.2 format for MSB+LSB), use SetSigmaThreshold(), default value 90 mm */ + 0x68, /* 0x65 : Sigma threshold LSB */ + 0x00, /* 0x66 : Min count Rate MSB (MCPS in 9.7 format for MSB+LSB), use SetSignalThreshold() */ + 0x80, /* 0x67 : Min count Rate LSB */ + 0x08, /* 0x68 : not user-modifiable */ + 0xb8, /* 0x69 : not user-modifiable */ + 0x00, /* 0x6a : not user-modifiable */ + 0x00, /* 0x6b : not user-modifiable */ + 0x00, /* 0x6c : Intermeasurement period MSB, 32 bits register, use SetIntermeasurementInMs() */ + 0x00, /* 0x6d : Intermeasurement period */ + 0x0f, /* 0x6e : Intermeasurement period */ + 0x89, /* 0x6f : Intermeasurement period LSB */ + 0x00, /* 0x70 : not user-modifiable */ + 0x00, /* 0x71 : not user-modifiable */ + 0x00, /* 0x72 : distance threshold high MSB (in mm, MSB+LSB), use SetD:tanceThreshold() */ + 0x00, /* 0x73 : distance threshold high LSB */ + 0x00, /* 0x74 : distance threshold low MSB ( in mm, MSB+LSB), use SetD:tanceThreshold() */ + 0x00, /* 0x75 : distance threshold low LSB */ + 0x00, /* 0x76 : not user-modifiable */ + 0x01, /* 0x77 : not user-modifiable */ + 0x0f, /* 0x78 : not user-modifiable */ + 0x0d, /* 0x79 : not user-modifiable */ + 0x0e, /* 0x7a : not user-modifiable */ + 0x0e, /* 0x7b : not user-modifiable */ + 0x00, /* 0x7c : not user-modifiable */ + 0x00, /* 0x7d : not user-modifiable */ + 0x02, /* 0x7e : not user-modifiable */ + 0xc7, /* 0x7f : ROI center, use SetROI() */ + 0xff, /* 0x80 : XY ROI (X=Width, Y=Height), use SetROI() */ + 0x9B, /* 0x81 : not user-modifiable */ + 0x00, /* 0x82 : not user-modifiable */ + 0x00, /* 0x83 : not user-modifiable */ + 0x00, /* 0x84 : not user-modifiable */ + 0x01, /* 0x85 : not user-modifiable */ + 0x00, /* 0x86 : clear interrupt, use ClearInterrupt() */ + 0x00 /* 0x87 : start ranging, use StartRanging() or StopRanging(), If you want an automatic start after VL53L1X_init() call, put 0x40 in location 0x87 */ +}; + +/* VL53L1X_api.h functions */ + +VL53L1X_ERROR VL53L1X::VL53L1X_GetSWVersion(VL53L1X_Version_t *pVersion) +{ + VL53L1X_ERROR Status = 0; + + pVersion->major = VL53L1X_IMPLEMENTATION_VER_MAJOR; + pVersion->minor = VL53L1X_IMPLEMENTATION_VER_MINOR; + pVersion->build = VL53L1X_IMPLEMENTATION_VER_SUB; + pVersion->revision = VL53L1X_IMPLEMENTATION_VER_REVISION; + return Status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_SetI2CAddress(uint8_t new_address) +{ + VL53L1X_ERROR status = 0; + + status = VL53L1_WrByte(Device, VL53L1_I2C_SLAVE__DEVICE_ADDRESS, new_address >> 1); + Device->I2cDevAddr = new_address; + + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_SensorInit() +{ + VL53L1X_ERROR status = 0; + uint8_t Addr = 0x00, dataReady = 0, timeout = 0; + + for (Addr = 0x2D; Addr <= 0x87; Addr++) + { + status = VL53L1_WrByte(Device, Addr, VL51L1X_DEFAULT_CONFIGURATION[Addr - 0x2D]); + } + status = VL53L1X_StartRanging(); + + //We need to wait at least the default intermeasurement period of 103ms before dataready will occur + //But if a unit has already been powered and polling, it may happen much faster + while (dataReady == 0) + { + status = VL53L1X_CheckForDataReady(&dataReady); + if (timeout++ > 150) + return VL53L1_ERROR_TIME_OUT; + delay(1); + } + status = VL53L1X_ClearInterrupt(); + status = VL53L1X_StopRanging(); + status = VL53L1_WrByte(Device, VL53L1_VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND, 0x09); /* two bounds VHV */ + status = VL53L1_WrByte(Device, 0x0B, 0); /* start VHV from the previous temperature */ + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_ClearInterrupt() +{ + VL53L1X_ERROR status = 0; + + status = VL53L1_WrByte(Device, SYSTEM__INTERRUPT_CLEAR, 0x01); + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_SetInterruptPolarity(uint8_t NewPolarity) +{ + uint8_t Temp; + VL53L1X_ERROR status = 0; + + status = VL53L1_RdByte(Device, GPIO_HV_MUX__CTRL, &Temp); + Temp = Temp & 0xEF; + status = VL53L1_WrByte(Device, GPIO_HV_MUX__CTRL, Temp | (!(NewPolarity & 1)) << 4); + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_GetInterruptPolarity(uint8_t *pInterruptPolarity) +{ + uint8_t Temp; + VL53L1X_ERROR status = 0; + + status = VL53L1_RdByte(Device, GPIO_HV_MUX__CTRL, &Temp); + Temp = Temp & 0x10; + *pInterruptPolarity = !(Temp >> 4); + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_StartRanging() +{ + VL53L1X_ERROR status = 0; + + status = VL53L1_WrByte(Device, SYSTEM__MODE_START, 0x40); /* Enable VL53L1X */ + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_StopRanging() +{ + VL53L1X_ERROR status = 0; + + status = VL53L1_WrByte(Device, SYSTEM__MODE_START, 0x00); /* Disable VL53L1X */ + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_CheckForDataReady(uint8_t *isDataReady) +{ + uint8_t Temp; + uint8_t IntPol; + VL53L1X_ERROR status = 0; + + status = VL53L1X_GetInterruptPolarity(&IntPol); + status = VL53L1_RdByte(Device, GPIO__TIO_HV_STATUS, &Temp); + /* Read in the register to check if a new value is available */ + if (status == 0) + { + if ((Temp & 1) == IntPol) + *isDataReady = 1; + else + *isDataReady = 0; + } + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_SetTimingBudgetInMs(uint16_t TimingBudgetInMs) +{ + uint16_t DM; + VL53L1X_ERROR status = 0; + + status = VL53L1X_GetDistanceMode(&DM); + if (DM == 0) + return 1; + else if (DM == 1) + { /* Short DistanceMode */ + switch (TimingBudgetInMs) + { + case 15: /* only available in short distance mode */ + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_A_HI, + 0x01D); + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_B_HI, + 0x0027); + break; + case 20: + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_A_HI, + 0x0051); + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_B_HI, + 0x006E); + break; + case 33: + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_A_HI, + 0x00D6); + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_B_HI, + 0x006E); + break; + case 50: + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_A_HI, + 0x1AE); + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_B_HI, + 0x01E8); + break; + case 100: + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_A_HI, + 0x02E1); + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_B_HI, + 0x0388); + break; + case 200: + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_A_HI, + 0x03E1); + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_B_HI, + 0x0496); + break; + case 500: + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_A_HI, + 0x0591); + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_B_HI, + 0x05C1); + break; + default: + status = 1; + break; + } + } + else + { + switch (TimingBudgetInMs) + { + case 20: + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_A_HI, + 0x001E); + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_B_HI, + 0x0022); + break; + case 33: + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_A_HI, + 0x0060); + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_B_HI, + 0x006E); + break; + case 50: + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_A_HI, + 0x00AD); + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_B_HI, + 0x00C6); + break; + case 100: + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_A_HI, + 0x01CC); + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_B_HI, + 0x01EA); + break; + case 200: + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_A_HI, + 0x02D9); + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_B_HI, + 0x02F8); + break; + case 500: + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_A_HI, + 0x048F); + VL53L1_WrWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_B_HI, + 0x04A4); + break; + default: + status = 1; + break; + } + } + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_GetTimingBudgetInMs(uint16_t *pTimingBudget) +{ + uint16_t Temp; + VL53L1X_ERROR status = 0; + + status = VL53L1_RdWord(Device, RANGE_CONFIG__TIMEOUT_MACROP_A_HI, &Temp); + switch (Temp) + { + case 0x001D: + *pTimingBudget = 15; + break; + case 0x0051: + case 0x001E: + *pTimingBudget = 20; + break; + case 0x00D6: + case 0x0060: + *pTimingBudget = 33; + break; + case 0x1AE: + case 0x00AD: + *pTimingBudget = 50; + break; + case 0x02E1: + case 0x01CC: + *pTimingBudget = 100; + break; + case 0x03E1: + case 0x02D9: + *pTimingBudget = 200; + break; + case 0x0591: + case 0x048F: + *pTimingBudget = 500; + break; + default: + *pTimingBudget = 0; + break; + } + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_SetDistanceMode(uint16_t DM) +{ + uint16_t TB; + VL53L1X_ERROR status = 0; + + status = VL53L1X_GetTimingBudgetInMs(&TB); + switch (DM) + { + case 1: + status = VL53L1_WrByte(Device, PHASECAL_CONFIG__TIMEOUT_MACROP, 0x14); + status = VL53L1_WrByte(Device, RANGE_CONFIG__VCSEL_PERIOD_A, 0x07); + status = VL53L1_WrByte(Device, RANGE_CONFIG__VCSEL_PERIOD_B, 0x05); + status = VL53L1_WrByte(Device, RANGE_CONFIG__VALID_PHASE_HIGH, 0x38); + status = VL53L1_WrWord(Device, SD_CONFIG__WOI_SD0, 0x0705); + status = VL53L1_WrWord(Device, SD_CONFIG__INITIAL_PHASE_SD0, 0x0606); + break; + case 2: + status = VL53L1_WrByte(Device, PHASECAL_CONFIG__TIMEOUT_MACROP, 0x0A); + status = VL53L1_WrByte(Device, RANGE_CONFIG__VCSEL_PERIOD_A, 0x0F); + status = VL53L1_WrByte(Device, RANGE_CONFIG__VCSEL_PERIOD_B, 0x0D); + status = VL53L1_WrByte(Device, RANGE_CONFIG__VALID_PHASE_HIGH, 0xB8); + status = VL53L1_WrWord(Device, SD_CONFIG__WOI_SD0, 0x0F0D); + status = VL53L1_WrWord(Device, SD_CONFIG__INITIAL_PHASE_SD0, 0x0E0E); + break; + default: + break; + } + status = VL53L1X_SetTimingBudgetInMs(TB); + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_GetDistanceMode(uint16_t *DM) +{ + uint8_t TempDM, status = 0; + + status = VL53L1_RdByte(Device, PHASECAL_CONFIG__TIMEOUT_MACROP, &TempDM); + if (TempDM == 0x14) + *DM = 1; + if (TempDM == 0x0A) + *DM = 2; + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_SetInterMeasurementInMs(uint16_t InterMeasMs) +{ + uint16_t ClockPLL; + VL53L1X_ERROR status = 0; + + status = VL53L1_RdWord(Device, VL53L1_RESULT__OSC_CALIBRATE_VAL, &ClockPLL); + ClockPLL = ClockPLL & 0x3FF; + VL53L1_WrDWord(Device, VL53L1_SYSTEM__INTERMEASUREMENT_PERIOD, + (uint32_t)(ClockPLL * InterMeasMs * 1.075)); + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_GetInterMeasurementInMs(uint16_t *pIM) +{ + uint16_t ClockPLL; + VL53L1X_ERROR status = 0; + uint32_t tmp; + + status = VL53L1_RdDWord(Device, VL53L1_SYSTEM__INTERMEASUREMENT_PERIOD, &tmp); + *pIM = (uint16_t)tmp; + status = VL53L1_RdWord(Device, VL53L1_RESULT__OSC_CALIBRATE_VAL, &ClockPLL); + ClockPLL = ClockPLL & 0x3FF; + *pIM = (uint16_t)(*pIM / (ClockPLL * 1.065)); + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_BootState(uint8_t *state) +{ + VL53L1X_ERROR status = 0; + uint8_t tmp = 0; + + status = VL53L1_RdByte(Device, VL53L1_FIRMWARE__SYSTEM_STATUS, &tmp); + *state = tmp; + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_GetSensorId(uint16_t *sensorId) +{ + VL53L1X_ERROR status = 0; + uint16_t tmp = 0; + + status = VL53L1_RdWord(Device, VL53L1_IDENTIFICATION__MODEL_ID, &tmp); + *sensorId = tmp; + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_GetDistance(uint16_t *distance) +{ + VL53L1X_ERROR status = 0; + uint16_t tmp; + + status = (VL53L1_RdWord(Device, + VL53L1_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0, &tmp)); + *distance = tmp; + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_GetSignalPerSpad(uint16_t *signalRate) +{ + VL53L1X_ERROR status = 0; + uint16_t SpNb = 1, signal; + + status = VL53L1_RdWord(Device, + VL53L1_RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0, &signal); + status = VL53L1_RdWord(Device, + VL53L1_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0, &SpNb); + *signalRate = (uint16_t)(2000.0 * signal / SpNb); + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_GetAmbientPerSpad(uint16_t *ambPerSp) +{ + VL53L1X_ERROR status = 0; + uint16_t AmbientRate, SpNb = 1; + + status = VL53L1_RdWord(Device, RESULT__AMBIENT_COUNT_RATE_MCPS_SD, &AmbientRate); + status = VL53L1_RdWord(Device, VL53L1_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0, &SpNb); + *ambPerSp = (uint16_t)(2000.0 * AmbientRate / SpNb); + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_GetSignalRate(uint16_t *signal) +{ + VL53L1X_ERROR status = 0; + uint16_t tmp; + + status = VL53L1_RdWord(Device, + VL53L1_RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0, &tmp); + *signal = tmp * 8; + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_GetSpadNb(uint16_t *spNb) +{ + VL53L1X_ERROR status = 0; + uint16_t tmp; + + status = VL53L1_RdWord(Device, + VL53L1_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0, &tmp); + *spNb = tmp >> 8; + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_GetAmbientRate(uint16_t *ambRate) +{ + VL53L1X_ERROR status = 0; + uint16_t tmp; + + status = VL53L1_RdWord(Device, RESULT__AMBIENT_COUNT_RATE_MCPS_SD, &tmp); + *ambRate = tmp * 8; + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_GetRangeStatus(uint8_t *rangeStatus) +{ + VL53L1X_ERROR status = 0; + uint8_t RgSt; + + status = VL53L1_RdByte(Device, VL53L1_RESULT__RANGE_STATUS, &RgSt); + RgSt = RgSt & 0x1F; + switch (RgSt) + { + case 9: + RgSt = 0; + break; + case 6: + RgSt = 1; + break; + case 4: + RgSt = 2; + break; + case 8: + RgSt = 3; + break; + case 5: + RgSt = 4; + break; + case 3: + RgSt = 5; + break; + case 19: + RgSt = 6; + break; + case 7: + RgSt = 7; + break; + case 12: + RgSt = 9; + break; + case 18: + RgSt = 10; + break; + case 22: + RgSt = 11; + break; + case 23: + RgSt = 12; + break; + case 13: + RgSt = 13; + break; + default: + RgSt = 255; + break; + } + *rangeStatus = RgSt; + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_SetOffset(int16_t OffsetValue) +{ + VL53L1X_ERROR status = 0; + int16_t Temp; + + Temp = (OffsetValue * 4); + VL53L1_WrWord(Device, ALGO__PART_TO_PART_RANGE_OFFSET_MM, + (uint16_t)Temp); + VL53L1_WrWord(Device, MM_CONFIG__INNER_OFFSET_MM, 0x0); + VL53L1_WrWord(Device, MM_CONFIG__OUTER_OFFSET_MM, 0x0); + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_GetOffset(int16_t *offset) +{ + VL53L1X_ERROR status = 0; + uint16_t Temp; + + status = VL53L1_RdWord(Device, ALGO__PART_TO_PART_RANGE_OFFSET_MM, &Temp); + Temp = Temp << 3; + Temp = Temp >> 5; + *offset = (int16_t)(Temp); + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_SetXtalk(uint16_t XtalkValue) +{ + /* XTalkValue in count per second to avoid float type */ + VL53L1X_ERROR status = 0; + + status = VL53L1_WrWord(Device, + ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS, + 0x0000); + status = VL53L1_WrWord(Device, ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS, + 0x0000); + status = VL53L1_WrWord(Device, ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS, + (XtalkValue << 9) / 1000); /* * << 9 (7.9 format) and /1000 to convert cps to kpcs */ + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_GetXtalk(uint16_t *xtalk) +{ + VL53L1X_ERROR status = 0; + uint16_t tmp; + + status = VL53L1_RdWord(Device, ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS, &tmp); + *xtalk = (tmp * 1000) >> 9; /* * 1000 to convert kcps to cps and >> 9 (7.9 format) */ + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_SetDistanceThreshold(uint16_t ThreshLow, + uint16_t ThreshHigh, uint8_t Window, + uint8_t IntOnNoTarget) +{ + VL53L1X_ERROR status = 0; + uint8_t Temp = 0; + + status = VL53L1_RdByte(Device, SYSTEM__INTERRUPT_CONFIG_GPIO, &Temp); + Temp = Temp & 0x47; + if (IntOnNoTarget == 0) + { + status = VL53L1_WrByte(Device, SYSTEM__INTERRUPT_CONFIG_GPIO, + (Temp | (Window & 0x07))); + } + else + { + status = VL53L1_WrByte(Device, SYSTEM__INTERRUPT_CONFIG_GPIO, + ((Temp | (Window & 0x07)) | 0x40)); + } + status = VL53L1_WrWord(Device, SYSTEM__THRESH_HIGH, ThreshHigh); + status = VL53L1_WrWord(Device, SYSTEM__THRESH_LOW, ThreshLow); + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_GetDistanceThresholdWindow(uint16_t *window) +{ + VL53L1X_ERROR status = 0; + uint8_t tmp; + status = VL53L1_RdByte(Device, SYSTEM__INTERRUPT_CONFIG_GPIO, &tmp); + *window = (uint16_t)(tmp & 0x7); + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_GetDistanceThresholdLow(uint16_t *low) +{ + VL53L1X_ERROR status = 0; + uint16_t tmp; + + status = VL53L1_RdWord(Device, SYSTEM__THRESH_LOW, &tmp); + *low = tmp; + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_GetDistanceThresholdHigh(uint16_t *high) +{ + VL53L1X_ERROR status = 0; + uint16_t tmp; + + status = VL53L1_RdWord(Device, SYSTEM__THRESH_HIGH, &tmp); + *high = tmp; + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_SetROI(uint8_t X, uint8_t Y, uint8_t opticalCenter) +{ + VL53L1X_ERROR status = 0; + + if (X > 16) + X = 16; + if (Y > 16) + Y = 16; + if (X > 10 || Y > 10) + { + opticalCenter = 199; + } + status = VL53L1_WrByte(Device, ROI_CONFIG__USER_ROI_CENTRE_SPAD, opticalCenter); + status = VL53L1_WrByte(Device, ROI_CONFIG__USER_ROI_REQUESTED_GLOBAL_XY_SIZE, + (Y - 1) << 4 | (X - 1)); + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_GetROI_XY(uint16_t *ROI_X, uint16_t *ROI_Y) +{ + VL53L1X_ERROR status = 0; + uint8_t tmp; + + status = VL53L1_RdByte(Device, ROI_CONFIG__USER_ROI_REQUESTED_GLOBAL_XY_SIZE, &tmp); + *ROI_X = ((uint16_t)tmp & 0x0F) + 1; + *ROI_Y = (((uint16_t)tmp & 0xF0) >> 4) + 1; + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_SetSignalThreshold(uint16_t Signal) +{ + VL53L1X_ERROR status = 0; + + VL53L1_WrWord(Device, RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS, Signal >> 3); + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_GetSignalThreshold(uint16_t *signal) +{ + VL53L1X_ERROR status = 0; + uint16_t tmp; + + status = VL53L1_RdWord(Device, + RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS, &tmp); + *signal = tmp << 3; + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_SetSigmaThreshold(uint16_t Sigma) +{ + VL53L1X_ERROR status = 0; + + if (Sigma > (0xFFFF >> 2)) + { + return 1; + } + /* 16 bits register 14.2 format */ + status = VL53L1_WrWord(Device, RANGE_CONFIG__SIGMA_THRESH, Sigma << 2); + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_GetSigmaThreshold(uint16_t *sigma) +{ + VL53L1X_ERROR status = 0; + uint16_t tmp; + + status = VL53L1_RdWord(Device, RANGE_CONFIG__SIGMA_THRESH, &tmp); + *sigma = tmp >> 2; + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1X_StartTemperatureUpdate() +{ + VL53L1X_ERROR status = 0; + uint8_t tmp = 0; + + status = VL53L1_WrByte(Device, VL53L1_VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND, 0x81); /* full VHV */ + status = VL53L1_WrByte(Device, 0x0B, 0x92); + status = VL53L1X_StartRanging(); + while (tmp == 0) + { + status = VL53L1X_CheckForDataReady(&tmp); + } + tmp = 0; + status = VL53L1X_ClearInterrupt(); + status = VL53L1X_StopRanging(); + status = VL53L1_WrByte(Device, VL53L1_VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND, 0x09); /* two bounds VHV */ + status = VL53L1_WrByte(Device, 0x0B, 0); /* start VHV from the previous temperature */ + return status; +} + +/* VL53L1X_calibration.h functions */ + +int8_t VL53L1X::VL53L1X_CalibrateOffset(uint16_t TargetDistInMm, int16_t *offset) +{ + uint8_t i = 0, tmp; + int16_t AverageDistance = 0; + uint16_t distance; + VL53L1X_ERROR status = 0; + + status = VL53L1_WrWord(Device, ALGO__PART_TO_PART_RANGE_OFFSET_MM, 0x0); + status = VL53L1_WrWord(Device, MM_CONFIG__INNER_OFFSET_MM, 0x0); + status = VL53L1_WrWord(Device, MM_CONFIG__OUTER_OFFSET_MM, 0x0); + status = VL53L1X_StartRanging(); /* Enable VL53L1X sensor */ + for (i = 0; i < 50; i++) + { + while (tmp == 0) + { + status = VL53L1X_CheckForDataReady(&tmp); + } + tmp = 0; + status = VL53L1X_GetDistance(&distance); + status = VL53L1X_ClearInterrupt(); + AverageDistance = AverageDistance + distance; + } + status = VL53L1X_StopRanging(); + AverageDistance = AverageDistance / 50; + *offset = TargetDistInMm - AverageDistance; + status = VL53L1_WrWord(Device, ALGO__PART_TO_PART_RANGE_OFFSET_MM, *offset * 4); + return status; +} + +int8_t VL53L1X::VL53L1X_CalibrateXtalk(uint16_t TargetDistInMm, uint16_t *xtalk) +{ + uint8_t i, tmp = 0; + float AverageSignalRate = 0; + float AverageDistance = 0; + float AverageSpadNb = 0; + uint16_t distance = 0, spadNum; + uint16_t sr; + VL53L1X_ERROR status = 0; + + status = VL53L1_WrWord(Device, 0x0016, 0); + status = VL53L1X_StartRanging(); + for (i = 0; i < 50; i++) + { + while (tmp == 0) + { + status = VL53L1X_CheckForDataReady(&tmp); + } + tmp = 0; + status = VL53L1X_GetSignalRate(&sr); + status = VL53L1X_GetDistance(&distance); + status = VL53L1X_ClearInterrupt(); + AverageDistance = AverageDistance + distance; + status = VL53L1X_GetSpadNb(&spadNum); + AverageSpadNb = AverageSpadNb + spadNum; + AverageSignalRate = + AverageSignalRate + sr; + } + status = VL53L1X_StopRanging(); + AverageDistance = AverageDistance / 50; + AverageSpadNb = AverageSpadNb / 50; + AverageSignalRate = AverageSignalRate / 50; + /* Calculate Xtalk value */ + *xtalk = (uint16_t)(512 * (AverageSignalRate * (1 - (AverageDistance / TargetDistInMm))) / AverageSpadNb); + status = VL53L1_WrWord(Device, 0x0016, *xtalk); + return status; +} + +/* Write and read functions from I2C */ + +VL53L1X_ERROR VL53L1X::VL53L1_WriteMulti(VL53L1_DEV Dev, uint16_t index, uint8_t *pdata, uint32_t count) +{ + int status; + + status = VL53L1_I2CWrite(Dev->I2cDevAddr, index, pdata, (uint16_t)count); + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1_ReadMulti(VL53L1_DEV Dev, uint16_t index, uint8_t *pdata, uint32_t count) +{ + int status; + + status = VL53L1_I2CRead(Dev->I2cDevAddr, index, pdata, (uint16_t)count); + + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1_WrByte(VL53L1_DEV Dev, uint16_t index, uint8_t data) +{ + int status; + + status = VL53L1_I2CWrite(Dev->I2cDevAddr, index, &data, 1); + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1_WrWord(VL53L1_DEV Dev, uint16_t index, uint16_t data) +{ + int status; + uint8_t buffer[2]; + + buffer[0] = data >> 8; + buffer[1] = data & 0x00FF; + status = VL53L1_I2CWrite(Dev->I2cDevAddr, index, (uint8_t *)buffer, 2); + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1_WrDWord(VL53L1_DEV Dev, uint16_t index, uint32_t data) +{ + int status; + uint8_t buffer[4]; + + buffer[0] = (data >> 24) & 0xFF; + buffer[1] = (data >> 16) & 0xFF; + buffer[2] = (data >> 8) & 0xFF; + buffer[3] = (data >> 0) & 0xFF; + status = VL53L1_I2CWrite(Dev->I2cDevAddr, index, (uint8_t *)buffer, 4); + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1_RdByte(VL53L1_DEV Dev, uint16_t index, uint8_t *data) +{ + int status; + + status = VL53L1_I2CRead(Dev->I2cDevAddr, index, data, 1); + + if (status) + return -1; + + return 0; +} + +VL53L1X_ERROR VL53L1X::VL53L1_RdWord(VL53L1_DEV Dev, uint16_t index, uint16_t *data) +{ + int status; + uint8_t buffer[2] = {0, 0}; + + status = VL53L1_I2CRead(Dev->I2cDevAddr, index, buffer, 2); + if (!status) + { + *data = (buffer[0] << 8) + buffer[1]; + } + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1_RdDWord(VL53L1_DEV Dev, uint16_t index, uint32_t *data) +{ + int status; + uint8_t buffer[4] = {0, 0, 0, 0}; + + status = VL53L1_I2CRead(Dev->I2cDevAddr, index, buffer, 4); + if (!status) + { + *data = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]; + } + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1_UpdateByte(VL53L1_DEV Dev, uint16_t index, uint8_t AndData, uint8_t OrData) +{ + int status; + uint8_t buffer = 0; + + /* read data direct onto buffer */ + status = VL53L1_I2CRead(Dev->I2cDevAddr, index, &buffer, 1); + if (!status) + { + buffer = (buffer & AndData) | OrData; + status = VL53L1_I2CWrite(Dev->I2cDevAddr, index, &buffer, (uint16_t)1); + } + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1_I2CWrite(uint8_t DeviceAddr, uint16_t RegisterAddr, uint8_t *pBuffer, uint16_t NumByteToWrite) +{ +#ifdef DEBUG_MODE + Serial.print("Beginning transmission to "); + Serial.println(((DeviceAddr) >> 1) & 0x7F); +#endif + dev_i2c->beginTransmission(((uint8_t)(((DeviceAddr) >> 1) & 0x7F))); +#ifdef DEBUG_MODE + Serial.print("Writing port number "); + Serial.println(RegisterAddr); +#endif + uint8_t buffer[2]; + buffer[0] = RegisterAddr >> 8; + buffer[1] = RegisterAddr & 0xFF; + dev_i2c->write(buffer, 2); + for (int i = 0; i < NumByteToWrite; i++) + dev_i2c->write(pBuffer[i]); + + dev_i2c->endTransmission(true); + return 0; +} + +VL53L1X_ERROR VL53L1X::VL53L1_I2CRead(uint8_t DeviceAddr, uint16_t RegisterAddr, uint8_t *pBuffer, uint16_t NumByteToRead) +{ + int status = 0; + + //Loop until the port is transmitted correctly + uint8_t maxAttempts = 5; + for (uint8_t x = 0; x < maxAttempts; x++) + { +#ifdef DEBUG_MODE + Serial.print("Beginning transmission to "); + Serial.println(((DeviceAddr) >> 1) & 0x7F); +#endif + dev_i2c->beginTransmission(((uint8_t)(((DeviceAddr) >> 1) & 0x7F))); +#ifdef DEBUG_MODE + Serial.print("Writing port number "); + Serial.println(RegisterAddr); +#endif + uint8_t buffer[2]; + buffer[0] = RegisterAddr >> 8; + buffer[1] = RegisterAddr & 0xFF; + dev_i2c->write(buffer, 2); + status = dev_i2c->endTransmission(false); + + if (status == 0) + break; + +//Fix for some STM32 boards +//Reinitialize th i2c bus with the default parameters +#ifdef ARDUINO_ARCH_STM32 + if (status) + { + dev_i2c->end(); + dev_i2c->begin(); + } +#endif + //End of fix + } + + dev_i2c->requestFrom(((uint8_t)(((DeviceAddr) >> 1) & 0x7F)), (byte)NumByteToRead); + + int i = 0; + while (dev_i2c->available()) + { + pBuffer[i] = dev_i2c->read(); + i++; + } + + return 0; +} + +VL53L1X_ERROR VL53L1X::VL53L1_GetTickCount( + uint32_t *ptick_count_ms) +{ + + /* Returns current tick count in [ms] */ + + VL53L1X_ERROR status = VL53L1_ERROR_NONE; + + //*ptick_count_ms = timeGetTime(); + *ptick_count_ms = 0; + + return status; +} + +VL53L1X_ERROR VL53L1X::VL53L1_WaitUs(VL53L1_Dev_t *pdev, int32_t wait_us) +{ + (void)pdev; + delay(wait_us / 1000); + return VL53L1_ERROR_NONE; +} + +VL53L1X_ERROR VL53L1X::VL53L1_WaitMs(VL53L1_Dev_t *pdev, int32_t wait_ms) +{ + (void)pdev; + delay(wait_ms); + return VL53L1_ERROR_NONE; +} + +VL53L1X_ERROR VL53L1X::VL53L1_WaitValueMaskEx( + VL53L1_Dev_t *pdev, + uint32_t timeout_ms, + uint16_t index, + uint8_t value, + uint8_t mask, + uint32_t poll_delay_ms) +{ + + /* + * Platform implementation of WaitValueMaskEx V2WReg script command + * + * WaitValueMaskEx( + * duration_ms, + * index, + * value, + * mask, + * poll_delay_ms); + */ + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint32_t start_time_ms = 0; + uint32_t current_time_ms = 0; + uint32_t polling_time_ms = 0; + uint8_t byte_value = 0; + uint8_t found = 0; + + /* calculate time limit in absolute time */ + + VL53L1_GetTickCount(&start_time_ms); + + /* remember current trace functions and temporarily disable + * function logging + */ + + /* wait until value is found, timeout reached on error occurred */ + + while ((status == VL53L1_ERROR_NONE) && + (polling_time_ms < timeout_ms) && + (found == 0)) + { + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_RdByte( + pdev, + index, + &byte_value); + + if ((byte_value & mask) == value) + found = 1; + + if (status == VL53L1_ERROR_NONE && + found == 0 && + poll_delay_ms > 0) + status = VL53L1_WaitMs( + pdev, + poll_delay_ms); + + /* Update polling time (Compare difference rather than absolute to + negate 32bit wrap around issue) */ + VL53L1_GetTickCount(¤t_time_ms); + polling_time_ms = current_time_ms - start_time_ms; + } + + if (found == 0 && status == VL53L1_ERROR_NONE) + status = VL53L1_ERROR_TIME_OUT; + + return status; +} diff --git a/lib/SparkFun_VL53L1X/vl53l1x_class.h b/lib/SparkFun_VL53L1X/vl53l1x_class.h new file mode 100644 index 000000000..ab941df36 --- /dev/null +++ b/lib/SparkFun_VL53L1X/vl53l1x_class.h @@ -0,0 +1,561 @@ +/******************************************************************************* + Copyright © 2018, STMicroelectronics International N.V. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND + NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. + IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __VL53L1X_CLASS_H +#define __VL53L1X_CLASS_H + + +#ifdef _MSC_VER +# ifdef VL53L1X_API_EXPORTS +# define VL53L1X_API __declspec(dllexport) +# else +# define VL53L1X_API +# endif +#else +# define VL53L1X_API +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "Arduino.h" +#include "Wire.h" +#include "RangeSensor.h" +#include "vl53l1_error_codes.h" + + +#define VL53L1X_IMPLEMENTATION_VER_MAJOR 1 +#define VL53L1X_IMPLEMENTATION_VER_MINOR 0 +#define VL53L1X_IMPLEMENTATION_VER_SUB 1 +#define VL53L1X_IMPLEMENTATION_VER_REVISION 0000 + +typedef int8_t VL53L1X_ERROR; + +#define SOFT_RESET 0x0000 +#define VL53L1_I2C_SLAVE__DEVICE_ADDRESS 0x0001 +#define VL53L1_VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND 0x0008 +#define ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS 0x0016 +#define ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS 0x0018 +#define ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS 0x001A +#define ALGO__PART_TO_PART_RANGE_OFFSET_MM 0x001E +#define MM_CONFIG__INNER_OFFSET_MM 0x0020 +#define MM_CONFIG__OUTER_OFFSET_MM 0x0022 +#define GPIO_HV_MUX__CTRL 0x0030 +#define GPIO__TIO_HV_STATUS 0x0031 +#define SYSTEM__INTERRUPT_CONFIG_GPIO 0x0046 +#define PHASECAL_CONFIG__TIMEOUT_MACROP 0x004B +#define RANGE_CONFIG__TIMEOUT_MACROP_A_HI 0x005E +#define RANGE_CONFIG__VCSEL_PERIOD_A 0x0060 +#define RANGE_CONFIG__VCSEL_PERIOD_B 0x0063 +#define RANGE_CONFIG__TIMEOUT_MACROP_B_HI 0x0061 +#define RANGE_CONFIG__TIMEOUT_MACROP_B_LO 0x0062 +#define RANGE_CONFIG__SIGMA_THRESH 0x0064 +#define RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS 0x0066 +#define RANGE_CONFIG__VALID_PHASE_HIGH 0x0069 +#define VL53L1_SYSTEM__INTERMEASUREMENT_PERIOD 0x006C +#define SYSTEM__THRESH_HIGH 0x0072 +#define SYSTEM__THRESH_LOW 0x0074 +#define SD_CONFIG__WOI_SD0 0x0078 +#define SD_CONFIG__INITIAL_PHASE_SD0 0x007A +#define ROI_CONFIG__USER_ROI_CENTRE_SPAD 0x007F +#define ROI_CONFIG__USER_ROI_REQUESTED_GLOBAL_XY_SIZE 0x0080 +#define SYSTEM__SEQUENCE_CONFIG 0x0081 +#define VL53L1_SYSTEM__GROUPED_PARAMETER_HOLD 0x0082 +#define SYSTEM__INTERRUPT_CLEAR 0x0086 +#define SYSTEM__MODE_START 0x0087 +#define VL53L1_RESULT__RANGE_STATUS 0x0089 +#define VL53L1_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0 0x008C +#define RESULT__AMBIENT_COUNT_RATE_MCPS_SD 0x0090 +#define VL53L1_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 0x0096 +#define VL53L1_RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0 0x0098 +#define VL53L1_RESULT__OSC_CALIBRATE_VAL 0x00DE +#define VL53L1_FIRMWARE__SYSTEM_STATUS 0x00E5 +#define VL53L1_IDENTIFICATION__MODEL_ID 0x010F +#define VL53L1_ROI_CONFIG__MODE_ROI_CENTRE_SPAD 0x013E + + +#define VL53L1X_DEFAULT_DEVICE_ADDRESS 0x52 + +/**************************************** + * PRIVATE define do not edit + ****************************************/ + +/** + * @brief defines SW Version + */ +typedef struct { + uint8_t major; /*!< major number */ + uint8_t minor; /*!< minor number */ + uint8_t build; /*!< build number */ + uint32_t revision; /*!< revision number */ +} VL53L1X_Version_t; + + +typedef struct { + + uint8_t I2cDevAddr; + TwoWire *I2cHandle; + +} VL53L1_Dev_t; + +typedef VL53L1_Dev_t *VL53L1_DEV; + + +/* Classes -------------------------------------------------------------------*/ +/** Class representing a VL53L1 sensor component + */ +class VL53L1X : public RangeSensor +{ + public: + /** Constructor + * @param[in] &i2c device I2C to be used for communication + * @param[in] &pin_gpio1 pin Mbed InterruptIn PinName to be used as component GPIO_1 INT + * @param[in] DevAddr device address, 0x52 by default + */ + VL53L1X(TwoWire *i2c, int pin, int pin_gpio1) : RangeSensor(), dev_i2c(i2c), gpio0(pin), gpio1Int(pin_gpio1) + { + MyDevice.I2cDevAddr=VL53L1X_DEFAULT_DEVICE_ADDRESS; + MyDevice.I2cHandle = i2c; + Device = &MyDevice; + if(gpio0 >= 0) + { + pinMode(gpio0, OUTPUT); + } + } + + /** Destructor + */ + virtual ~VL53L1X(){} + /* warning: VL53L1 class inherits from GenericSensor, RangeSensor and LightSensor, that haven`t a destructor. + The warning should request to introduce a virtual destructor to make sure to delete the object */ + + /*** Interface Methods ***/ + /*** High level API ***/ + /** + * @brief PowerOn the sensor + * @return void + */ + /* turns on the sensor */ + virtual void VL53L1_On(void) + { + if(gpio0 >= 0) + { + digitalWrite(gpio0, HIGH); + } + delay(10); + } + + /** + * @brief PowerOff the sensor + * @return void + */ + /* turns off the sensor */ + virtual void VL53L1_Off(void) + { + if(gpio0 >= 0) + { + digitalWrite(gpio0, LOW); + } + delay(10); + } + + /** + * @brief Initialize the sensor with default values + * @return 0 on Success + */ + + VL53L1X_ERROR InitSensor(uint8_t address){ + VL53L1X_ERROR status = 0; + uint8_t sensorState = 0; + VL53L1_Off(); + VL53L1_On(); + status = VL53L1X_SetI2CAddress(address); + +#ifdef DEBUG_MODE + uint8_t byteData; + uint16_t wordData; + status = VL53L1_RdByte(Device, 0x010F, &byteData); + Serial.println("VL53L1X Model_ID: " + String(byteData)); + status = VL53L1_RdByte(Device, 0x0110, &byteData); + Serial.println("VL53L1X Module_Type: " + String(byteData)); + status = VL53L1_RdWord(Device, 0x010F, &wordData); + Serial.println("VL53L1X: " + String(wordData)); +#endif + + + while (!sensorState && !status){ + status = VL53L1X_BootState(&sensorState); + delay(2); + } + if(!status){ + status = VL53L1X_SensorInit(); + } + return status; + } + + + +/** + * + * @brief One time device initialization + * @param void + * @return 0 on success, @a #CALIBRATION_WARNING if failed + */ + virtual int Init() + { + return VL53L1X_SensorInit(); + } + + + + /* Read function of the ID device */ + virtual int ReadID(){ + uint16_t sensorId; + VL53L1X_GetSensorId(&sensorId); + if (sensorId == 0xEEAC) + return 0; + return -1; + } + + + +/** + * @brief Get ranging result and only that + * @param pRange_mm Pointer to range distance + * @return 0 on success + */ + int GetDistance(uint32_t *piData) + { + int status; + uint16_t distance; + status = VL53L1X_GetDistance(&distance); + *piData = (uint32_t) distance; + return status; + } + + +/* VL53L1X_api.h functions */ + + + /** + * @brief This function returns the SW driver version + */ + VL53L1X_ERROR VL53L1X_GetSWVersion(VL53L1X_Version_t *pVersion); + + /** + * @brief This function sets the sensor I2C address used in case multiple devices application, default address 0x52 + */ + VL53L1X_ERROR VL53L1X_SetI2CAddress(uint8_t new_address); + + /** + * @brief This function loads the 135 bytes default values to initialize the sensor. + * @param dev Device address + * @return 0:success, != 0:failed + */ + VL53L1X_ERROR VL53L1X_SensorInit(); + + /** + * @brief This function clears the interrupt, to be called after a ranging data reading + * to arm the interrupt for the next data ready event. + */ + VL53L1X_ERROR VL53L1X_ClearInterrupt(); + + /** + * @brief This function programs the interrupt polarity\n + * 1=active high (default), 0=active low + */ + VL53L1X_ERROR VL53L1X_SetInterruptPolarity(uint8_t IntPol); + + /** + * @brief This function returns the current interrupt polarity\n + * 1=active high (default), 0=active low + */ + VL53L1X_ERROR VL53L1X_GetInterruptPolarity(uint8_t *pIntPol); + + /** + * @brief This function starts the ranging distance operation\n + * The ranging operation is continuous. The clear interrupt has to be done after each get data to allow the interrupt to raise when the next data is ready\n + * 1=active high (default), 0=active low, use SetInterruptPolarity() to change the interrupt polarity if required. + */ + VL53L1X_ERROR VL53L1X_StartRanging(); + + /** + * @brief This function stops the ranging. + */ + VL53L1X_ERROR VL53L1X_StopRanging(); + + /** + * @brief This function checks if the new ranging data is available by polling the dedicated register. + * @param : isDataReady==0 -> not ready; isDataReady==1 -> ready + */ + VL53L1X_ERROR VL53L1X_CheckForDataReady(uint8_t *isDataReady); + + /** + * @brief This function programs the timing budget in ms. + * Predefined values = 15, 20, 33, 50, 100(default), 200, 500. + */ + VL53L1X_ERROR VL53L1X_SetTimingBudgetInMs(uint16_t TimingBudgetInMs); + + /** + * @brief This function returns the current timing budget in ms. + */ + VL53L1X_ERROR VL53L1X_GetTimingBudgetInMs(uint16_t *pTimingBudgetInMs); + + /** + * @brief This function programs the distance mode (1=short, 2=long(default)). + * Short mode max distance is limited to 1.3 m but better ambient immunity.\n + * Long mode can range up to 4 m in the dark with 200 ms timing budget. + */ + VL53L1X_ERROR VL53L1X_SetDistanceMode(uint16_t DistanceMode); + + /** + * @brief This function returns the current distance mode (1=short, 2=long). + */ + VL53L1X_ERROR VL53L1X_GetDistanceMode(uint16_t *pDistanceMode); + + /** + * @brief This function programs the Intermeasurement period in ms\n + * Intermeasurement period must be >/= timing budget. This condition is not checked by the API, + * the customer has the duty to check the condition. Default = 100 ms + */ + VL53L1X_ERROR VL53L1X_SetInterMeasurementInMs(uint16_t InterMeasurementInMs); + + /** + * @brief This function returns the Intermeasurement period in ms. + */ + VL53L1X_ERROR VL53L1X_GetInterMeasurementInMs(uint16_t * pIM); + + /** + * @brief This function returns the boot state of the device (1:booted, 0:not booted) + */ + VL53L1X_ERROR VL53L1X_BootState(uint8_t *state); + + /** + * @brief This function returns the sensor id, sensor Id must be 0xEEAC + */ + VL53L1X_ERROR VL53L1X_GetSensorId(uint16_t *id); + + /** + * @brief This function returns the distance measured by the sensor in mm + */ + VL53L1X_ERROR VL53L1X_GetDistance(uint16_t *distance); + + /** + * @brief This function returns the returned signal per SPAD in kcps/SPAD. + * With kcps stands for Kilo Count Per Second + */ + VL53L1X_ERROR VL53L1X_GetSignalPerSpad(uint16_t *signalPerSp); + + /** + * @brief This function returns the ambient per SPAD in kcps/SPAD + */ + VL53L1X_ERROR VL53L1X_GetAmbientPerSpad(uint16_t *amb); + + /** + * @brief This function returns the returned signal in kcps. + */ + VL53L1X_ERROR VL53L1X_GetSignalRate(uint16_t *signalRate); + + /** + * @brief This function returns the current number of enabled SPADs + */ + VL53L1X_ERROR VL53L1X_GetSpadNb(uint16_t *spNb); + + /** + * @brief This function returns the ambient rate in kcps + */ + VL53L1X_ERROR VL53L1X_GetAmbientRate(uint16_t *ambRate); + + /** + * @brief This function returns the ranging status error \n + * (0:no error, 1:sigma failed, 2:signal failed, ..., 7:wrap-around) + */ + VL53L1X_ERROR VL53L1X_GetRangeStatus(uint8_t *rangeStatus); + + /** + * @brief This function programs the offset correction in mm + * @param OffsetValue:the offset correction value to program in mm + */ + VL53L1X_ERROR VL53L1X_SetOffset(int16_t OffsetValue); + + /** + * @brief This function returns the programmed offset correction value in mm + */ + VL53L1X_ERROR VL53L1X_GetOffset(int16_t *Offset); + + /** + * @brief This function programs the xtalk correction value in cps (Count Per Second).\n + * This is the number of photons reflected back from the cover glass in cps. + */ + VL53L1X_ERROR VL53L1X_SetXtalk(uint16_t XtalkValue); + + /** + * @brief This function returns the current programmed xtalk correction value in cps + */ + VL53L1X_ERROR VL53L1X_GetXtalk(uint16_t *Xtalk); + + /** + * @brief This function programs the threshold detection mode\n + * Example:\n + * VL53L1X_SetDistanceThreshold(dev,100,300,0,1): Below 100 \n + * VL53L1X_SetDistanceThreshold(dev,100,300,1,1): Above 300 \n + * VL53L1X_SetDistanceThreshold(dev,100,300,2,1): Out of window \n + * VL53L1X_SetDistanceThreshold(dev,100,300,3,1): In window \n + * @param dev : device address + * @param ThreshLow(in mm) : the threshold under which one the device raises an interrupt if Window = 0 + * @param ThreshHigh(in mm) : the threshold above which one the device raises an interrupt if Window = 1 + * @param Window detection mode : 0=below, 1=above, 2=out, 3=in + * @param IntOnNoTarget = 1 (No longer used - just use 1) + */ + VL53L1X_ERROR VL53L1X_SetDistanceThreshold(uint16_t ThreshLow, + uint16_t ThreshHigh, uint8_t Window, + uint8_t IntOnNoTarget); + + /** + * @brief This function returns the window detection mode (0=below; 1=above; 2=out; 3=in) + */ + VL53L1X_ERROR VL53L1X_GetDistanceThresholdWindow(uint16_t *window); + + /** + * @brief This function returns the low threshold in mm + */ + VL53L1X_ERROR VL53L1X_GetDistanceThresholdLow(uint16_t *low); + + /** + * @brief This function returns the high threshold in mm + */ + VL53L1X_ERROR VL53L1X_GetDistanceThresholdHigh(uint16_t *high); + + /** + * @brief This function programs the ROI (Region of Interest)\n + * The ROI position is centered, only the ROI size can be reprogrammed.\n + * The smallest acceptable ROI size = 4\n + * @param X:ROI Width; Y=ROI Height + */ + VL53L1X_ERROR VL53L1X_SetROI(uint8_t X, uint8_t Y, uint8_t opticalCenter); + + /** + *@brief This function returns width X and height Y + */ + VL53L1X_ERROR VL53L1X_GetROI_XY(uint16_t *ROI_X, uint16_t *ROI_Y); + + /** + * @brief This function programs a new signal threshold in kcps (default=1024 kcps\n + */ + VL53L1X_ERROR VL53L1X_SetSignalThreshold(uint16_t signal); + + /** + * @brief This function returns the current signal threshold in kcps + */ + VL53L1X_ERROR VL53L1X_GetSignalThreshold(uint16_t *signal); + + /** + * @brief This function programs a new sigma threshold in mm (default=15 mm) + */ + VL53L1X_ERROR VL53L1X_SetSigmaThreshold(uint16_t sigma); + + /** + * @brief This function returns the current sigma threshold in mm + */ + VL53L1X_ERROR VL53L1X_GetSigmaThreshold(uint16_t *signal); + + /** + * @brief This function performs the temperature calibration. + * It is recommended to call this function any time the temperature might have changed by more than 8 deg C + * without sensor ranging activity for an extended period. + */ + VL53L1X_ERROR VL53L1X_StartTemperatureUpdate(); + + + /* VL53L1X_calibration.h functions */ + + /** + * @brief This function performs the offset calibration.\n + * The function returns the offset value found and programs the offset compensation into the device. + * @param TargetDistInMm target distance in mm, ST recommended 100 mm + * Target reflectance = grey17% + * @return 0:success, !=0: failed + * @return offset pointer contains the offset found in mm + */ + int8_t VL53L1X_CalibrateOffset(uint16_t TargetDistInMm, int16_t *offset); + + /** + * @brief This function performs the xtalk calibration.\n + * The function returns the xtalk value found and programs the xtalk compensation to the device + * @param TargetDistInMm target distance in mm\n + * The target distance : the distance where the sensor start to "under range"\n + * due to the influence of the photons reflected back from the cover glass becoming strong\n + * It's also called inflection point\n + * Target reflectance = grey 17% + * @return 0: success, !=0: failed + * @return xtalk pointer contains the xtalk value found in cps (number of photons in count per second) + */ + int8_t VL53L1X_CalibrateXtalk(uint16_t TargetDistInMm, uint16_t *xtalk); + + + + + + protected: + + + /* Write and read functions from I2C */ + + VL53L1X_ERROR VL53L1_WrByte(VL53L1_DEV dev, uint16_t index, uint8_t data); + VL53L1X_ERROR VL53L1_WrWord(VL53L1_DEV dev, uint16_t index, uint16_t data); + VL53L1X_ERROR VL53L1_WrDWord(VL53L1_DEV dev, uint16_t index, uint32_t data); + VL53L1X_ERROR VL53L1_RdByte(VL53L1_DEV dev, uint16_t index, uint8_t *data); + VL53L1X_ERROR VL53L1_RdWord(VL53L1_DEV dev, uint16_t index, uint16_t *data); + VL53L1X_ERROR VL53L1_RdDWord(VL53L1_DEV dev, uint16_t index, uint32_t *data); + VL53L1X_ERROR VL53L1_UpdateByte(VL53L1_DEV dev, uint16_t index, uint8_t AndData, uint8_t OrData); + + VL53L1X_ERROR VL53L1_WriteMulti(VL53L1_DEV Dev, uint16_t index, uint8_t *pdata, uint32_t count); + VL53L1X_ERROR VL53L1_ReadMulti(VL53L1_DEV Dev, uint16_t index, uint8_t *pdata, uint32_t count); + + VL53L1X_ERROR VL53L1_I2CWrite(uint8_t dev, uint16_t index, uint8_t *data, uint16_t number_of_bytes); + VL53L1X_ERROR VL53L1_I2CRead(uint8_t dev, uint16_t index, uint8_t *data, uint16_t number_of_bytes); + VL53L1X_ERROR VL53L1_GetTickCount(uint32_t *ptick_count_ms); + VL53L1X_ERROR VL53L1_WaitUs(VL53L1_Dev_t *pdev, int32_t wait_us); + VL53L1X_ERROR VL53L1_WaitMs(VL53L1_Dev_t *pdev, int32_t wait_ms); + + VL53L1X_ERROR VL53L1_WaitValueMaskEx(VL53L1_Dev_t *pdev, uint32_t timeout_ms, uint16_t index, uint8_t value, uint8_t mask, uint32_t poll_delay_ms); + + + + protected: + + /* IO Device */ + TwoWire *dev_i2c; + /* Digital out pin */ + int gpio0; + int gpio1Int; + /* Device data */ + VL53L1_Dev_t MyDevice; + VL53L1_DEV Device; +}; + + +#endif /* _VL53L1X_CLASS_H_ */ diff --git a/src/_P113_VL53L1X.ino b/src/_P113_VL53L1X.ino new file mode 100644 index 000000000..a354cd51b --- /dev/null +++ b/src/_P113_VL53L1X.ino @@ -0,0 +1,148 @@ +#include "_Plugin_Helper.h" +#ifdef USES_P113 +//####################################################################################################### +//########################### Plugin 113 VL53L1X I2C Ranging LIDAR ################################# +//####################################################################################################### + +// Changelog: +// 2021-04-05, tonhuisman: Add VL53L1X Time of Flight sensor to main repo (similar to but not compatible with VL53L0X) + +// needs SparkFun_VL53L1X library from https://github.com/sparkfun/SparkFun_VL53L1X_Arduino_Library + +#include "src/PluginStructs/P113_data_struct.h" + +#define PLUGIN_113 +#define PLUGIN_ID_113 113 +#define PLUGIN_NAME_113 "Distance - VL53L1X (400cm) [TESTING]" +#define PLUGIN_VALUENAME1_113 "Distance" +#define PLUGIN_VALUENAME2_113 "Ambient" + + +boolean Plugin_113_init[3] = {false, false, false}; + +boolean Plugin_113(byte function, struct EventStruct *event, String& string) +{ + boolean success = false; + + switch (function) + { + case PLUGIN_DEVICE_ADD: + { + Device[++deviceCount].Number = PLUGIN_ID_113; + Device[deviceCount].Type = DEVICE_TYPE_I2C; + Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_SINGLE; + Device[deviceCount].Ports = 0; + Device[deviceCount].PullUpOption = false; + Device[deviceCount].InverseLogicOption = false; + Device[deviceCount].FormulaOption = true; + Device[deviceCount].ValueCount = 2; + Device[deviceCount].SendDataOption = true; + Device[deviceCount].TimerOption = true; + Device[deviceCount].TimerOptional = true; + Device[deviceCount].GlobalSyncOption = true; + break; + } + + case PLUGIN_GET_DEVICENAME: + { + string = F(PLUGIN_NAME_113); + break; + } + + case PLUGIN_GET_DEVICEVALUENAMES: + { + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_113)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_113)); + break; + } + case PLUGIN_WEBFORM_SHOW_I2C_PARAMS: + { + byte choice = PCONFIG(0); + int optionValues[2] = { 0x29, 0x30 }; + addFormSelectorI2C(F("plugin_113_vl53l1x_i2c"), 2, optionValues, choice); + // addFormNote(F("SDO Low=0x29, High=0x30")); + break; + } + case PLUGIN_WEBFORM_LOAD: + { + unsigned int choiceMode2 = PCONFIG(1); + { + String optionsMode2[6]; + optionsMode2[0] = F("100ms (Normal)"); + optionsMode2[1] = F("20ms (Fastest)"); + optionsMode2[2] = F("33ms (Fast)"); + optionsMode2[3] = F("50ms"); + optionsMode2[4] = F("200ms (Accurate)"); + optionsMode2[5] = F("500ms"); + int optionValuesMode2[6] = { 100, 20, 33, 50, 200, 500 }; + addFormSelector(F("Timing"), F("plugin_113_vl53l1x_timing"), 6, optionsMode2, optionValuesMode2, choiceMode2); + } + + int choiceMode3 = PCONFIG(2); + { + String optionsMode3[2]; + optionsMode3[0] = F("Normal (~130cm)"); + optionsMode3[1] = F("Long (~400cm)"); + int optionValuesMode3[2] = {0, 1 }; + addFormSelector(F("Range"), F("plugin_113_vl53l1x_range"), 2, optionsMode3, optionValuesMode3, choiceMode3); + } + addFormCheckBox(F("Send event when value unchanged"), F("plugin_113_vl53l1x_notchanged"), PCONFIG(3) == 1); + + success = true; + break; + } + + case PLUGIN_WEBFORM_SAVE: + { + PCONFIG(0) = getFormItemInt(F("plugin_113_vl53l1x_i2c")); + PCONFIG(1) = getFormItemInt(F("plugin_113_vl53l1x_timing")); + PCONFIG(2) = getFormItemInt(F("plugin_113_vl53l1x_range")); + PCONFIG(3) = isFormItemChecked(F("plugin_113_vl53l1x_notchanged")) ? 1 : 0; + + success = true; + break; + } + + case PLUGIN_INIT: + { + initPluginTaskData(event->TaskIndex, new (std::nothrow) P113_data_struct(PCONFIG(0), PCONFIG(1), PCONFIG(2) == 1)); + P113_data_struct *P113_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P113_data) { + success = P113_data->begin(); // Start the sensor + } + break; + } + case PLUGIN_READ: + { + P113_data_struct *P113_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P113_data) { + uint16_t dist = P113_data->readDistance(); + uint16_t ambient = P113_data->readAmbient(); + if (P113_data->isReadSuccessful() && (UserVar[event->BaseVarIndex] != dist || PCONFIG(3) == 1) && dist != 0xFFFF) { + UserVar[event->BaseVarIndex + 0] = dist; + UserVar[event->BaseVarIndex + 1] = ambient; + success = true; + } + } + break; + } + case PLUGIN_FIFTY_PER_SECOND: + { + P113_data_struct *P113_data = static_cast(getPluginTaskData(event->TaskIndex)); + if (nullptr != P113_data) { + if (P113_data->startRead()) { + if (P113_data->readAvailable() && Settings.TaskDeviceTimer[event->TaskIndex] == 0) { // Trigger as soon as there's a valid measurement and the time-out is set to 0 + Scheduler.schedule_task_device_timer(event->TaskIndex, millis() + 10); + } + } + } + break; + } + } + return success; +} + + +#endif diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index fab878573..b468b3dda 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -927,6 +927,7 @@ To create/register a plugin, you have to : #define USES_P107 // SI1145 UV index //#define USES_P108 // DDS238-x ZN MODBUS energy meter (was P224 in the Playground) // #define USES_P111 // RC522 RFID reader + // #define USES_P113 // VL53L1X ToF #endif diff --git a/src/src/PluginStructs/P113_data_struct.cpp b/src/src/PluginStructs/P113_data_struct.cpp new file mode 100644 index 000000000..1c9ece31d --- /dev/null +++ b/src/src/PluginStructs/P113_data_struct.cpp @@ -0,0 +1,108 @@ +#include "../PluginStructs/P113_data_struct.h" + +#ifdef USES_P113 + +P113_data_struct::P113_data_struct(uint8_t i2c_addr, int timing, bool range) : i2cAddress(i2c_addr), timing(timing), range(range) {} + +//**************************************************************************/ +// Initialize VL53L1X +//**************************************************************************/ +bool P113_data_struct::begin() { + + initState = true; + + sensor.setI2CAddress(i2cAddress); // Initialize for configured address + + if (sensor.begin() != 0) { + String log = F("VL53L1X: Sensor not found, init failed for 0x"); + log += String(i2cAddress, HEX); + addLog(LOG_LEVEL_INFO, log); + initState = false; + return initState; + } + + sensor.setTimingBudgetInMs(timing); + + if (range) { + sensor.setDistanceModeLong(); + } else { + sensor.setDistanceModeShort(); + } + + return initState; +} + +bool P113_data_struct::startRead() { + if (initState && !readActive) { + sensor.startRanging(); + readActive = true; + distance = -1; + } + return readActive; +} + +bool P113_data_struct::readAvailable() { + bool ready = sensor.checkForDataReady(); + if (ready) { + distance = sensor.getDistance(); + sensor.clearInterrupt(); + sensor.stopRanging(); + // readActive = false; + } + return ready; +} + +uint16_t P113_data_struct::readDistance() { + + success = false; + +#if defined(P113_DEBUG) || defined (P113_DEBUG_DEBUG) + String log; +#endif +#ifdef P113_DEBUG_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + log = F("VL53L1X : idx: 0x"); + log += String(i2cAddress, HEX); + log += F(" init: "); + log += String(initState, BIN); + addLog(LOG_LEVEL_DEBUG, log); + } +#endif // P113_DEBUG_DEBUG + + // if (initState && readActive) { + success = true; + // distance = sensor.getDistance(); + // sensor.clearInterrupt(); + // sensor.stopRanging(); + readActive = false; + if ( distance >= 8190 ) { + #ifdef P113_DEBUG_DEBUG + addLog(LOG_LEVEL_DEBUG, "VL53L1X: NO MEASUREMENT"); + #endif // P113_DEBUG_DEBUG + success = false; + } + +#ifdef P113_DEBUG + log = F("VL53L1X: Address: 0x"); + log += String(i2cAddress, HEX); + log += F(" / Timing: "); + log += String(timing, DEC); + log += F(" / Long Range: "); + log += String(range, BIN); + log += F(" / Distance: "); + log += distance; + addLog(LOG_LEVEL_INFO, log); +#endif // P113_DEBUG + // } + return distance; +}; + +uint16_t P113_data_struct::readAmbient() { + return sensor.getAmbientRate(); +} + +bool P113_data_struct::isReadSuccessful() { + return success; +}; + +#endif // ifdef USES_P113 diff --git a/src/src/PluginStructs/P113_data_struct.h b/src/src/PluginStructs/P113_data_struct.h new file mode 100644 index 000000000..5b6b11985 --- /dev/null +++ b/src/src/PluginStructs/P113_data_struct.h @@ -0,0 +1,44 @@ +#ifndef PLUGINSTRUCTS_P113_DATA_STRUCT_H +#define PLUGINSTRUCTS_P113_DATA_STRUCT_H + +#include "../../_Plugin_Helper.h" +#ifdef USES_P113 + +#define P113_DEBUG // Enable debugging output (INFO loglevel) +#define P113_DEBUG_DEBUG // Enable extended debugging output (DEBUG loglevel) + +#ifdef LIMIT_BUILD_SIZE + #ifdef P113_DEBUG_DEBUG + #undef P113_DEBUG_DEBUG + #endif +#endif + +#include +#include + +struct P113_data_struct : public PluginTaskData_base { +public: + + P113_data_struct(uint8_t i2c_addr, int timing, bool range); + + bool begin(); + bool startRead(); + bool readAvailable(); + uint16_t readDistance(); + uint16_t readAmbient(); + bool isReadSuccessful(); + +private: + + SFEVL53L1X sensor; + + uint8_t i2cAddress; + bool initState = false; + int timing; + bool range; + bool success = false; + bool readActive = false; + uint16_t distance; +}; +#endif // ifdef USES_P113 +#endif // ifndef PLUGINSTRUCTS_P113_DATA_STRUCT_H diff --git a/src/src/WebServer/I2C_Scanner.cpp b/src/src/WebServer/I2C_Scanner.cpp index 9ca36e37e..557ab0bdd 100644 --- a/src/src/WebServer/I2C_Scanner.cpp +++ b/src/src/WebServer/I2C_Scanner.cpp @@ -164,7 +164,7 @@ String getKnownI2Cdevice(byte address) { result = F("PCF8574,MCP23017,LCD,PN532"); break; case 0x29: - result = F("TSL2561,TCS34725"); + result = F("TSL2561,TCS34725,VL53L1X"); break; case 0x38: case 0x3A: From 4022a771996f08f41a77f1659d6ec532257b1d59 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Mon, 5 Apr 2021 16:16:07 +0200 Subject: [PATCH 2/7] [P113] Add VL53L1X to Custom-sample.h (disabled) --- src/Custom-sample.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Custom-sample.h b/src/Custom-sample.h index 05dc6d635..84ff76875 100644 --- a/src/Custom-sample.h +++ b/src/Custom-sample.h @@ -291,6 +291,7 @@ // #define USES_P106 // BME680 // #define USES_P107 // Si1145 // #define USES_P111 // RF522 RFID reader +// #define USES_P113 // VL53L1X ToF // Special plugins needing IR library From 7487ff86900c59642a68f7b6a55b04cdf08fd084 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Mon, 5 Apr 2021 20:20:15 +0200 Subject: [PATCH 3/7] [P113] Add Documentation --- docs/source/Hardware/Hardware.rst | 2 + docs/source/Plugin/P113.rst | 100 ++++++++++++++++++ .../Plugin/P113_DeviceConfiguration.png | Bin 0 -> 40024 bytes docs/source/Plugin/P113_RangeOptions.png | Bin 0 -> 9657 bytes docs/source/Plugin/P113_TimingOptions.png | Bin 0 -> 11359 bytes docs/source/Plugin/_Plugin.rst | 6 ++ docs/source/Plugin/_plugin_categories.repl | 1 + .../Plugin/_plugin_substitutions_p11x.repl | 13 +++ 8 files changed, 122 insertions(+) create mode 100644 docs/source/Plugin/P113.rst create mode 100644 docs/source/Plugin/P113_DeviceConfiguration.png create mode 100644 docs/source/Plugin/P113_RangeOptions.png create mode 100644 docs/source/Plugin/P113_TimingOptions.png diff --git a/docs/source/Hardware/Hardware.rst b/docs/source/Hardware/Hardware.rst index b4d0f5c85..bb4553f05 100644 --- a/docs/source/Hardware/Hardware.rst +++ b/docs/source/Hardware/Hardware.rst @@ -1,3 +1,5 @@ +.. _Hardware_page: + Hardware page ************* diff --git a/docs/source/Plugin/P113.rst b/docs/source/Plugin/P113.rst new file mode 100644 index 000000000..ed99246f8 --- /dev/null +++ b/docs/source/Plugin/P113.rst @@ -0,0 +1,100 @@ +.. include:: ../Plugin/_plugin_substitutions_p11x.repl +.. _P113_page: + +|P113_typename| +================================================== + +|P113_shortinfo| + +Plugin details +-------------- + +Type: |P113_type| + +Name: |P113_name| + +Status: |P113_status| + +GitHub: |P113_github|_ + +Maintainer: |P113_maintainer| + +Used libraries: |P113_usedlibraries| + +Description +----------- + +This I2C sensor with 2 ranges offers distance measurement based on a Laser Time-of-Flight sensor. Output result for Distance is in millimeters. + +The VL53L1X sensor can measure either 0 - 130cm (Normal range) or 0 - 400cm (Long range), where the closely related VL53L0X can measure up to 200cm. The API's for these sensors are not compatible, so different libraries are used. + +Configuration +-------------- + +.. image:: P113_DeviceConfiguration.png + +**Name** A unique name should be entered here. + +**Enabled** The device can be disabled or enabled. When not enabled the device should not use any resources. + +I2C Options +^^^^^^^^^^^^ + +The available settings here depend on the build used. At least the **Force Slow I2C speed** option is available, but selections for the I2C Multiplexer can also be shown. For details see the :ref:`Hardware_page` + +**I2C Address**: The address the device is using. The chip allows to select the secondary address and keep that active until the next power-cycle of the sensor. + +Device Settings +^^^^^^^^^^^^^^^^ + +**Timing**: The timing setting of the sensor determines the accuracy of the measurement. There are 6 options available: + +.. image:: P113_TimingOptions.png + +*100 ms (Normal)* The default value, using an integration time of 100 msec. + +*20ms (Fastest)* The fastest but least accurate measurement. Should only be used for Normal range (see below). + +*33ms (Fast)* A fast but not very accurate measurement. + +*50ms* A somewhat more accurate measurement. + +*200ms Accurate* A slower but far more accurate measurement. For use with a longer read interval setting (30-60 sec). + +*500ms* The longest integration time available. For use with long read interval settings. + +**Range**: the measuring ranges: + +.. image:: P113_RangeOptions.png + +*Normal* For measurements in the 0 to 1300 millimeter (130cm) range. + +*Long* For measurements in the 0 to 4000 millimeter (400cm) range (but somewhat less accurate). + +**Send event when value unchanged** To avoid many of the same events when the measurement is stable, this option is off by default. When enabled, every measurement will cause an event, and send the data to any enabled Controllers. + +The Data Acquisition, Send to Controller and Interval settings are standard available configuration items. Send to Controller only when one or more Controllers are configured. + +**Interval** By default, Interval will be set to 0, meaning that once a measurement is completed, it will be read. This implies that quite a lot of events will be generated, possibly putting quite some load on the ESP. Setting this to 1 or 2 seconds, usually offers a reasonable response time. + +Values +^^^^^^ + +The measured distance is available in ``Distance``. A formula can be set to recalculate, f.e. to centimeters using ``%value%/10``. The number of decimals is by default set to 2, but for use with millimeters distance it can be set to 0, as no decimals are provided from the measurement. + +The Ambient ligthing condition during measurement is available in ``Ambient``. The unit is kcps (Photons per second, recalculated to kilo count per second) + +.. Events +.. ~~~~~~ + +.. .. include:: P113_events.repl + + + +Change log +---------- + +.. versionchanged:: 2.0 + ... + + |added| 2021-04-05 Added to main repository Plugin 113, based on a copy of Plugin 110 Distance - VL53L0X diff --git a/docs/source/Plugin/P113_DeviceConfiguration.png b/docs/source/Plugin/P113_DeviceConfiguration.png new file mode 100644 index 0000000000000000000000000000000000000000..7fcf59af51aaf07669815d0be1ba42f0689de49d GIT binary patch literal 40024 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 qK`MMmeZ_&=W~OoepTvszRNhF7?| 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 literal 0 HcmV?d00001 diff --git a/docs/source/Plugin/P113_RangeOptions.png b/docs/source/Plugin/P113_RangeOptions.png new file mode 100644 index 0000000000000000000000000000000000000000..b19c910a50de67309f2dd1af6a65cd598bb2b647 GIT binary patch literal 9657 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 Y(XdeI0FAGez%$Up`)c<}?-~XE9}c$500000 literal 0 HcmV?d00001 diff --git a/docs/source/Plugin/P113_TimingOptions.png b/docs/source/Plugin/P113_TimingOptions.png new file mode 100644 index 0000000000000000000000000000000000000000..314eb0f2941556db03efed732a5df0489fca0a23 GIT binary patch literal 11359 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 Date: Mon, 5 Apr 2021 21:11:08 +0200 Subject: [PATCH 4/7] [P113] Add secondary address to I2C_Scanner --- src/src/WebServer/I2C_Scanner.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/src/WebServer/I2C_Scanner.cpp b/src/src/WebServer/I2C_Scanner.cpp index 557ab0bdd..0ff459828 100644 --- a/src/src/WebServer/I2C_Scanner.cpp +++ b/src/src/WebServer/I2C_Scanner.cpp @@ -166,6 +166,9 @@ String getKnownI2Cdevice(byte address) { case 0x29: result = F("TSL2561,TCS34725,VL53L1X"); break; + case 0x30: + result = F("VL53L1X"); + break; case 0x38: case 0x3A: case 0x3B: From c879c3516f42409a9c74c1f187c09e0334be0606 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Tue, 6 Apr 2021 21:11:19 +0200 Subject: [PATCH 5/7] [P113] Removed Interval optional setting, minor source cleanup --- src/_P113_VL53L1X.ino | 12 +++-- src/src/PluginStructs/P113_data_struct.cpp | 53 ++++++++++------------ src/src/PluginStructs/P113_data_struct.h | 2 +- 3 files changed, 33 insertions(+), 34 deletions(-) diff --git a/src/_P113_VL53L1X.ino b/src/_P113_VL53L1X.ino index a354cd51b..ebdb7ad73 100644 --- a/src/_P113_VL53L1X.ino +++ b/src/_P113_VL53L1X.ino @@ -5,6 +5,7 @@ //####################################################################################################### // Changelog: +// 2021-04-06, tonhuisman: Remove Interval optional attribute to avoid system overload, cleanup source // 2021-04-05, tonhuisman: Add VL53L1X Time of Flight sensor to main repo (similar to but not compatible with VL53L0X) // needs SparkFun_VL53L1X library from https://github.com/sparkfun/SparkFun_VL53L1X_Arduino_Library @@ -18,8 +19,6 @@ #define PLUGIN_VALUENAME2_113 "Ambient" -boolean Plugin_113_init[3] = {false, false, false}; - boolean Plugin_113(byte function, struct EventStruct *event, String& string) { boolean success = false; @@ -38,7 +37,6 @@ boolean Plugin_113(byte function, struct EventStruct *event, String& string) Device[deviceCount].ValueCount = 2; Device[deviceCount].SendDataOption = true; Device[deviceCount].TimerOption = true; - Device[deviceCount].TimerOptional = true; Device[deviceCount].GlobalSyncOption = true; break; } @@ -55,6 +53,7 @@ boolean Plugin_113(byte function, struct EventStruct *event, String& string) strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_113)); break; } + case PLUGIN_WEBFORM_SHOW_I2C_PARAMS: { byte choice = PCONFIG(0); @@ -63,6 +62,7 @@ boolean Plugin_113(byte function, struct EventStruct *event, String& string) // addFormNote(F("SDO Low=0x29, High=0x30")); break; } + case PLUGIN_WEBFORM_LOAD: { unsigned int choiceMode2 = PCONFIG(1); @@ -113,6 +113,7 @@ boolean Plugin_113(byte function, struct EventStruct *event, String& string) } break; } + case PLUGIN_READ: { P113_data_struct *P113_data = static_cast(getPluginTaskData(event->TaskIndex)); @@ -128,6 +129,7 @@ boolean Plugin_113(byte function, struct EventStruct *event, String& string) } break; } + case PLUGIN_FIFTY_PER_SECOND: { P113_data_struct *P113_data = static_cast(getPluginTaskData(event->TaskIndex)); @@ -140,9 +142,9 @@ boolean Plugin_113(byte function, struct EventStruct *event, String& string) } break; } + } return success; } - -#endif +#endif // USES_P113 diff --git a/src/src/PluginStructs/P113_data_struct.cpp b/src/src/PluginStructs/P113_data_struct.cpp index 1c9ece31d..eac87b101 100644 --- a/src/src/PluginStructs/P113_data_struct.cpp +++ b/src/src/PluginStructs/P113_data_struct.cpp @@ -56,10 +56,10 @@ uint16_t P113_data_struct::readDistance() { success = false; -#if defined(P113_DEBUG) || defined (P113_DEBUG_DEBUG) + #if defined(P113_DEBUG) || defined (P113_DEBUG_DEBUG) String log; -#endif -#ifdef P113_DEBUG_DEBUG + #endif + #ifdef P113_DEBUG_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { log = F("VL53L1X : idx: 0x"); log += String(i2cAddress, HEX); @@ -67,33 +67,30 @@ uint16_t P113_data_struct::readDistance() { log += String(initState, BIN); addLog(LOG_LEVEL_DEBUG, log); } -#endif // P113_DEBUG_DEBUG + #endif // P113_DEBUG_DEBUG - // if (initState && readActive) { - success = true; - // distance = sensor.getDistance(); - // sensor.clearInterrupt(); - // sensor.stopRanging(); - readActive = false; - if ( distance >= 8190 ) { - #ifdef P113_DEBUG_DEBUG - addLog(LOG_LEVEL_DEBUG, "VL53L1X: NO MEASUREMENT"); - #endif // P113_DEBUG_DEBUG - success = false; - } + success = true; + readActive = false; + + if ( distance >= 8190 ) { + #ifdef P113_DEBUG_DEBUG + addLog(LOG_LEVEL_DEBUG, "VL53L1X: NO MEASUREMENT"); + #endif // P113_DEBUG_DEBUG + success = false; + } + + #ifdef P113_DEBUG + log = F("VL53L1X: Address: 0x"); + log += String(i2cAddress, HEX); + log += F(" / Timing: "); + log += String(timing, DEC); + log += F(" / Long Range: "); + log += String(range, BIN); + log += F(" / Distance: "); + log += distance; + addLog(LOG_LEVEL_INFO, log); + #endif // P113_DEBUG -#ifdef P113_DEBUG - log = F("VL53L1X: Address: 0x"); - log += String(i2cAddress, HEX); - log += F(" / Timing: "); - log += String(timing, DEC); - log += F(" / Long Range: "); - log += String(range, BIN); - log += F(" / Distance: "); - log += distance; - addLog(LOG_LEVEL_INFO, log); -#endif // P113_DEBUG - // } return distance; }; diff --git a/src/src/PluginStructs/P113_data_struct.h b/src/src/PluginStructs/P113_data_struct.h index 5b6b11985..9bbf67256 100644 --- a/src/src/PluginStructs/P113_data_struct.h +++ b/src/src/PluginStructs/P113_data_struct.h @@ -9,7 +9,7 @@ #ifdef LIMIT_BUILD_SIZE #ifdef P113_DEBUG_DEBUG - #undef P113_DEBUG_DEBUG + #undef P113_DEBUG_DEBUG #endif #endif From 39231876558a5a2c36c9bcbb66d9a7e7c428eb6a Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 10 Apr 2021 14:06:55 +0200 Subject: [PATCH 6/7] [P113] Add 'Trigger delta' option, updated documentation --- docs/source/Plugin/P113.rst | 18 +++++++++++------- .../Plugin/P113_DeviceConfiguration.png | Bin 40024 -> 45524 bytes docs/source/Plugin/P113_RangeOptions.png | Bin 9657 -> 7065 bytes docs/source/Plugin/P113_TimingOptions.png | Bin 11359 -> 11870 bytes src/_P113_VL53L1X.ino | 15 ++++++++++++++- 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/docs/source/Plugin/P113.rst b/docs/source/Plugin/P113.rst index ed99246f8..582333209 100644 --- a/docs/source/Plugin/P113.rst +++ b/docs/source/Plugin/P113.rst @@ -67,22 +67,27 @@ Device Settings .. image:: P113_RangeOptions.png -*Normal* For measurements in the 0 to 1300 millimeter (130cm) range. +*Normal (~130cm)* For measurements in the 0 to 1300 millimeter (130cm) range. -*Long* For measurements in the 0 to 4000 millimeter (400cm) range (but somewhat less accurate). +*Long (~400cm)* For measurements in the 0 to 4000 millimeter (400cm) range (but somewhat less accurate). + +**Send event when value unchanged** To avoid many of the same events when the measurement is stable, this option is off by default. When enabled, every measurement will cause an event, and send the data to any enabled Controller. + +**Trigger delta** To avoid triggering many events with only a small difference in distance the 'Trigger delta' option is available. This can be set to only trigger an event when the new distance is at least the delta less or more than the previous measurement. + +NB: This setting is ignored if 'Send event when value unchanged' is checked! -**Send event when value unchanged** To avoid many of the same events when the measurement is stable, this option is off by default. When enabled, every measurement will cause an event, and send the data to any enabled Controllers. The Data Acquisition, Send to Controller and Interval settings are standard available configuration items. Send to Controller only when one or more Controllers are configured. -**Interval** By default, Interval will be set to 0, meaning that once a measurement is completed, it will be read. This implies that quite a lot of events will be generated, possibly putting quite some load on the ESP. Setting this to 1 or 2 seconds, usually offers a reasonable response time. +**Interval** By default, Interval will be set to 60 sec. Setting this to 1 or 2 seconds, usually offers a reasonable response time. Values ^^^^^^ The measured distance is available in ``Distance``. A formula can be set to recalculate, f.e. to centimeters using ``%value%/10``. The number of decimals is by default set to 2, but for use with millimeters distance it can be set to 0, as no decimals are provided from the measurement. -The Ambient ligthing condition during measurement is available in ``Ambient``. The unit is kcps (Photons per second, recalculated to kilo count per second) +The Ambient lighting condition during measurement is available in ``Ambient``. The unit is kcps (Photons per second, recalculated to kilo count per second) .. Events .. ~~~~~~ @@ -95,6 +100,5 @@ Change log ---------- .. versionchanged:: 2.0 - ... - |added| 2021-04-05 Added to main repository Plugin 113, based on a copy of Plugin 110 Distance - VL53L0X + |added| 2021-04-05 Added to main repository as Plugin 113 Distance - VL53L1X (400cm), based on a copy of Plugin 110 Distance - VL53L0X (200cm) diff --git a/docs/source/Plugin/P113_DeviceConfiguration.png b/docs/source/Plugin/P113_DeviceConfiguration.png index 7fcf59af51aaf07669815d0be1ba42f0689de49d..147c4deadb38e2517504e379627355e23d2c1836 100644 GIT binary patch literal 45524 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 BL7M;o literal 40024 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 qK`MMmeZ_&=W~OoepTvszRNhF7?| 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 diff --git a/docs/source/Plugin/P113_RangeOptions.png b/docs/source/Plugin/P113_RangeOptions.png index b19c910a50de67309f2dd1af6a65cd598bb2b647..6471905674f7e982b5f9fab4b0934037cde9ddcb 100644 GIT binary patch literal 7065 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 literal 9657 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 Y(XdeI0FAGez%$Up`)c<}?-~XE9}c$500000 diff --git a/docs/source/Plugin/P113_TimingOptions.png b/docs/source/Plugin/P113_TimingOptions.png index 314eb0f2941556db03efed732a5df0489fca0a23..ee318e7d6e2ddb661feb17b60cabe6b0524eec68 100644 GIT binary patch literal 11870 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 @@ -121,7 +133,8 @@ boolean Plugin_113(byte function, struct EventStruct *event, String& string) if (nullptr != P113_data) { uint16_t dist = P113_data->readDistance(); uint16_t ambient = P113_data->readAmbient(); - if (P113_data->isReadSuccessful() && (UserVar[event->BaseVarIndex] != dist || PCONFIG(3) == 1) && dist != 0xFFFF) { + bool triggered = (dist > UserVar[event->BaseVarIndex] + PCONFIG(4)) || (dist < UserVar[event->BaseVarIndex] - PCONFIG(4)); + if (P113_data->isReadSuccessful() && (triggered || PCONFIG(3) == 1) && dist != 0xFFFF) { UserVar[event->BaseVarIndex + 0] = dist; UserVar[event->BaseVarIndex + 1] = ambient; success = true; From 00a027fba53b5e45d3ac7dfd731db9e4a187b182 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 10 Apr 2021 14:15:32 +0200 Subject: [PATCH 7/7] [P113] Apply uncrustify source formatting --- src/_P113_VL53L1X.ino | 224 +++++++++++---------- src/src/PluginStructs/P113_data_struct.cpp | 37 ++-- src/src/PluginStructs/P113_data_struct.h | 24 ++- 3 files changed, 146 insertions(+), 139 deletions(-) diff --git a/src/_P113_VL53L1X.ino b/src/_P113_VL53L1X.ino index 6bcbf0f88..8209f3072 100644 --- a/src/_P113_VL53L1X.ino +++ b/src/_P113_VL53L1X.ino @@ -1,8 +1,9 @@ #include "_Plugin_Helper.h" #ifdef USES_P113 -//####################################################################################################### -//########################### Plugin 113 VL53L1X I2C Ranging LIDAR ################################# -//####################################################################################################### + +// ####################################################################################################### +// ########################### Plugin 113 VL53L1X I2C Ranging LIDAR ################################# +// ####################################################################################################### // Changelog: // 2021-04-06, tonhuisman: Remove Interval optional attribute to avoid system overload, cleanup source @@ -10,13 +11,13 @@ // needs SparkFun_VL53L1X library from https://github.com/sparkfun/SparkFun_VL53L1X_Arduino_Library -#include "src/PluginStructs/P113_data_struct.h" +# include "src/PluginStructs/P113_data_struct.h" -#define PLUGIN_113 -#define PLUGIN_ID_113 113 -#define PLUGIN_NAME_113 "Distance - VL53L1X (400cm) [TESTING]" -#define PLUGIN_VALUENAME1_113 "Distance" -#define PLUGIN_VALUENAME2_113 "Ambient" +# define PLUGIN_113 +# define PLUGIN_ID_113 113 +# define PLUGIN_NAME_113 "Distance - VL53L1X (400cm) [TESTING]" +# define PLUGIN_VALUENAME1_113 "Distance" +# define PLUGIN_VALUENAME2_113 "Ambient" boolean Plugin_113(byte function, struct EventStruct *event, String& string) @@ -26,136 +27,139 @@ boolean Plugin_113(byte function, struct EventStruct *event, String& string) switch (function) { case PLUGIN_DEVICE_ADD: - { - Device[++deviceCount].Number = PLUGIN_ID_113; - Device[deviceCount].Type = DEVICE_TYPE_I2C; - Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_SINGLE; - Device[deviceCount].Ports = 0; - Device[deviceCount].PullUpOption = false; - Device[deviceCount].InverseLogicOption = false; - Device[deviceCount].FormulaOption = true; - Device[deviceCount].ValueCount = 2; - Device[deviceCount].SendDataOption = true; - Device[deviceCount].TimerOption = true; - Device[deviceCount].GlobalSyncOption = true; - break; - } + { + Device[++deviceCount].Number = PLUGIN_ID_113; + Device[deviceCount].Type = DEVICE_TYPE_I2C; + Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_SINGLE; + Device[deviceCount].Ports = 0; + Device[deviceCount].PullUpOption = false; + Device[deviceCount].InverseLogicOption = false; + Device[deviceCount].FormulaOption = true; + Device[deviceCount].ValueCount = 2; + Device[deviceCount].SendDataOption = true; + Device[deviceCount].TimerOption = true; + Device[deviceCount].GlobalSyncOption = true; + break; + } case PLUGIN_GET_DEVICENAME: - { - string = F(PLUGIN_NAME_113); - break; - } + { + string = F(PLUGIN_NAME_113); + break; + } case PLUGIN_GET_DEVICEVALUENAMES: - { - strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_113)); - strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_113)); - break; - } + { + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_113)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_113)); + break; + } case PLUGIN_WEBFORM_SHOW_I2C_PARAMS: - { - byte choice = PCONFIG(0); - int optionValues[2] = { 0x29, 0x30 }; - addFormSelectorI2C(F("plugin_113_vl53l1x_i2c"), 2, optionValues, choice); - // addFormNote(F("SDO Low=0x29, High=0x30")); - break; - } + { + byte choice = PCONFIG(0); + int optionValues[2] = { 0x29, 0x30 }; + addFormSelectorI2C(F("plugin_113_vl53l1x_i2c"), 2, optionValues, choice); + + // addFormNote(F("SDO Low=0x29, High=0x30")); + break; + } case PLUGIN_WEBFORM_LOAD: + { + unsigned int choiceMode2 = PCONFIG(1); { - unsigned int choiceMode2 = PCONFIG(1); - { - String optionsMode2[6]; - optionsMode2[0] = F("100ms (Normal)"); - optionsMode2[1] = F("20ms (Fastest)"); - optionsMode2[2] = F("33ms (Fast)"); - optionsMode2[3] = F("50ms"); - optionsMode2[4] = F("200ms (Accurate)"); - optionsMode2[5] = F("500ms"); - int optionValuesMode2[6] = { 100, 20, 33, 50, 200, 500 }; - addFormSelector(F("Timing"), F("plugin_113_vl53l1x_timing"), 6, optionsMode2, optionValuesMode2, choiceMode2); - } - - int choiceMode3 = PCONFIG(2); - { - String optionsMode3[2]; - optionsMode3[0] = F("Normal (~130cm)"); - optionsMode3[1] = F("Long (~400cm)"); - int optionValuesMode3[2] = {0, 1 }; - addFormSelector(F("Range"), F("plugin_113_vl53l1x_range"), 2, optionsMode3, optionValuesMode3, choiceMode3); - } - addFormCheckBox(F("Send event when value unchanged"), F("plugin_113_vl53l1x_notchanged"), PCONFIG(3) == 1); - addFormNote(F("When checked, 'Trigger delta' setting is ignored!")); - - addFormNumericBox(F("Trigger delta"), F("plugin_113_trigger_delta"), PCONFIG(4), 0, 100); - addUnit(F("0-100mm")); - addFormNote(F("Minimal change in Distance to trigger an event.")); - - success = true; - break; + String optionsMode2[6]; + optionsMode2[0] = F("100ms (Normal)"); + optionsMode2[1] = F("20ms (Fastest)"); + optionsMode2[2] = F("33ms (Fast)"); + optionsMode2[3] = F("50ms"); + optionsMode2[4] = F("200ms (Accurate)"); + optionsMode2[5] = F("500ms"); + int optionValuesMode2[6] = { 100, 20, 33, 50, 200, 500 }; + addFormSelector(F("Timing"), F("plugin_113_vl53l1x_timing"), 6, optionsMode2, optionValuesMode2, choiceMode2); } + int choiceMode3 = PCONFIG(2); + { + String optionsMode3[2]; + optionsMode3[0] = F("Normal (~130cm)"); + optionsMode3[1] = F("Long (~400cm)"); + int optionValuesMode3[2] = { 0, 1 }; + addFormSelector(F("Range"), F("plugin_113_vl53l1x_range"), 2, optionsMode3, optionValuesMode3, choiceMode3); + } + addFormCheckBox(F("Send event when value unchanged"), F("plugin_113_vl53l1x_notchanged"), PCONFIG(3) == 1); + addFormNote(F("When checked, 'Trigger delta' setting is ignored!")); + + addFormNumericBox(F("Trigger delta"), F("plugin_113_trigger_delta"), PCONFIG(4), 0, 100); + addUnit(F("0-100mm")); + addFormNote(F("Minimal change in Distance to trigger an event.")); + + success = true; + break; + } + case PLUGIN_WEBFORM_SAVE: - { - PCONFIG(0) = getFormItemInt(F("plugin_113_vl53l1x_i2c")); - PCONFIG(1) = getFormItemInt(F("plugin_113_vl53l1x_timing")); - PCONFIG(2) = getFormItemInt(F("plugin_113_vl53l1x_range")); - PCONFIG(3) = isFormItemChecked(F("plugin_113_vl53l1x_notchanged")) ? 1 : 0; - PCONFIG(4) = getFormItemInt(F("plugin_113_trigger_delta")); + { + PCONFIG(0) = getFormItemInt(F("plugin_113_vl53l1x_i2c")); + PCONFIG(1) = getFormItemInt(F("plugin_113_vl53l1x_timing")); + PCONFIG(2) = getFormItemInt(F("plugin_113_vl53l1x_range")); + PCONFIG(3) = isFormItemChecked(F("plugin_113_vl53l1x_notchanged")) ? 1 : 0; + PCONFIG(4) = getFormItemInt(F("plugin_113_trigger_delta")); - success = true; - break; - } + success = true; + break; + } case PLUGIN_INIT: - { - initPluginTaskData(event->TaskIndex, new (std::nothrow) P113_data_struct(PCONFIG(0), PCONFIG(1), PCONFIG(2) == 1)); - P113_data_struct *P113_data = static_cast(getPluginTaskData(event->TaskIndex)); + { + initPluginTaskData(event->TaskIndex, new (std::nothrow) P113_data_struct(PCONFIG(0), PCONFIG(1), PCONFIG(2) == 1)); + P113_data_struct *P113_data = static_cast(getPluginTaskData(event->TaskIndex)); - if (nullptr != P113_data) { - success = P113_data->begin(); // Start the sensor - } - break; + if (nullptr != P113_data) { + success = P113_data->begin(); // Start the sensor } + break; + } case PLUGIN_EXIT: - { - success = true; - break; - } + { + success = true; + break; + } case PLUGIN_READ: - { - P113_data_struct *P113_data = static_cast(getPluginTaskData(event->TaskIndex)); + { + P113_data_struct *P113_data = static_cast(getPluginTaskData(event->TaskIndex)); - if (nullptr != P113_data) { - uint16_t dist = P113_data->readDistance(); - uint16_t ambient = P113_data->readAmbient(); - bool triggered = (dist > UserVar[event->BaseVarIndex] + PCONFIG(4)) || (dist < UserVar[event->BaseVarIndex] - PCONFIG(4)); - if (P113_data->isReadSuccessful() && (triggered || PCONFIG(3) == 1) && dist != 0xFFFF) { - UserVar[event->BaseVarIndex + 0] = dist; - UserVar[event->BaseVarIndex + 1] = ambient; - success = true; - } + if (nullptr != P113_data) { + uint16_t dist = P113_data->readDistance(); + uint16_t ambient = P113_data->readAmbient(); + bool triggered = (dist > UserVar[event->BaseVarIndex] + PCONFIG(4)) || (dist < UserVar[event->BaseVarIndex] - PCONFIG(4)); + + if (P113_data->isReadSuccessful() && (triggered || (PCONFIG(3) == 1)) && (dist != 0xFFFF)) { + UserVar[event->BaseVarIndex + 0] = dist; + UserVar[event->BaseVarIndex + 1] = ambient; + success = true; } - break; } + break; + } case PLUGIN_FIFTY_PER_SECOND: - { - P113_data_struct *P113_data = static_cast(getPluginTaskData(event->TaskIndex)); - if (nullptr != P113_data) { - if (P113_data->startRead()) { - if (P113_data->readAvailable() && Settings.TaskDeviceTimer[event->TaskIndex] == 0) { // Trigger as soon as there's a valid measurement and the time-out is set to 0 - Scheduler.schedule_task_device_timer(event->TaskIndex, millis() + 10); - } + { + P113_data_struct *P113_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P113_data) { + if (P113_data->startRead()) { + if (P113_data->readAvailable() && (Settings.TaskDeviceTimer[event->TaskIndex] == 0)) { // Trigger as soon as there's a valid + // measurement and the time-out is set to 0 + Scheduler.schedule_task_device_timer(event->TaskIndex, millis() + 10); } } - break; } - + break; + } } return success; } diff --git a/src/src/PluginStructs/P113_data_struct.cpp b/src/src/PluginStructs/P113_data_struct.cpp index eac87b101..ee97ffcf2 100644 --- a/src/src/PluginStructs/P113_data_struct.cpp +++ b/src/src/PluginStructs/P113_data_struct.cpp @@ -4,11 +4,10 @@ P113_data_struct::P113_data_struct(uint8_t i2c_addr, int timing, bool range) : i2cAddress(i2c_addr), timing(timing), range(range) {} -//**************************************************************************/ +// **************************************************************************/ // Initialize VL53L1X -//**************************************************************************/ +// **************************************************************************/ bool P113_data_struct::begin() { - initState = true; sensor.setI2CAddress(i2cAddress); // Initialize for configured address @@ -36,30 +35,32 @@ bool P113_data_struct::startRead() { if (initState && !readActive) { sensor.startRanging(); readActive = true; - distance = -1; + distance = -1; } return readActive; } bool P113_data_struct::readAvailable() { bool ready = sensor.checkForDataReady(); + if (ready) { distance = sensor.getDistance(); sensor.clearInterrupt(); sensor.stopRanging(); + // readActive = false; } return ready; } uint16_t P113_data_struct::readDistance() { - success = false; - #if defined(P113_DEBUG) || defined (P113_DEBUG_DEBUG) + # if defined(P113_DEBUG) || defined(P113_DEBUG_DEBUG) String log; - #endif - #ifdef P113_DEBUG_DEBUG + # endif // if defined(P113_DEBUG) || defined(P113_DEBUG_DEBUG) + # ifdef P113_DEBUG_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { log = F("VL53L1X : idx: 0x"); log += String(i2cAddress, HEX); @@ -67,20 +68,20 @@ uint16_t P113_data_struct::readDistance() { log += String(initState, BIN); addLog(LOG_LEVEL_DEBUG, log); } - #endif // P113_DEBUG_DEBUG + # endif // P113_DEBUG_DEBUG - success = true; + success = true; readActive = false; - if ( distance >= 8190 ) { - #ifdef P113_DEBUG_DEBUG + if (distance >= 8190) { + # ifdef P113_DEBUG_DEBUG addLog(LOG_LEVEL_DEBUG, "VL53L1X: NO MEASUREMENT"); - #endif // P113_DEBUG_DEBUG + # endif // P113_DEBUG_DEBUG success = false; } - #ifdef P113_DEBUG - log = F("VL53L1X: Address: 0x"); + # ifdef P113_DEBUG + log = F("VL53L1X: Address: 0x"); log += String(i2cAddress, HEX); log += F(" / Timing: "); log += String(timing, DEC); @@ -89,10 +90,10 @@ uint16_t P113_data_struct::readDistance() { log += F(" / Distance: "); log += distance; addLog(LOG_LEVEL_INFO, log); - #endif // P113_DEBUG + # endif // P113_DEBUG return distance; -}; +} uint16_t P113_data_struct::readAmbient() { return sensor.getAmbientRate(); @@ -100,6 +101,6 @@ uint16_t P113_data_struct::readAmbient() { bool P113_data_struct::isReadSuccessful() { return success; -}; +} #endif // ifdef USES_P113 diff --git a/src/src/PluginStructs/P113_data_struct.h b/src/src/PluginStructs/P113_data_struct.h index 9bbf67256..dfd03edaa 100644 --- a/src/src/PluginStructs/P113_data_struct.h +++ b/src/src/PluginStructs/P113_data_struct.h @@ -4,22 +4,24 @@ #include "../../_Plugin_Helper.h" #ifdef USES_P113 -#define P113_DEBUG // Enable debugging output (INFO loglevel) -#define P113_DEBUG_DEBUG // Enable extended debugging output (DEBUG loglevel) +# define P113_DEBUG // Enable debugging output (INFO loglevel) +# define P113_DEBUG_DEBUG // Enable extended debugging output (DEBUG loglevel) -#ifdef LIMIT_BUILD_SIZE - #ifdef P113_DEBUG_DEBUG - #undef P113_DEBUG_DEBUG - #endif -#endif +# ifdef LIMIT_BUILD_SIZE + # ifdef P113_DEBUG_DEBUG + # undef P113_DEBUG_DEBUG + # endif // ifdef P113_DEBUG_DEBUG +# endif // ifdef LIMIT_BUILD_SIZE -#include -#include +# include +# include struct P113_data_struct : public PluginTaskData_base { public: - P113_data_struct(uint8_t i2c_addr, int timing, bool range); + P113_data_struct(uint8_t i2c_addr, + int timing, + bool range); bool begin(); bool startRead(); @@ -33,7 +35,7 @@ private: SFEVL53L1X sensor; uint8_t i2cAddress; - bool initState = false; + bool initState = false; int timing; bool range; bool success = false;