From 9a152e588e3e9a57627bee0bdb687795c06bc6f2 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sun, 20 Oct 2019 17:56:57 +0200 Subject: [PATCH] Vl53l0x (#644) * VL530LX * VL53L0X * Updates * License * Lint --- MANIFEST.in | 1 + esphome/components/i2c/i2c.cpp | 25 ++ esphome/components/i2c/i2c.h | 20 ++ esphome/components/vl53l0x/LICENSE.txt | 80 ++++++ esphome/components/vl53l0x/__init__.py | 0 esphome/components/vl53l0x/sensor.py | 24 ++ esphome/components/vl53l0x/vl53l0x_sensor.cpp | 249 +++++++++++++++++ esphome/components/vl53l0x/vl53l0x_sensor.h | 257 ++++++++++++++++++ 8 files changed, 656 insertions(+) create mode 100644 esphome/components/vl53l0x/LICENSE.txt create mode 100644 esphome/components/vl53l0x/__init__.py create mode 100644 esphome/components/vl53l0x/sensor.py create mode 100644 esphome/components/vl53l0x/vl53l0x_sensor.cpp create mode 100644 esphome/components/vl53l0x/vl53l0x_sensor.h diff --git a/MANIFEST.in b/MANIFEST.in index e5c7b8f748..cdea2df2a6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,3 +3,4 @@ include README.md include esphome/dashboard/templates/*.html recursive-include esphome/dashboard/static *.ico *.js *.css *.woff* LICENSE recursive-include esphome *.cpp *.h *.tcc +recursive-include esphome LICENSE.txt diff --git a/esphome/components/i2c/i2c.cpp b/esphome/components/i2c/i2c.cpp index 840944748c..562bd26771 100644 --- a/esphome/components/i2c/i2c.cpp +++ b/esphome/components/i2c/i2c.cpp @@ -208,5 +208,30 @@ void I2CDevice::set_i2c_parent(I2CComponent *parent) { this->parent_ = parent; } uint8_t next_i2c_bus_num_ = 0; #endif +I2CRegister &I2CRegister::operator=(uint8_t value) { + this->parent_->write_byte(this->register_, value); + return *this; +} + +I2CRegister &I2CRegister::operator&=(uint8_t value) { + this->parent_->write_byte(this->register_, this->get() & value); + return *this; +} + +I2CRegister &I2CRegister::operator|=(uint8_t value) { + this->parent_->write_byte(this->register_, this->get() | value); + return *this; +} + +uint8_t I2CRegister::get() { + uint8_t value = 0x00; + this->parent_->read_byte(this->register_, &value); + return value; +} +I2CRegister &I2CRegister::operator=(const std::vector &value) { + this->parent_->write_bytes(this->register_, value); + return *this; +} + } // namespace i2c } // namespace esphome diff --git a/esphome/components/i2c/i2c.h b/esphome/components/i2c/i2c.h index 67cd0373d3..c4ed40e268 100644 --- a/esphome/components/i2c/i2c.h +++ b/esphome/components/i2c/i2c.h @@ -134,6 +134,24 @@ class I2CComponent : public Component { extern uint8_t next_i2c_bus_num_; #endif +class I2CDevice; + +class I2CRegister { + public: + I2CRegister(I2CDevice *parent, uint8_t a_register) : parent_(parent), register_(a_register) {} + + I2CRegister &operator=(uint8_t value); + I2CRegister &operator=(const std::vector &value); + I2CRegister &operator&=(uint8_t value); + I2CRegister &operator|=(uint8_t value); + + uint8_t get(); + + protected: + I2CDevice *parent_; + uint8_t register_; +}; + /** All components doing communication on the I2C bus should subclass I2CDevice. * * This class stores 1. the address of the i2c device and has a helper function to allow @@ -153,6 +171,8 @@ class I2CDevice { /// Manually set the parent i2c bus for this device. void set_i2c_parent(I2CComponent *parent); + I2CRegister reg(uint8_t a_register) { return {this, a_register}; } + /** Read len amount of bytes from a register into data. Optionally with a conversion time after * writing the register value to the bus. * diff --git a/esphome/components/vl53l0x/LICENSE.txt b/esphome/components/vl53l0x/LICENSE.txt new file mode 100644 index 0000000000..fe33583414 --- /dev/null +++ b/esphome/components/vl53l0x/LICENSE.txt @@ -0,0 +1,80 @@ +Most of the code in this integration is based on the VL53L0x library +by Pololu (Pololu Corporation), which in turn is based on the VL53L0X +API from ST. The code has been adapted to work with ESPHome's i2c APIs. +Please see the top-level LICENSE.txt for information about ESPHome's license. +The licenses for Pololu's and ST's software are included below. +Orignally taken from https://github.com/pololu/vl53l0x-arduino (accessed 20th october 2019). + +================================================================= + +Copyright (c) 2017 Pololu Corporation. For more information, see + +https://www.pololu.com/ +https://forum.pololu.com/ + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +================================================================= + +Most of the functionality of this library is based on the VL53L0X +API provided by ST (STSW-IMG005), and some of the explanatory +comments are quoted or paraphrased from the API source code, API +user manual (UM2039), and the VL53L0X datasheet. + +The following applies to source code reproduced or derived from +the API: + +----------------------------------------------------------------- + +Copyright © 2016, 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. + +----------------------------------------------------------------- diff --git a/esphome/components/vl53l0x/__init__.py b/esphome/components/vl53l0x/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/vl53l0x/sensor.py b/esphome/components/vl53l0x/sensor.py new file mode 100644 index 0000000000..6740d53e13 --- /dev/null +++ b/esphome/components/vl53l0x/sensor.py @@ -0,0 +1,24 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c, sensor +from esphome.const import CONF_ID, UNIT_METER, ICON_ARROW_EXPAND_VERTICAL + +DEPENDENCIES = ['i2c'] + +vl53l0x_ns = cg.esphome_ns.namespace('vl53l0x') +VL53L0XSensor = vl53l0x_ns.class_('VL53L0XSensor', sensor.Sensor, cg.PollingComponent, + i2c.I2CDevice) + +CONF_SIGNAL_RATE_LIMIT = 'signal_rate_limit' +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_METER, ICON_ARROW_EXPAND_VERTICAL, 2).extend({ + cv.GenerateID(): cv.declare_id(VL53L0XSensor), + cv.Optional(CONF_SIGNAL_RATE_LIMIT, default=0.25): cv.float_range( + min=0.0, max=512.0, min_included=False, max_included=False) +}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x29)) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield sensor.register_sensor(var, config) + yield i2c.register_i2c_device(var, config) diff --git a/esphome/components/vl53l0x/vl53l0x_sensor.cpp b/esphome/components/vl53l0x/vl53l0x_sensor.cpp new file mode 100644 index 0000000000..231bed99ac --- /dev/null +++ b/esphome/components/vl53l0x/vl53l0x_sensor.cpp @@ -0,0 +1,249 @@ +#include "vl53l0x_sensor.h" +#include "esphome/core/log.h" + +/* + * Most of the code in this integration is based on the VL53L0x library + * by Pololu (Pololu Corporation), which in turn is based on the VL53L0X + * API from ST. + * + * For more information about licensing, please view the included LICENSE.txt file + * in the vl53l0x integration directory. + */ + +namespace esphome { +namespace vl53l0x { + +static const char *TAG = "vl53l0x"; + +void VL53L0XSensor::dump_config() { + LOG_SENSOR("", "VL53L0X", this); + LOG_UPDATE_INTERVAL(this); + LOG_I2C_DEVICE(this); +} +void VL53L0XSensor::setup() { + reg(0x89) |= 0x01; + reg(0x88) = 0x00; + + reg(0x80) = 0x01; + reg(0xFF) = 0x01; + reg(0x00) = 0x00; + stop_variable_ = reg(0x91).get(); + + reg(0x00) = 0x01; + reg(0xFF) = 0x00; + reg(0x80) = 0x00; + reg(0x60) |= 0x12; + + auto rate_value = static_cast(signal_rate_limit_ * 128); + write_byte_16(0x44, rate_value); + + reg(0x01) = 0xFF; + + // getSpadInfo() + reg(0x80) = 0x01; + reg(0xFF) = 0x01; + reg(0x00) = 0x00; + reg(0xFF) = 0x06; + reg(0x83) |= 0x04; + reg(0xFF) = 0x07; + reg(0x81) = 0x01; + reg(0x80) = 0x01; + reg(0x94) = 0x6B; + reg(0x83) = 0x00; + + while (reg(0x83).get() == 0x00) + yield(); + + reg(0x83) = 0x01; + uint8_t tmp = reg(0x92).get(); + uint8_t spad_count = tmp & 0x7F; + bool spad_type_is_aperture = tmp & 0x80; + + reg(0x81) = 0x00; + reg(0xFF) = 0x06; + reg(0x83) &= ~0x04; + reg(0xFF) = 0x01; + reg(0x00) = 0x01; + reg(0xFF) = 0x00; + reg(0x80) = 0x00; + + uint8_t ref_spad_map[6]; + this->read_bytes(0xB0, ref_spad_map, 6); + + reg(0xFF) = 0x01; + reg(0x4F) = 0x00; + reg(0x4E) = 0x2C; + reg(0xFF) = 0x00; + reg(0xB6) = 0xB4; + + uint8_t first_spad_to_enable = spad_type_is_aperture ? 12 : 0; + uint8_t spads_enabled = 0; + for (int i = 0; i < 48; i++) { + uint8_t &val = ref_spad_map[i / 8]; + uint8_t mask = 1 << (i % 8); + + if (i < first_spad_to_enable || spads_enabled == spad_count) + val &= ~mask; + else if (val & mask) + spads_enabled += 1; + } + + this->write_bytes(0xB0, ref_spad_map, 6); + + reg(0xFF) = 0x01; + reg(0x00) = 0x00; + reg(0xFF) = 0x00; + reg(0x09) = 0x00; + reg(0x10) = 0x00; + reg(0x11) = 0x00; + reg(0x24) = 0x01; + reg(0x25) = 0xFF; + reg(0x75) = 0x00; + reg(0xFF) = 0x01; + reg(0x4E) = 0x2C; + reg(0x48) = 0x00; + reg(0x30) = 0x20; + reg(0xFF) = 0x00; + reg(0x30) = 0x09; + reg(0x54) = 0x00; + reg(0x31) = 0x04; + reg(0x32) = 0x03; + reg(0x40) = 0x83; + reg(0x46) = 0x25; + reg(0x60) = 0x00; + reg(0x27) = 0x00; + reg(0x50) = 0x06; + reg(0x51) = 0x00; + reg(0x52) = 0x96; + reg(0x56) = 0x08; + reg(0x57) = 0x30; + reg(0x61) = 0x00; + reg(0x62) = 0x00; + reg(0x64) = 0x00; + reg(0x65) = 0x00; + reg(0x66) = 0xA0; + reg(0xFF) = 0x01; + reg(0x22) = 0x32; + reg(0x47) = 0x14; + reg(0x49) = 0xFF; + reg(0x4A) = 0x00; + reg(0xFF) = 0x00; + reg(0x7A) = 0x0A; + reg(0x7B) = 0x00; + reg(0x78) = 0x21; + reg(0xFF) = 0x01; + reg(0x23) = 0x34; + reg(0x42) = 0x00; + reg(0x44) = 0xFF; + reg(0x45) = 0x26; + reg(0x46) = 0x05; + reg(0x40) = 0x40; + reg(0x0E) = 0x06; + reg(0x20) = 0x1A; + reg(0x43) = 0x40; + reg(0xFF) = 0x00; + reg(0x34) = 0x03; + reg(0x35) = 0x44; + reg(0xFF) = 0x01; + reg(0x31) = 0x04; + reg(0x4B) = 0x09; + reg(0x4C) = 0x05; + reg(0x4D) = 0x04; + reg(0xFF) = 0x00; + reg(0x44) = 0x00; + reg(0x45) = 0x20; + reg(0x47) = 0x08; + reg(0x48) = 0x28; + reg(0x67) = 0x00; + reg(0x70) = 0x04; + reg(0x71) = 0x01; + reg(0x72) = 0xFE; + reg(0x76) = 0x00; + reg(0x77) = 0x00; + reg(0xFF) = 0x01; + reg(0x0D) = 0x01; + reg(0xFF) = 0x00; + reg(0x80) = 0x01; + reg(0x01) = 0xF8; + reg(0xFF) = 0x01; + reg(0x8E) = 0x01; + reg(0x00) = 0x01; + reg(0xFF) = 0x00; + reg(0x80) = 0x00; + + reg(0x0A) = 0x04; + reg(0x84) &= ~0x10; + reg(0x0B) = 0x01; + + measurement_timing_budget_us_ = get_measurement_timing_budget_(); + reg(0x01) = 0xE8; + set_measurement_timing_budget_(measurement_timing_budget_us_); + reg(0x01) = 0x01; + + if (!perform_single_ref_calibration_(0x40)) { + ESP_LOGW(TAG, "1st reference calibration failed!"); + this->mark_failed(); + return; + } + reg(0x01) = 0x02; + if (!perform_single_ref_calibration_(0x00)) { + ESP_LOGW(TAG, "2nd reference calibration failed!"); + this->mark_failed(); + return; + } + reg(0x01) = 0xE8; +} +void VL53L0XSensor::update() { + if (this->initiated_read_ || this->waiting_for_interrupt_) { + this->publish_state(NAN); + this->status_set_warning(); + } + + // initiate single shot measurement + reg(0x80) = 0x01; + reg(0xFF) = 0x01; + + reg(0x00) = 0x00; + reg(0x91) = stop_variable_; + reg(0x00) = 0x01; + reg(0xFF) = 0x00; + reg(0x80) = 0x00; + + reg(0x00) = 0x01; + this->waiting_for_interrupt_ = false; + this->initiated_read_ = true; + // wait for timeout +} +void VL53L0XSensor::loop() { + if (this->initiated_read_) { + if (reg(0x00).get() & 0x01) { + // waiting + } else { + // done + // wait until reg(0x13) & 0x07 is set + this->initiated_read_ = false; + this->waiting_for_interrupt_ = true; + } + } + if (this->waiting_for_interrupt_) { + if (reg(0x13).get() & 0x07) { + uint16_t range_mm; + this->read_byte_16(0x14 + 10, &range_mm); + reg(0x0B) = 0x01; + this->waiting_for_interrupt_ = false; + + if (range_mm >= 8190) { + ESP_LOGW(TAG, "'%s' - Distance is out of range, please move the target closer", this->name_.c_str()); + this->publish_state(NAN); + return; + } + + float range_m = range_mm / 1e3f; + ESP_LOGD(TAG, "'%s' - Got distance %.3f m", this->name_.c_str(), range_m); + this->publish_state(range_m); + } + } +} + +} // namespace vl53l0x +} // namespace esphome diff --git a/esphome/components/vl53l0x/vl53l0x_sensor.h b/esphome/components/vl53l0x/vl53l0x_sensor.h new file mode 100644 index 0000000000..1825383cee --- /dev/null +++ b/esphome/components/vl53l0x/vl53l0x_sensor.h @@ -0,0 +1,257 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace vl53l0x { + +struct SequenceStepEnables { + bool tcc, msrc, dss, pre_range, final_range; +}; + +struct SequenceStepTimeouts { + uint16_t pre_range_vcsel_period_pclks, final_range_vcsel_period_pclks; + + uint16_t msrc_dss_tcc_mclks, pre_range_mclks, final_range_mclks; + uint32_t msrc_dss_tcc_us, pre_range_us, final_range_us; +}; + +class VL53L0XSensor : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice { + public: + void setup() override; + + void dump_config() override; + float get_setup_priority() const override { return setup_priority::DATA; } + void update() override; + + void loop() override; + + void set_signal_rate_limit(float signal_rate_limit) { signal_rate_limit_ = signal_rate_limit; } + + protected: + uint32_t get_measurement_timing_budget_() { + SequenceStepEnables enables{}; + SequenceStepTimeouts timeouts{}; + + uint16_t start_overhead = 1910; + uint16_t end_overhead = 960; + uint16_t msrc_overhead = 660; + uint16_t tcc_overhead = 590; + uint16_t dss_overhead = 690; + uint16_t pre_range_overhead = 660; + uint16_t final_range_overhead = 550; + + // "Start and end overhead times always present" + uint32_t budget_us = start_overhead + end_overhead; + + get_sequence_step_enables_(&enables); + get_sequence_step_timeouts_(&enables, &timeouts); + + if (enables.tcc) + budget_us += (timeouts.msrc_dss_tcc_us + tcc_overhead); + + if (enables.dss) + budget_us += 2 * (timeouts.msrc_dss_tcc_us + dss_overhead); + else if (enables.msrc) + budget_us += (timeouts.msrc_dss_tcc_us + msrc_overhead); + + if (enables.pre_range) + budget_us += (timeouts.pre_range_us + pre_range_overhead); + + if (enables.final_range) + budget_us += (timeouts.final_range_us + final_range_overhead); + + measurement_timing_budget_us_ = budget_us; // store for internal reuse + return budget_us; + } + + bool set_measurement_timing_budget_(uint32_t budget_us) { + SequenceStepEnables enables{}; + SequenceStepTimeouts timeouts{}; + + uint16_t start_overhead = 1320; // note that this is different than the value in get_ + uint16_t end_overhead = 960; + uint16_t msrc_overhead = 660; + uint16_t tcc_overhead = 590; + uint16_t dss_overhead = 690; + uint16_t pre_range_overhead = 660; + uint16_t final_range_overhead = 550; + + uint32_t min_timing_budget = 20000; + + if (budget_us < min_timing_budget) { + return false; + } + + uint32_t used_budget_us = start_overhead + end_overhead; + + get_sequence_step_enables_(&enables); + get_sequence_step_timeouts_(&enables, &timeouts); + + if (enables.tcc) { + used_budget_us += (timeouts.msrc_dss_tcc_us + tcc_overhead); + } + + if (enables.dss) { + used_budget_us += 2 * (timeouts.msrc_dss_tcc_us + dss_overhead); + } else if (enables.msrc) { + used_budget_us += (timeouts.msrc_dss_tcc_us + msrc_overhead); + } + + if (enables.pre_range) { + used_budget_us += (timeouts.pre_range_us + pre_range_overhead); + } + + if (enables.final_range) { + used_budget_us += final_range_overhead; + + // "Note that the final range timeout is determined by the timing + // budget and the sum of all other timeouts within the sequence. + // If there is no room for the final range timeout, then an error + // will be set. Otherwise the remaining time will be applied to + // the final range." + + if (used_budget_us > budget_us) { + // "Requested timeout too big." + return false; + } + + uint32_t final_range_timeout_us = budget_us - used_budget_us; + + // set_sequence_step_timeout() begin + // (SequenceStepId == VL53L0X_SEQUENCESTEP_FINAL_RANGE) + + // "For the final range timeout, the pre-range timeout + // must be added. To do this both final and pre-range + // timeouts must be expressed in macro periods MClks + // because they have different vcsel periods." + + uint16_t final_range_timeout_mclks = + timeout_microseconds_to_mclks_(final_range_timeout_us, timeouts.final_range_vcsel_period_pclks); + + if (enables.pre_range) { + final_range_timeout_mclks += timeouts.pre_range_mclks; + } + + write_byte_16(0x71, encode_timeout_(final_range_timeout_mclks)); + + // set_sequence_step_timeout() end + + measurement_timing_budget_us_ = budget_us; // store for internal reuse + } + return true; + } + + void get_sequence_step_enables_(SequenceStepEnables *enables) { + uint8_t sequence_config = reg(0x01).get(); + enables->tcc = (sequence_config >> 4) & 0x1; + enables->dss = (sequence_config >> 3) & 0x1; + enables->msrc = (sequence_config >> 2) & 0x1; + enables->pre_range = (sequence_config >> 6) & 0x1; + enables->final_range = (sequence_config >> 7) & 0x1; + } + + enum VcselPeriodType { VCSEL_PERIOD_PRE_RANGE, VCSEL_PERIOD_FINAL_RANGE }; + + void get_sequence_step_timeouts_(SequenceStepEnables const *enables, SequenceStepTimeouts *timeouts) { + timeouts->pre_range_vcsel_period_pclks = get_vcsel_pulse_period_(VCSEL_PERIOD_PRE_RANGE); + + timeouts->msrc_dss_tcc_mclks = reg(0x46).get() + 1; + timeouts->msrc_dss_tcc_us = + timeout_mclks_to_microseconds_(timeouts->msrc_dss_tcc_mclks, timeouts->pre_range_vcsel_period_pclks); + + uint16_t value; + read_byte_16(0x51, &value); + timeouts->pre_range_mclks = decode_timeout_(value); + timeouts->pre_range_us = + timeout_mclks_to_microseconds_(timeouts->pre_range_mclks, timeouts->pre_range_vcsel_period_pclks); + + timeouts->final_range_vcsel_period_pclks = get_vcsel_pulse_period_(VCSEL_PERIOD_FINAL_RANGE); + + read_byte_16(0x71, &value); + timeouts->final_range_mclks = decode_timeout_(value); + + if (enables->pre_range) { + timeouts->final_range_mclks -= timeouts->pre_range_mclks; + } + + timeouts->final_range_us = + timeout_mclks_to_microseconds_(timeouts->final_range_mclks, timeouts->final_range_vcsel_period_pclks); + } + + uint8_t get_vcsel_pulse_period_(VcselPeriodType type) { + uint8_t vcsel; + if (type == VCSEL_PERIOD_PRE_RANGE) + vcsel = reg(0x50).get(); + else if (type == VCSEL_PERIOD_FINAL_RANGE) + vcsel = reg(0x70).get(); + else + return 255; + + return (vcsel + 1) << 1; + } + + uint32_t get_macro_period_(uint8_t vcsel_period_pclks) { + return ((2304UL * vcsel_period_pclks * 1655UL) + 500UL) / 1000UL; + } + + uint32_t timeout_mclks_to_microseconds_(uint16_t timeout_period_mclks, uint8_t vcsel_period_pclks) { + uint32_t macro_period_ns = get_macro_period_(vcsel_period_pclks); + return ((timeout_period_mclks * macro_period_ns) + (macro_period_ns / 2)) / 1000; + } + uint32_t timeout_microseconds_to_mclks_(uint32_t timeout_period_us, uint8_t vcsel_period_pclks) { + uint32_t macro_period_ns = get_macro_period_(vcsel_period_pclks); + return (((timeout_period_us * 1000) + (macro_period_ns / 2)) / macro_period_ns); + } + + uint16_t decode_timeout_(uint16_t reg_val) { + // format: "(LSByte * 2^MSByte) + 1" + uint8_t msb = (reg_val >> 8) & 0xFF; + uint8_t lsb = (reg_val >> 0) & 0xFF; + return (uint16_t(lsb) << msb) + 1; + } + uint16_t encode_timeout_(uint16_t timeout_mclks) { + // format: "(LSByte * 2^MSByte) + 1" + uint32_t ls_byte = 0; + uint16_t ms_byte = 0; + + if (timeout_mclks <= 0) + return 0; + + ls_byte = timeout_mclks - 1; + + while ((ls_byte & 0xFFFFFF00) > 0) { + ls_byte >>= 1; + ms_byte++; + } + + return (ms_byte << 8) | (ls_byte & 0xFF); + } + + bool perform_single_ref_calibration_(uint8_t vhv_init_byte) { + reg(0x00) = 0x01 | vhv_init_byte; // VL53L0X_REG_SYSRANGE_MODE_START_STOP + + uint32_t start = millis(); + while ((reg(0x13).get() & 0x07) == 0) { + if (millis() - start > 1000) + return false; + yield(); + } + + reg(0x0B) = 0x01; + reg(0x00) = 0x00; + + return true; + } + + float signal_rate_limit_; + uint32_t measurement_timing_budget_us_; + bool initiated_read_{false}; + bool waiting_for_interrupt_{false}; + uint8_t stop_variable_; +}; + +} // namespace vl53l0x +} // namespace esphome