mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 13:34:54 +01:00
Add QMC5883L Sensor + Improvements to HMC5883L (#671)
* Add QMC5883L and Updated HMC5883L * add tests * changed to oversampling * fix pylint * fix private method * typo fix * fix protected method * Clean up code and PR recomendations * fix tests * remote file * fix qmc oversampling unit * Remove hmc5883l config logging Either the units are converted to the user values like 1x, 8x oversampling or not printed at all. Printing the machine-value of these is only confusing users. * Changes for validate_enum Move stuff that can be done beforehand out of the bound function, use text_type for py2/3 compatability. * Remove unused constant * Remove duplicate tests * Repeat remove config print * remove changes to test2 since bin is to large * Add comment to HMC5583L Co-authored-by: Timothy Purchas <timothy@TPF.local> Co-authored-by: Otto Winter <otto@otto-winter.com>
This commit is contained in:
parent
36ffef083b
commit
fa1adfd934
8 changed files with 378 additions and 15 deletions
|
@ -38,12 +38,9 @@ void HMC5883LComponent::setup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t config_a = 0;
|
uint8_t config_a = 0;
|
||||||
// 0b0xx00000 << 5 Sample Averaging - 0b00=1 sample, 0b11=8 samples
|
config_a |= this->oversampling_ << 5;
|
||||||
config_a |= 0b01100000;
|
config_a |= this->datarate_ << 2;
|
||||||
// 0b000xxx00 << 2 Data Output Rate - 0b100=15Hz
|
config_a |= 0b0 << 0; // Measurement Mode: Normal(high impedance on load)
|
||||||
config_a |= 0b00010000;
|
|
||||||
// 0b000000xx << 0 Measurement Mode - 0b00=high impedance on load
|
|
||||||
config_a |= 0b00000000;
|
|
||||||
if (!this->write_byte(HMC5883L_REGISTER_CONFIG_A, config_a)) {
|
if (!this->write_byte(HMC5883L_REGISTER_CONFIG_A, config_a)) {
|
||||||
this->error_code_ = COMMUNICATION_FAILED;
|
this->error_code_ = COMMUNICATION_FAILED;
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
|
@ -61,7 +58,6 @@ void HMC5883LComponent::setup() {
|
||||||
uint8_t mode = 0;
|
uint8_t mode = 0;
|
||||||
// Continuous Measurement Mode
|
// Continuous Measurement Mode
|
||||||
mode |= 0b00;
|
mode |= 0b00;
|
||||||
|
|
||||||
if (!this->write_byte(HMC5883L_REGISTER_MODE, mode)) {
|
if (!this->write_byte(HMC5883L_REGISTER_MODE, mode)) {
|
||||||
this->error_code_ = COMMUNICATION_FAILED;
|
this->error_code_ = COMMUNICATION_FAILED;
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
|
|
|
@ -7,6 +7,23 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace hmc5883l {
|
namespace hmc5883l {
|
||||||
|
|
||||||
|
enum HMC5883LOversampling {
|
||||||
|
HMC5883L_OVERSAMPLING_1 = 0b000,
|
||||||
|
HMC5883L_OVERSAMPLING_2 = 0b001,
|
||||||
|
HMC5883L_OVERSAMPLING_4 = 0b010,
|
||||||
|
HMC5883L_OVERSAMPLING_8 = 0b011,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum HMC5883LDatarate {
|
||||||
|
HMC5883L_DATARATE_0_75_HZ = 0b000,
|
||||||
|
HMC5883L_DATARATE_1_5_HZ = 0b001,
|
||||||
|
HMC5883L_DATARATE_3_0_HZ = 0b010,
|
||||||
|
HMC5883L_DATARATE_7_5_HZ = 0b011,
|
||||||
|
HMC5883L_DATARATE_15_0_HZ = 0b100,
|
||||||
|
HMC5883L_DATARATE_30_0_HZ = 0b101,
|
||||||
|
HMC5883L_DATARATE_75_0_HZ = 0b110,
|
||||||
|
};
|
||||||
|
|
||||||
enum HMC5883LRange {
|
enum HMC5883LRange {
|
||||||
HMC5883L_RANGE_88_UT = 0b000,
|
HMC5883L_RANGE_88_UT = 0b000,
|
||||||
HMC5883L_RANGE_130_UT = 0b001,
|
HMC5883L_RANGE_130_UT = 0b001,
|
||||||
|
@ -25,6 +42,8 @@ class HMC5883LComponent : public PollingComponent, public i2c::I2CDevice {
|
||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
|
void set_oversampling(HMC5883LOversampling oversampling) { oversampling_ = oversampling; }
|
||||||
|
void set_datarate(HMC5883LDatarate datarate) { datarate_ = datarate; }
|
||||||
void set_range(HMC5883LRange range) { range_ = range; }
|
void set_range(HMC5883LRange range) { range_ = range; }
|
||||||
void set_x_sensor(sensor::Sensor *x_sensor) { x_sensor_ = x_sensor; }
|
void set_x_sensor(sensor::Sensor *x_sensor) { x_sensor_ = x_sensor; }
|
||||||
void set_y_sensor(sensor::Sensor *y_sensor) { y_sensor_ = y_sensor; }
|
void set_y_sensor(sensor::Sensor *y_sensor) { y_sensor_ = y_sensor; }
|
||||||
|
@ -32,6 +51,8 @@ class HMC5883LComponent : public PollingComponent, public i2c::I2CDevice {
|
||||||
void set_heading_sensor(sensor::Sensor *heading_sensor) { heading_sensor_ = heading_sensor; }
|
void set_heading_sensor(sensor::Sensor *heading_sensor) { heading_sensor_ = heading_sensor; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
HMC5883LOversampling oversampling_{HMC5883L_OVERSAMPLING_1};
|
||||||
|
HMC5883LDatarate datarate_{HMC5883L_DATARATE_15_0_HZ};
|
||||||
HMC5883LRange range_{HMC5883L_RANGE_130_UT};
|
HMC5883LRange range_{HMC5883L_RANGE_130_UT};
|
||||||
sensor::Sensor *x_sensor_;
|
sensor::Sensor *x_sensor_;
|
||||||
sensor::Sensor *y_sensor_;
|
sensor::Sensor *y_sensor_;
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import i2c, sensor
|
from esphome.components import i2c, sensor
|
||||||
from esphome.const import CONF_ADDRESS, CONF_ID, CONF_RANGE, ICON_MAGNET, UNIT_MICROTESLA, \
|
from esphome.const import (CONF_ADDRESS, CONF_ID, CONF_OVERSAMPLING, CONF_RANGE, ICON_MAGNET,
|
||||||
UNIT_DEGREES, ICON_SCREEN_ROTATION
|
UNIT_MICROTESLA, UNIT_DEGREES, ICON_SCREEN_ROTATION,
|
||||||
|
CONF_UPDATE_INTERVAL)
|
||||||
|
from esphome.py_compat import text_type
|
||||||
|
|
||||||
DEPENDENCIES = ['i2c']
|
DEPENDENCIES = ['i2c']
|
||||||
|
|
||||||
|
@ -16,6 +18,25 @@ CONF_HEADING = 'heading'
|
||||||
|
|
||||||
HMC5883LComponent = hmc5883l_ns.class_('HMC5883LComponent', cg.PollingComponent, i2c.I2CDevice)
|
HMC5883LComponent = hmc5883l_ns.class_('HMC5883LComponent', cg.PollingComponent, i2c.I2CDevice)
|
||||||
|
|
||||||
|
HMC5883LOversampling = hmc5883l_ns.enum('HMC5883LOversampling')
|
||||||
|
HMC5883LOversamplings = {
|
||||||
|
1: HMC5883LOversampling.HMC5883L_OVERSAMPLING_1,
|
||||||
|
2: HMC5883LOversampling.HMC5883L_OVERSAMPLING_2,
|
||||||
|
4: HMC5883LOversampling.HMC5883L_OVERSAMPLING_4,
|
||||||
|
8: HMC5883LOversampling.HMC5883L_OVERSAMPLING_8,
|
||||||
|
}
|
||||||
|
|
||||||
|
HMC5883LDatarate = hmc5883l_ns.enum('HMC5883LDatarate')
|
||||||
|
HMC5883LDatarates = {
|
||||||
|
0.75: HMC5883LDatarate.HMC5883L_DATARATE_0_75_HZ,
|
||||||
|
1.5: HMC5883LDatarate.HMC5883L_DATARATE_1_5_HZ,
|
||||||
|
3.0: HMC5883LDatarate.HMC5883L_DATARATE_3_0_HZ,
|
||||||
|
7.5: HMC5883LDatarate.HMC5883L_DATARATE_7_5_HZ,
|
||||||
|
15: HMC5883LDatarate.HMC5883L_DATARATE_15_0_HZ,
|
||||||
|
30: HMC5883LDatarate.HMC5883L_DATARATE_30_0_HZ,
|
||||||
|
75: HMC5883LDatarate.HMC5883L_DATARATE_75_0_HZ,
|
||||||
|
}
|
||||||
|
|
||||||
HMC5883LRange = hmc5883l_ns.enum('HMC5883LRange')
|
HMC5883LRange = hmc5883l_ns.enum('HMC5883LRange')
|
||||||
HMC5883L_RANGES = {
|
HMC5883L_RANGES = {
|
||||||
88: HMC5883LRange.HMC5883L_RANGE_88_UT,
|
88: HMC5883LRange.HMC5883L_RANGE_88_UT,
|
||||||
|
@ -29,11 +50,21 @@ HMC5883L_RANGES = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def validate_range(value):
|
def validate_enum(enum_values, units=None, int=True):
|
||||||
value = cv.string(value)
|
_units = []
|
||||||
if value.endswith(u'µT') or value.endswith('uT'):
|
if units is not None:
|
||||||
value = value[:-2]
|
_units = units if isinstance(units, list) else [units]
|
||||||
return cv.enum(HMC5883L_RANGES, int=True)(value)
|
_units = [text_type(x) for x in _units]
|
||||||
|
enum_bound = cv.enum(enum_values, int=int)
|
||||||
|
|
||||||
|
def validate_enum_bound(value):
|
||||||
|
value = cv.string(value)
|
||||||
|
for unit in _units:
|
||||||
|
if value.endswith(unit):
|
||||||
|
value = value[:-len(unit)]
|
||||||
|
break
|
||||||
|
return enum_bound(value)
|
||||||
|
return validate_enum_bound
|
||||||
|
|
||||||
|
|
||||||
field_strength_schema = sensor.sensor_schema(UNIT_MICROTESLA, ICON_MAGNET, 1)
|
field_strength_schema = sensor.sensor_schema(UNIT_MICROTESLA, ICON_MAGNET, 1)
|
||||||
|
@ -42,19 +73,31 @@ heading_schema = sensor.sensor_schema(UNIT_DEGREES, ICON_SCREEN_ROTATION, 1)
|
||||||
CONFIG_SCHEMA = cv.Schema({
|
CONFIG_SCHEMA = cv.Schema({
|
||||||
cv.GenerateID(): cv.declare_id(HMC5883LComponent),
|
cv.GenerateID(): cv.declare_id(HMC5883LComponent),
|
||||||
cv.Optional(CONF_ADDRESS): cv.i2c_address,
|
cv.Optional(CONF_ADDRESS): cv.i2c_address,
|
||||||
|
cv.Optional(CONF_OVERSAMPLING, default='1x'): validate_enum(HMC5883LOversamplings, units="x"),
|
||||||
|
cv.Optional(CONF_RANGE, default=u'130µT'): validate_enum(HMC5883L_RANGES, units=["uT", u"µT"]),
|
||||||
cv.Optional(CONF_FIELD_STRENGTH_X): field_strength_schema,
|
cv.Optional(CONF_FIELD_STRENGTH_X): field_strength_schema,
|
||||||
cv.Optional(CONF_FIELD_STRENGTH_Y): field_strength_schema,
|
cv.Optional(CONF_FIELD_STRENGTH_Y): field_strength_schema,
|
||||||
cv.Optional(CONF_FIELD_STRENGTH_Z): field_strength_schema,
|
cv.Optional(CONF_FIELD_STRENGTH_Z): field_strength_schema,
|
||||||
cv.Optional(CONF_HEADING): heading_schema,
|
cv.Optional(CONF_HEADING): heading_schema,
|
||||||
cv.Optional(CONF_RANGE, default='130uT'): validate_range,
|
|
||||||
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x1E))
|
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x1E))
|
||||||
|
|
||||||
|
|
||||||
|
def auto_data_rate(config):
|
||||||
|
interval_sec = config[CONF_UPDATE_INTERVAL].seconds
|
||||||
|
interval_hz = 1.0/interval_sec
|
||||||
|
for datarate in sorted(HMC5883LDatarates.keys()):
|
||||||
|
if float(datarate) >= interval_hz:
|
||||||
|
return HMC5883LDatarates[datarate]
|
||||||
|
return HMC5883LDatarates[75]
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
yield cg.register_component(var, config)
|
yield cg.register_component(var, config)
|
||||||
yield i2c.register_i2c_device(var, config)
|
yield i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
|
cg.add(var.set_oversampling(config[CONF_OVERSAMPLING]))
|
||||||
|
cg.add(var.set_datarate(auto_data_rate(config)))
|
||||||
cg.add(var.set_range(config[CONF_RANGE]))
|
cg.add(var.set_range(config[CONF_RANGE]))
|
||||||
if CONF_FIELD_STRENGTH_X in config:
|
if CONF_FIELD_STRENGTH_X in config:
|
||||||
sens = yield sensor.new_sensor(config[CONF_FIELD_STRENGTH_X])
|
sens = yield sensor.new_sensor(config[CONF_FIELD_STRENGTH_X])
|
||||||
|
|
0
esphome/components/qmc5883l/__init__.py
Normal file
0
esphome/components/qmc5883l/__init__.py
Normal file
124
esphome/components/qmc5883l/qmc5883l.cpp
Normal file
124
esphome/components/qmc5883l/qmc5883l.cpp
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
#include "qmc5883l.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace qmc5883l {
|
||||||
|
|
||||||
|
static const char *TAG = "qmc5883l";
|
||||||
|
static const uint8_t QMC5883L_ADDRESS = 0x0D;
|
||||||
|
|
||||||
|
static const uint8_t QMC5883L_REGISTER_DATA_X_LSB = 0x00;
|
||||||
|
static const uint8_t QMC5883L_REGISTER_DATA_X_MSB = 0x01;
|
||||||
|
static const uint8_t QMC5883L_REGISTER_DATA_Y_LSB = 0x02;
|
||||||
|
static const uint8_t QMC5883L_REGISTER_DATA_Y_MSB = 0x03;
|
||||||
|
static const uint8_t QMC5883L_REGISTER_DATA_Z_LSB = 0x04;
|
||||||
|
static const uint8_t QMC5883L_REGISTER_DATA_Z_MSB = 0x05;
|
||||||
|
static const uint8_t QMC5883L_REGISTER_STATUS = 0x06;
|
||||||
|
static const uint8_t QMC5883L_REGISTER_TEMPERATURE_LSB = 0x07;
|
||||||
|
static const uint8_t QMC5883L_REGISTER_TEMPERATURE_MSB = 0x08;
|
||||||
|
static const uint8_t QMC5883L_REGISTER_CONTROL_1 = 0x09;
|
||||||
|
static const uint8_t QMC5883L_REGISTER_CONTROL_2 = 0x0A;
|
||||||
|
static const uint8_t QMC5883L_REGISTER_PERIOD = 0x0B;
|
||||||
|
|
||||||
|
void QMC5883LComponent::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up QMC5883L...");
|
||||||
|
// Soft Reset
|
||||||
|
if (!this->write_byte(QMC5883L_REGISTER_CONTROL_2, 1 << 7)) {
|
||||||
|
this->error_code_ = COMMUNICATION_FAILED;
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
delay(10);
|
||||||
|
|
||||||
|
uint8_t control_1 = 0;
|
||||||
|
control_1 |= 0b01 << 0; // MODE (Mode) -> 0b00=standby, 0b01=continuous
|
||||||
|
control_1 |= this->datarate_ << 2;
|
||||||
|
control_1 |= this->range_ << 4;
|
||||||
|
control_1 |= this->oversampling_ << 6;
|
||||||
|
if (!this->write_byte(QMC5883L_REGISTER_CONTROL_1, control_1)) {
|
||||||
|
this->error_code_ = COMMUNICATION_FAILED;
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t control_2 = 0;
|
||||||
|
control_2 |= 0b0 << 7; // SOFT_RST (Soft Reset) -> 0b00=disabled, 0b01=enabled
|
||||||
|
control_2 |= 0b0 << 6; // ROL_PNT (Pointer Roll Over) -> 0b00=disabled, 0b01=enabled
|
||||||
|
control_2 |= 0b0 << 0; // INT_ENB (Interrupt) -> 0b00=disabled, 0b01=enabled
|
||||||
|
if (!this->write_byte(QMC5883L_REGISTER_CONTROL_2, control_2)) {
|
||||||
|
this->error_code_ = COMMUNICATION_FAILED;
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t period = 0x01; // recommended value
|
||||||
|
if (!this->write_byte(QMC5883L_REGISTER_PERIOD, period)) {
|
||||||
|
this->error_code_ = COMMUNICATION_FAILED;
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void QMC5883LComponent::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "QMC5883L:");
|
||||||
|
LOG_I2C_DEVICE(this);
|
||||||
|
if (this->error_code_ == COMMUNICATION_FAILED) {
|
||||||
|
ESP_LOGE(TAG, "Communication with QMC5883L failed!");
|
||||||
|
}
|
||||||
|
LOG_UPDATE_INTERVAL(this);
|
||||||
|
|
||||||
|
LOG_SENSOR(" ", "X Axis", this->x_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Y Axis", this->y_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Z Axis", this->z_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Heading", this->heading_sensor_);
|
||||||
|
}
|
||||||
|
float QMC5883LComponent::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
void QMC5883LComponent::update() {
|
||||||
|
uint8_t status = false;
|
||||||
|
this->read_byte(QMC5883L_REGISTER_STATUS, &status);
|
||||||
|
|
||||||
|
uint16_t raw_x, raw_y, raw_z;
|
||||||
|
if (!this->read_byte_16_(QMC5883L_REGISTER_DATA_X_LSB, &raw_x) ||
|
||||||
|
!this->read_byte_16_(QMC5883L_REGISTER_DATA_Y_LSB, &raw_y) ||
|
||||||
|
!this->read_byte_16_(QMC5883L_REGISTER_DATA_Z_LSB, &raw_z)) {
|
||||||
|
this->status_set_warning();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float mg_per_bit;
|
||||||
|
switch (this->range_) {
|
||||||
|
case QMC5883L_RANGE_200_UT:
|
||||||
|
mg_per_bit = 0.0833f;
|
||||||
|
break;
|
||||||
|
case QMC5883L_RANGE_800_UT:
|
||||||
|
mg_per_bit = 0.333f;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mg_per_bit = NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// in µT
|
||||||
|
const float x = int16_t(raw_x) * mg_per_bit * 0.1f;
|
||||||
|
const float y = int16_t(raw_y) * mg_per_bit * 0.1f;
|
||||||
|
const float z = int16_t(raw_z) * mg_per_bit * 0.1f;
|
||||||
|
|
||||||
|
float heading = atan2f(0.0f - x, y) * 180.0f / M_PI;
|
||||||
|
ESP_LOGD(TAG, "Got x=%0.02fµT y=%0.02fµT z=%0.02fµT heading=%0.01f° status=%u", x, y, z, heading, status);
|
||||||
|
|
||||||
|
if (this->x_sensor_ != nullptr)
|
||||||
|
this->x_sensor_->publish_state(x);
|
||||||
|
if (this->y_sensor_ != nullptr)
|
||||||
|
this->y_sensor_->publish_state(y);
|
||||||
|
if (this->z_sensor_ != nullptr)
|
||||||
|
this->z_sensor_->publish_state(z);
|
||||||
|
if (this->heading_sensor_ != nullptr)
|
||||||
|
this->heading_sensor_->publish_state(heading);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QMC5883LComponent::read_byte_16_(uint8_t a_register, uint16_t *data) {
|
||||||
|
bool success = this->read_byte_16(a_register, data);
|
||||||
|
*data = (*data & 0x00FF) << 8 | (*data & 0xFF00) >> 8; // Flip Byte oder, LSB first;
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace qmc5883l
|
||||||
|
} // namespace esphome
|
60
esphome/components/qmc5883l/qmc5883l.h
Normal file
60
esphome/components/qmc5883l/qmc5883l.h
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace qmc5883l {
|
||||||
|
|
||||||
|
enum QMC5883LDatarate {
|
||||||
|
QMC5883L_DATARATE_10_HZ = 0b00,
|
||||||
|
QMC5883L_DATARATE_50_HZ = 0b01,
|
||||||
|
QMC5883L_DATARATE_100_HZ = 0b10,
|
||||||
|
QMC5883L_DATARATE_200_HZ = 0b11,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum QMC5883LRange {
|
||||||
|
QMC5883L_RANGE_200_UT = 0b00,
|
||||||
|
QMC5883L_RANGE_800_UT = 0b01,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum QMC5883LOversampling {
|
||||||
|
QMC5883L_SAMPLING_512 = 0b00,
|
||||||
|
QMC5883L_SAMPLING_256 = 0b01,
|
||||||
|
QMC5883L_SAMPLING_128 = 0b10,
|
||||||
|
QMC5883L_SAMPLING_64 = 0b11,
|
||||||
|
};
|
||||||
|
|
||||||
|
class QMC5883LComponent : public PollingComponent, public i2c::I2CDevice {
|
||||||
|
public:
|
||||||
|
void setup() override;
|
||||||
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
void set_datarate(QMC5883LDatarate datarate) { datarate_ = datarate; }
|
||||||
|
void set_range(QMC5883LRange range) { range_ = range; }
|
||||||
|
void set_oversampling(QMC5883LOversampling oversampling) { oversampling_ = oversampling; }
|
||||||
|
void set_x_sensor(sensor::Sensor *x_sensor) { x_sensor_ = x_sensor; }
|
||||||
|
void set_y_sensor(sensor::Sensor *y_sensor) { y_sensor_ = y_sensor; }
|
||||||
|
void set_z_sensor(sensor::Sensor *z_sensor) { z_sensor_ = z_sensor; }
|
||||||
|
void set_heading_sensor(sensor::Sensor *heading_sensor) { heading_sensor_ = heading_sensor; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QMC5883LDatarate datarate_{QMC5883L_DATARATE_10_HZ};
|
||||||
|
QMC5883LRange range_{QMC5883L_RANGE_200_UT};
|
||||||
|
QMC5883LOversampling oversampling_{QMC5883L_SAMPLING_512};
|
||||||
|
sensor::Sensor *x_sensor_;
|
||||||
|
sensor::Sensor *y_sensor_;
|
||||||
|
sensor::Sensor *z_sensor_;
|
||||||
|
sensor::Sensor *heading_sensor_;
|
||||||
|
enum ErrorCode {
|
||||||
|
NONE = 0,
|
||||||
|
COMMUNICATION_FAILED,
|
||||||
|
} error_code_;
|
||||||
|
bool read_byte_16_(uint8_t a_register, uint16_t *data);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qmc5883l
|
||||||
|
} // namespace esphome
|
105
esphome/components/qmc5883l/sensor.py
Normal file
105
esphome/components/qmc5883l/sensor.py
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
# coding=utf-8
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import i2c, sensor
|
||||||
|
from esphome.const import (CONF_ADDRESS, CONF_ID, CONF_OVERSAMPLING, CONF_RANGE, ICON_MAGNET,
|
||||||
|
UNIT_MICROTESLA, UNIT_DEGREES, ICON_SCREEN_ROTATION,
|
||||||
|
CONF_UPDATE_INTERVAL)
|
||||||
|
from esphome.py_compat import text_type
|
||||||
|
|
||||||
|
DEPENDENCIES = ['i2c']
|
||||||
|
|
||||||
|
qmc5883l_ns = cg.esphome_ns.namespace('qmc5883l')
|
||||||
|
|
||||||
|
CONF_FIELD_STRENGTH_X = 'field_strength_x'
|
||||||
|
CONF_FIELD_STRENGTH_Y = 'field_strength_y'
|
||||||
|
CONF_FIELD_STRENGTH_Z = 'field_strength_z'
|
||||||
|
CONF_HEADING = 'heading'
|
||||||
|
|
||||||
|
QMC5883LComponent = qmc5883l_ns.class_(
|
||||||
|
'QMC5883LComponent', cg.PollingComponent, i2c.I2CDevice)
|
||||||
|
|
||||||
|
QMC5883LDatarate = qmc5883l_ns.enum('QMC5883LDatarate')
|
||||||
|
QMC5883LDatarates = {
|
||||||
|
10: QMC5883LDatarate.QMC5883L_DATARATE_10_HZ,
|
||||||
|
50: QMC5883LDatarate.QMC5883L_DATARATE_50_HZ,
|
||||||
|
100: QMC5883LDatarate.QMC5883L_DATARATE_100_HZ,
|
||||||
|
200: QMC5883LDatarate.QMC5883L_DATARATE_200_HZ,
|
||||||
|
}
|
||||||
|
|
||||||
|
QMC5883LRange = qmc5883l_ns.enum('QMC5883LRange')
|
||||||
|
QMC5883L_RANGES = {
|
||||||
|
200: QMC5883LRange.QMC5883L_RANGE_200_UT,
|
||||||
|
800: QMC5883LRange.QMC5883L_RANGE_800_UT,
|
||||||
|
}
|
||||||
|
|
||||||
|
QMC5883LOversampling = qmc5883l_ns.enum('QMC5883LOversampling')
|
||||||
|
QMC5883LOversamplings = {
|
||||||
|
512: QMC5883LOversampling.QMC5883L_SAMPLING_512,
|
||||||
|
256: QMC5883LOversampling.QMC5883L_SAMPLING_256,
|
||||||
|
128: QMC5883LOversampling.QMC5883L_SAMPLING_128,
|
||||||
|
64: QMC5883LOversampling.QMC5883L_SAMPLING_64,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_enum(enum_values, units=None, int=True):
|
||||||
|
_units = []
|
||||||
|
if units is not None:
|
||||||
|
_units = units if isinstance(units, list) else [units]
|
||||||
|
_units = [text_type(x) for x in _units]
|
||||||
|
enum_bound = cv.enum(enum_values, int=int)
|
||||||
|
|
||||||
|
def validate_enum_bound(value):
|
||||||
|
value = cv.string(value)
|
||||||
|
for unit in _units:
|
||||||
|
if value.endswith(unit):
|
||||||
|
value = value[:-len(unit)]
|
||||||
|
break
|
||||||
|
return enum_bound(value)
|
||||||
|
return validate_enum_bound
|
||||||
|
|
||||||
|
|
||||||
|
field_strength_schema = sensor.sensor_schema(UNIT_MICROTESLA, ICON_MAGNET, 1)
|
||||||
|
heading_schema = sensor.sensor_schema(UNIT_DEGREES, ICON_SCREEN_ROTATION, 1)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema({
|
||||||
|
cv.GenerateID(): cv.declare_id(QMC5883LComponent),
|
||||||
|
cv.Optional(CONF_ADDRESS): cv.i2c_address,
|
||||||
|
cv.Optional(CONF_RANGE, default=u'200µT'): validate_enum(QMC5883L_RANGES, units=["uT", u"µT"]),
|
||||||
|
cv.Optional(CONF_OVERSAMPLING, default="512x"): validate_enum(QMC5883LOversamplings, units="x"),
|
||||||
|
cv.Optional(CONF_FIELD_STRENGTH_X): field_strength_schema,
|
||||||
|
cv.Optional(CONF_FIELD_STRENGTH_Y): field_strength_schema,
|
||||||
|
cv.Optional(CONF_FIELD_STRENGTH_Z): field_strength_schema,
|
||||||
|
cv.Optional(CONF_HEADING): heading_schema,
|
||||||
|
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x0D))
|
||||||
|
|
||||||
|
|
||||||
|
def auto_data_rate(config):
|
||||||
|
interval_sec = config[CONF_UPDATE_INTERVAL].seconds
|
||||||
|
interval_hz = 1.0/interval_sec
|
||||||
|
for datarate in sorted(QMC5883LDatarates.keys()):
|
||||||
|
if float(datarate) >= interval_hz:
|
||||||
|
return QMC5883LDatarates[datarate]
|
||||||
|
return QMC5883LDatarates[200]
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
yield cg.register_component(var, config)
|
||||||
|
yield i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
|
cg.add(var.set_oversampling(config[CONF_OVERSAMPLING]))
|
||||||
|
cg.add(var.set_datarate(auto_data_rate(config)))
|
||||||
|
cg.add(var.set_range(config[CONF_RANGE]))
|
||||||
|
if CONF_FIELD_STRENGTH_X in config:
|
||||||
|
sens = yield sensor.new_sensor(config[CONF_FIELD_STRENGTH_X])
|
||||||
|
cg.add(var.set_x_sensor(sens))
|
||||||
|
if CONF_FIELD_STRENGTH_Y in config:
|
||||||
|
sens = yield sensor.new_sensor(config[CONF_FIELD_STRENGTH_Y])
|
||||||
|
cg.add(var.set_y_sensor(sens))
|
||||||
|
if CONF_FIELD_STRENGTH_Z in config:
|
||||||
|
sens = yield sensor.new_sensor(config[CONF_FIELD_STRENGTH_Z])
|
||||||
|
cg.add(var.set_z_sensor(sens))
|
||||||
|
if CONF_HEADING in config:
|
||||||
|
sens = yield sensor.new_sensor(config[CONF_HEADING])
|
||||||
|
cg.add(var.set_heading_sensor(sens))
|
|
@ -417,6 +417,20 @@ sensor:
|
||||||
heading:
|
heading:
|
||||||
name: "HMC5883L Heading"
|
name: "HMC5883L Heading"
|
||||||
range: 130uT
|
range: 130uT
|
||||||
|
oversampling: 8x
|
||||||
|
update_interval: 15s
|
||||||
|
- platform: qmc5883l
|
||||||
|
address: 0x0D
|
||||||
|
field_strength_x:
|
||||||
|
name: "QMC5883L Field Strength X"
|
||||||
|
field_strength_y:
|
||||||
|
name: "QMC5883L Field Strength Y"
|
||||||
|
field_strength_z:
|
||||||
|
name: "QMC5883L Field Strength Z"
|
||||||
|
heading:
|
||||||
|
name: "QMC5883L Heading"
|
||||||
|
range: 800uT
|
||||||
|
oversampling: 256x
|
||||||
update_interval: 15s
|
update_interval: 15s
|
||||||
- platform: hx711
|
- platform: hx711
|
||||||
name: "HX711 Value"
|
name: "HX711 Value"
|
||||||
|
|
Loading…
Reference in a new issue