model checks and extra config

This commit is contained in:
Anton Viktorov 2024-05-23 05:43:57 +02:00
parent 2dad225f35
commit 9c24d199e5
3 changed files with 148 additions and 54 deletions

View file

@ -4,8 +4,10 @@ from esphome.components import i2c
from esphome import automation
from esphome.const import (
CONF_ID,
CONF_ADDRESS,
CONF_MODEL,
CONF_RANGE,
CONF_RESOLUTION,
)
DEPENDENCIES = ["i2c"]
@ -34,20 +36,42 @@ MSA_MODELS = {
}
MSARange = msa3xx_ns.enum("Range", True)
MSA_RANGE = {
MSA_RANGES = {
"2G": MSARange.RANGE_2G,
"4G": MSARange.RANGE_4G,
"8G": MSARange.RANGE_8G,
"16G": MSARange.RANGE_16G,
}
# MSABandwidth = msa3xx_ns.enum("Bandwidth", True)
# MSA_BANDWIDTHS = {
# "1.95HZ": MSABandwidth.BW_1_95HZ,
# "3.9HZ": MSABandwidth.BW_3_9HZ,
# "7.81HZ": MSABandwidth.BW_7_81HZ,
# "15.63HZ": MSABandwidth.BW_15_63HZ,
# "31.25HZ": MSABandwidth.BW_31_25HZ,
# "62.5HZ": MSABandwidth.BW_62_5HZ,
# "125HZ": MSABandwidth.BW_125HZ,
# "250HZ": MSABandwidth.BW_250HZ,
# "500HZ": MSABandwidth.BW_500HZ,
# }
MSAResolution = msa3xx_ns.enum("Resolution", True)
MSA_RESOLUTIONS = {
14: MSAResolution.RES_14BIT,
12: MSAResolution.RES_12BIT,
10: MSAResolution.RES_10BIT,
8: MSAResolution.RES_8BIT,
}
CONFIG_SCHEMA = cv.Schema(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(MSA3xxComponent),
cv.Required(CONF_MODEL): cv.enum(MSA_MODELS, upper=True),
cv.Optional(CONF_RANGE, default="2G"): cv.enum(MSA_RANGE, upper=True),
cv.Optional(CONF_RANGE, default="2G"): cv.enum(MSA_RANGES, upper=True),
# cv.Optional(CONF_BANDWIDTH, default="250HZ"): cv.enum(MSA_BANDWIDTHS, upper=True),
cv.Optional(CONF_RESOLUTION): cv.enum(MSA_RESOLUTIONS),
cv.Optional(CONF_OFFSET_X, default=0): cv.float_range(min=-4.5, max=4.5),
cv.Optional(CONF_OFFSET_Y, default=0): cv.float_range(min=-4.5, max=4.5),
cv.Optional(CONF_OFFSET_Z, default=0): cv.float_range(min=-4.5, max=4.5),
@ -62,15 +86,48 @@ CONFIG_SCHEMA = cv.Schema(
}
)
.extend(cv.polling_component_schema("10s"))
.extend(i2c.i2c_device_schema(0x62))
.extend(i2c.i2c_device_schema(0x00))
)
# def validate_model(config):
# model = config[CONF_MODEL]
def validate_i2c_address(config):
if config[CONF_ADDRESS] == 0x00:
if config[CONF_MODEL] == "MSA301":
config[CONF_ADDRESS] = 0x26
elif config[CONF_MODEL] == "MSA311":
config[CONF_ADDRESS] = 0x62
return config
if (config[CONF_MODEL] == "MSA301" and config[CONF_ADDRESS] == 0x26) or (
config[CONF_MODEL] == "MSA311" and config[CONF_ADDRESS] == 0x62
):
return config
raise cv.Invalid(
"Wrong I2C Address ("
+ hex(config[CONF_ADDRESS])
+ "). MSA301 shall be 0x26, MSA311 shall be 0x62."
)
# FINAL_VALIDATE_SCHEMA = validate_model
def validate_resolution(config):
if CONF_RESOLUTION not in config:
if config[CONF_MODEL] == "MSA301":
config[CONF_RESOLUTION] = 14
elif config[CONF_MODEL] == "MSA311":
config[CONF_RESOLUTION] = 12
return config
if config[CONF_MODEL] == "MSA311" and config[CONF_RESOLUTION] != 12:
raise cv.Invalid("Check resolution. MSA311 only supports 12-bit")
return config
FINAL_VALIDATE_SCHEMA = cv.All(
validate_i2c_address,
validate_resolution,
)
async def to_code(config):
@ -84,11 +141,20 @@ async def to_code(config):
config[CONF_OFFSET_X], config[CONF_OFFSET_Y], config[CONF_OFFSET_Z]
)
)
cg.add(var.set_range(config[CONF_RANGE]))
cg.add(var.set_range(MSA_RANGES[config[CONF_RANGE]]))
cg.add(var.set_resolution(MSA_RESOLUTIONS[config[CONF_RESOLUTION]]))
irq_set_0 = 0
irq_set_1 = 0
if CONF_ON_ORIENTATION in config:
await automation.build_automation(
var.get_orientation_trigger(),
[],
config[CONF_ON_ORIENTATION],
)
irq_set_0 |= 1 << 6
if CONF_ON_TAP in config:
await automation.build_automation(
var.get_tap_trigger(),
@ -105,14 +171,6 @@ async def to_code(config):
)
irq_set_0 |= 1 << 4
if CONF_ON_ORIENTATION in config:
await automation.build_automation(
var.get_orientation_trigger(),
[],
config[CONF_ON_ORIENTATION],
)
irq_set_0 |= 1 << 6
if CONF_ON_FREEFALL in config:
await automation.build_automation(
var.get_freefall_trigger(),

View file

@ -108,11 +108,11 @@ void MSA3xxComponent::setup() {
// S8g : 256 (2^8) : 1024 (2^10)
// S16g : 128 (2^7) : 512 (2^9)
if (this->model_ == Model::MSA301) {
this->params_.accel_data_width = 14;
this->params_.scale_factor_exp = static_cast<uint8_t>(this->range_) - 12;
this->device_params_.accel_data_width = 14;
this->device_params_.scale_factor_exp = static_cast<uint8_t>(this->range_) - 12;
} else if (this->model_ == Model::MSA311) {
this->params_.accel_data_width = 12;
this->params_.scale_factor_exp = static_cast<uint8_t>(this->range_) - 10;
this->device_params_.accel_data_width = 12;
this->device_params_.scale_factor_exp = static_cast<uint8_t>(this->range_) - 10;
} else {
ESP_LOGE(TAG, "Unknown model");
@ -120,7 +120,7 @@ void MSA3xxComponent::setup() {
return;
}
this->setup_odr_();
this->setup_odr_(this->data_rate_);
this->setup_power_mode_bandwidth_(this->power_mode_, this->bandwidth_);
this->setup_range_resolution_(this->range_, this->resolution_);
this->setup_offset_(this->offset_x_, this->offset_y_, this->offset_z_);
@ -164,16 +164,19 @@ bool MSA3xxComponent::read_data_() {
return alpha * new_value + (1.0f - alpha) * old_value;
};
this->data_.lsb_x = this->two_complement_to_normal_(
raw_to_x_bit(accel_data[0], accel_data[1], this->params_.accel_data_width), this->params_.accel_data_width);
this->data_.lsb_y = this->two_complement_to_normal_(
raw_to_x_bit(accel_data[2], accel_data[3], this->params_.accel_data_width), this->params_.accel_data_width);
this->data_.lsb_z = this->two_complement_to_normal_(
raw_to_x_bit(accel_data[4], accel_data[5], this->params_.accel_data_width), this->params_.accel_data_width);
this->data_.lsb_x =
this->two_complement_to_normal_(raw_to_x_bit(accel_data[0], accel_data[1], this->device_params_.accel_data_width),
this->device_params_.accel_data_width);
this->data_.lsb_y =
this->two_complement_to_normal_(raw_to_x_bit(accel_data[2], accel_data[3], this->device_params_.accel_data_width),
this->device_params_.accel_data_width);
this->data_.lsb_z =
this->two_complement_to_normal_(raw_to_x_bit(accel_data[4], accel_data[5], this->device_params_.accel_data_width),
this->device_params_.accel_data_width);
this->data_.x = lpf(ldexp(this->data_.lsb_x, this->params_.scale_factor_exp) * GRAVITY_EARTH, this->data_.x);
this->data_.y = lpf(ldexp(this->data_.lsb_y, this->params_.scale_factor_exp) * GRAVITY_EARTH, this->data_.y);
this->data_.z = lpf(ldexp(this->data_.lsb_z, this->params_.scale_factor_exp) * GRAVITY_EARTH, this->data_.z);
this->data_.x = lpf(ldexp(this->data_.lsb_x, this->device_params_.scale_factor_exp) * GRAVITY_EARTH, this->data_.x);
this->data_.y = lpf(ldexp(this->data_.lsb_y, this->device_params_.scale_factor_exp) * GRAVITY_EARTH, this->data_.y);
this->data_.z = lpf(ldexp(this->data_.lsb_z, this->device_params_.scale_factor_exp) * GRAVITY_EARTH, this->data_.z);
// ESP_LOGVV(TAG, "Got raw data {x=%5d, y=%5d, z=%5d}, accel={x=%+1.3f m/s², y=%+1.3f m/s², z=%+1.3f m/s²}",
// this->data_.lsb_x, this->data_.lsb_y, this->data_.lsb_z, this->data_.x, this->data_.y, this->data_.z);
@ -182,7 +185,11 @@ bool MSA3xxComponent::read_data_() {
}
bool MSA3xxComponent::read_motion_status_() {
if (!this->read_byte(static_cast<uint8_t>(RegisterMap::MOTION_INTERRUPT), &this->status_.mi.raw)) {
if (!this->read_byte(static_cast<uint8_t>(RegisterMap::MOTION_INTERRUPT), &this->status_.motion_int.raw)) {
return false;
}
if (!this->read_byte(static_cast<uint8_t>(RegisterMap::ORIENTATION_STATUS), &this->status_.orientation.raw)) {
return false;
}
@ -231,20 +238,21 @@ void MSA3xxComponent::set_offset(float offset_x, float offset_y, float offset_z)
this->offset_z_ = offset_z;
}
void MSA3xxComponent::setup_odr_() {
RegODR odr;
void MSA3xxComponent::setup_odr_(DataRate rate) {
RegOutputDataRate reg_odr;
auto reg = this->read_byte(static_cast<uint8_t>(RegisterMap::ODR));
if (reg.has_value()) {
odr.raw = reg.value();
reg_odr.raw = reg.value();
} else {
odr.raw = 0x0F; // defaut from datasheet
reg_odr.raw = 0x0F; // defaut from datasheet
}
odr.x_axis_disable = 0;
odr.y_axis_disable = 0;
odr.z_axis_disable = 0;
reg_odr.x_axis_disable = 0;
reg_odr.y_axis_disable = 0;
reg_odr.z_axis_disable = 0;
reg_odr.odr = rate;
this->write_byte(static_cast<uint8_t>(RegisterMap::ODR), odr.raw);
this->write_byte(static_cast<uint8_t>(RegisterMap::ODR), reg_odr.raw);
}
void MSA3xxComponent::setup_power_mode_bandwidth_(PowerMode power_mode, Bandwidth bandwidth) {
@ -259,7 +267,7 @@ void MSA3xxComponent::setup_power_mode_bandwidth_(PowerMode power_mode, Bandwidt
}
power_mode_bandwidth.power_mode = power_mode;
power_mode_bandwidth.low_power_bw = bandwidth;
power_mode_bandwidth.low_power_bandwidth = bandwidth;
this->write_byte(static_cast<uint8_t>(RegisterMap::POWER_MODE_BANDWIDTH), power_mode_bandwidth.raw);
}
@ -300,21 +308,21 @@ int64_t MSA3xxComponent::two_complement_to_normal_(uint64_t value, uint8_t bits)
}
void MSA3xxComponent::process_interrupts_() {
if (this->status_.mi.raw == 0) {
if (this->status_.motion_int.raw == 0) {
return;
}
if (this->status_.mi.single_tap_interrupt) {
if (this->status_.motion_int.single_tap_interrupt) {
ESP_LOGW(TAG, "Single Tap detected");
this->tap_trigger_.trigger();
}
if (this->status_.mi.double_tap_interrupt) {
if (this->status_.motion_int.double_tap_interrupt) {
ESP_LOGW(TAG, "Double Tap detected");
this->double_tap_trigger_.trigger();
}
if (this->status_.mi.orientation_interrupt) {
if (this->status_.motion_int.orientation_interrupt) {
ESP_LOGW(TAG, "Orientation changed");
this->orientation_trigger_.trigger();
}

View file

@ -91,6 +91,20 @@ enum class Bandwidth : uint8_t {
BW_500HZ = 0b1010,
};
enum class DataRate : uint8_t {
ODR_1HZ = 0b0000, // not available in normal mode
ODR_1_95HZ = 0b0001, // not available in normal mode
ODR_3_9HZ = 0b0010,
ODR_7_81HZ = 0b0011,
ODR_15_63HZ = 0b0100,
ODR_31_25HZ = 0b0101,
ODR_62_5HZ = 0b0110,
ODR_125HZ = 0b0111,
ODR_250HZ = 0b1000,
ODR_500HZ = 0b1001, // not available in low power mode
ODR_1000HZ = 0b1010, // not available in low power mode
};
// 0x09
union RegMotionInterrupt {
struct {
@ -106,21 +120,31 @@ union RegMotionInterrupt {
uint8_t raw;
};
// 0x0C
union RegOrientationStatus {
struct {
uint8_t reserved_0_3 : 4;
uint8_t orient : 3;
uint8_t reserved_7 : 1;
};
uint8_t raw{0x00};
};
// 0x0f
union RegRangeResolution {
struct {
Range range : 2;
Resolution resolution : 2;
bool reserved_2 : 4;
uint8_t reserved_2 : 4;
};
uint8_t raw{0x00};
};
// 0x10
union RegODR {
union RegOutputDataRate {
struct {
uint8_t odr : 4;
bool reserved_4 : 1;
DataRate odr : 4;
uint8_t reserved_4 : 1;
bool z_axis_disable : 1;
bool y_axis_disable : 1;
bool x_axis_disable : 1;
@ -131,9 +155,9 @@ union RegODR {
// 0x11
union RegPowerModeBandwidth {
struct {
bool reserved_0 : 1;
Bandwidth low_power_bw : 4;
bool reserved_5 : 1;
uint8_t reserved_0 : 1;
Bandwidth low_power_bandwidth : 4;
uint8_t reserved_5 : 1;
PowerMode power_mode : 2;
};
uint8_t raw{0xde};
@ -143,7 +167,7 @@ union RegPowerModeBandwidth {
union RegTapDuration {
struct {
uint8_t duration : 3;
bool reserved_3 : 3;
uint8_t reserved : 3;
bool tap_shock : 1;
bool tap_quiet : 1;
};
@ -163,6 +187,8 @@ class MSA3xxComponent : public PollingComponent, public i2c::I2CDevice {
void set_model(Model model) { this->model_ = model; }
void set_offset(float offset_x, float offset_y, float offset_z);
void set_range(Range range) { this->range_ = range; }
void set_bandwidth(Bandwidth bandwidth) { this->bandwidth_ = bandwidth; }
void set_resolution(Resolution resolution) { this->resolution_ = resolution; }
void set_interrupts(uint8_t set0, uint8_t set1) {
this->int_set_0_ = set0;
this->int_set_1_ = set1;
@ -183,6 +209,7 @@ class MSA3xxComponent : public PollingComponent, public i2c::I2CDevice {
Model model_{Model::MSA311};
PowerMode power_mode_{PowerMode::NORMAL};
DataRate data_rate_{DataRate::ODR_250HZ};
Bandwidth bandwidth_{Bandwidth::BW_250HZ};
Range range_{Range::RANGE_2G};
Resolution resolution_{Resolution::RES_14BIT};
@ -195,7 +222,7 @@ class MSA3xxComponent : public PollingComponent, public i2c::I2CDevice {
struct {
int scale_factor_exp;
uint8_t accel_data_width;
} params_{};
} device_params_{};
struct {
int16_t lsb_x, lsb_y, lsb_z;
@ -203,10 +230,11 @@ class MSA3xxComponent : public PollingComponent, public i2c::I2CDevice {
} data_{};
struct {
RegMotionInterrupt mi;
RegMotionInterrupt motion_int;
RegOrientationStatus orientation;
} status_{};
void setup_odr_();
void setup_odr_(DataRate rate);
void setup_power_mode_bandwidth_(PowerMode power_mode, Bandwidth bandwidth);
void setup_range_resolution_(Range range, Resolution resolution);
void setup_offset_(float offset_x, float offset_y, float offset_z);