mirror of
https://github.com/esphome/esphome.git
synced 2024-11-14 02:58:11 +01:00
parent
16f42a3d03
commit
9a152e588e
8 changed files with 656 additions and 0 deletions
|
@ -3,3 +3,4 @@ include README.md
|
||||||
include esphome/dashboard/templates/*.html
|
include esphome/dashboard/templates/*.html
|
||||||
recursive-include esphome/dashboard/static *.ico *.js *.css *.woff* LICENSE
|
recursive-include esphome/dashboard/static *.ico *.js *.css *.woff* LICENSE
|
||||||
recursive-include esphome *.cpp *.h *.tcc
|
recursive-include esphome *.cpp *.h *.tcc
|
||||||
|
recursive-include esphome LICENSE.txt
|
||||||
|
|
|
@ -208,5 +208,30 @@ void I2CDevice::set_i2c_parent(I2CComponent *parent) { this->parent_ = parent; }
|
||||||
uint8_t next_i2c_bus_num_ = 0;
|
uint8_t next_i2c_bus_num_ = 0;
|
||||||
#endif
|
#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<uint8_t> &value) {
|
||||||
|
this->parent_->write_bytes(this->register_, value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace i2c
|
} // namespace i2c
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -134,6 +134,24 @@ class I2CComponent : public Component {
|
||||||
extern uint8_t next_i2c_bus_num_;
|
extern uint8_t next_i2c_bus_num_;
|
||||||
#endif
|
#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<uint8_t> &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.
|
/** 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
|
* 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.
|
/// Manually set the parent i2c bus for this device.
|
||||||
void set_i2c_parent(I2CComponent *parent);
|
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
|
/** Read len amount of bytes from a register into data. Optionally with a conversion time after
|
||||||
* writing the register value to the bus.
|
* writing the register value to the bus.
|
||||||
*
|
*
|
||||||
|
|
80
esphome/components/vl53l0x/LICENSE.txt
Normal file
80
esphome/components/vl53l0x/LICENSE.txt
Normal file
|
@ -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.
|
||||||
|
|
||||||
|
-----------------------------------------------------------------
|
0
esphome/components/vl53l0x/__init__.py
Normal file
0
esphome/components/vl53l0x/__init__.py
Normal file
24
esphome/components/vl53l0x/sensor.py
Normal file
24
esphome/components/vl53l0x/sensor.py
Normal file
|
@ -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)
|
249
esphome/components/vl53l0x/vl53l0x_sensor.cpp
Normal file
249
esphome/components/vl53l0x/vl53l0x_sensor.cpp
Normal file
|
@ -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<uint16_t>(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
|
257
esphome/components/vl53l0x/vl53l0x_sensor.h
Normal file
257
esphome/components/vl53l0x/vl53l0x_sensor.h
Normal file
|
@ -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
|
Loading…
Reference in a new issue