diff --git a/esphome/components/msa3xx/__init__.py b/esphome/components/msa3xx/__init__.py index 9137dac92b..2590570225 100644 --- a/esphome/components/msa3xx/__init__.py +++ b/esphome/components/msa3xx/__init__.py @@ -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(), diff --git a/esphome/components/msa3xx/msa3xx.cpp b/esphome/components/msa3xx/msa3xx.cpp index 45778b976d..737eea5851 100644 --- a/esphome/components/msa3xx/msa3xx.cpp +++ b/esphome/components/msa3xx/msa3xx.cpp @@ -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(this->range_) - 12; + this->device_params_.accel_data_width = 14; + this->device_params_.scale_factor_exp = static_cast(this->range_) - 12; } else if (this->model_ == Model::MSA311) { - this->params_.accel_data_width = 12; - this->params_.scale_factor_exp = static_cast(this->range_) - 10; + this->device_params_.accel_data_width = 12; + this->device_params_.scale_factor_exp = static_cast(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(RegisterMap::MOTION_INTERRUPT), &this->status_.mi.raw)) { + if (!this->read_byte(static_cast(RegisterMap::MOTION_INTERRUPT), &this->status_.motion_int.raw)) { + return false; + } + + if (!this->read_byte(static_cast(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(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(RegisterMap::ODR), odr.raw); + this->write_byte(static_cast(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(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(); } diff --git a/esphome/components/msa3xx/msa3xx.h b/esphome/components/msa3xx/msa3xx.h index 3bde37689c..263df06735 100644 --- a/esphome/components/msa3xx/msa3xx.h +++ b/esphome/components/msa3xx/msa3xx.h @@ -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);