diff --git a/CODEOWNERS b/CODEOWNERS index c006db2a6a..76156db6e6 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -159,6 +159,7 @@ esphome/components/midea/* @dudanov esphome/components/midea_ir/* @dudanov esphome/components/mitsubishi/* @RubyBailey esphome/components/mlx90393/* @functionpointer +esphome/components/mmc5603/* @benhoff esphome/components/modbus_controller/* @martgras esphome/components/modbus_controller/binary_sensor/* @martgras esphome/components/modbus_controller/number/* @martgras diff --git a/esphome/components/mmc5603/__init__.py b/esphome/components/mmc5603/__init__.py new file mode 100644 index 0000000000..cc88f26231 --- /dev/null +++ b/esphome/components/mmc5603/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@benhoff"] diff --git a/esphome/components/mmc5603/mmc5603.cpp b/esphome/components/mmc5603/mmc5603.cpp new file mode 100644 index 0000000000..6fbf4810f2 --- /dev/null +++ b/esphome/components/mmc5603/mmc5603.cpp @@ -0,0 +1,162 @@ +#include "mmc5603.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace mmc5603 { + +static const char *const TAG = "mmc5603"; +static const uint8_t MMC5603_ADDRESS = 0x30; +static const uint8_t MMC56X3_PRODUCT_ID = 0x39; + +static const uint8_t MMC56X3_DEFAULT_ADDRESS = 0x30; +static const uint8_t MMC56X3_CHIP_ID = 0x10; + +static const uint8_t MMC56X3_ADDR_XOUT0 = 0x00; +static const uint8_t MMC56X3_ADDR_XOUT1 = 0x01; +static const uint8_t MMC56X3_ADDR_XOUT2 = 0x06; + +static const uint8_t MMC56X3_ADDR_YOUT0 = 0x02; +static const uint8_t MMC56X3_ADDR_YOUT1 = 0x03; +static const uint8_t MMC56X3_ADDR_YOUT2 = 0x07; + +static const uint8_t MMC56X3_ADDR_ZOUT0 = 0x04; +static const uint8_t MMC56X3_ADDR_ZOUT1 = 0x05; +static const uint8_t MMC56X3_ADDR_ZOUT2 = 0x08; + +static const uint8_t MMC56X3_OUT_TEMP = 0x09; +static const uint8_t MMC56X3_STATUS_REG = 0x18; +static const uint8_t MMC56X3_CTRL0_REG = 0x1B; +static const uint8_t MMC56X3_CTRL1_REG = 0x1C; +static const uint8_t MMC56X3_CTRL2_REG = 0x1D; +static const uint8_t MMC5603_ODR_REG = 0x1A; + +void MMC5603Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up MMC5603..."); + uint8_t id = 0; + if (!this->read_byte(MMC56X3_PRODUCT_ID, &id)) { + this->error_code_ = COMMUNICATION_FAILED; + this->mark_failed(); + return; + } + + if (id != MMC56X3_CHIP_ID) { + ESP_LOGCONFIG(TAG, "Chip Wrong"); + this->error_code_ = ID_REGISTERS; + this->mark_failed(); + return; + } + + if (!this->write_byte(MMC56X3_CTRL1_REG, 0x80)) { // turn on set bit + ESP_LOGCONFIG(TAG, "Control 1 Failed for set bit"); + this->error_code_ = COMMUNICATION_FAILED; + this->mark_failed(); + return; + } + + if (!this->write_byte(MMC56X3_CTRL0_REG, 0x08)) { // turn on set bit + ESP_LOGCONFIG(TAG, "Control 0 Failed for set bit"); + this->error_code_ = COMMUNICATION_FAILED; + this->mark_failed(); + return; + } + + if (!this->write_byte(MMC56X3_CTRL0_REG, 0x10)) { + this->error_code_ = COMMUNICATION_FAILED; + this->mark_failed(); + return; + } + + uint8_t ctrl_2 = 0; + + ctrl_2 &= ~0x10; // turn off cmm_en bit + if (!this->write_byte(MMC56X3_CTRL2_REG, ctrl_2)) { + this->error_code_ = COMMUNICATION_FAILED; + this->mark_failed(); + return; + } +} +void MMC5603Component::dump_config() { + ESP_LOGCONFIG(TAG, "MMC5603:"); + LOG_I2C_DEVICE(this); + if (this->error_code_ == COMMUNICATION_FAILED) { + ESP_LOGE(TAG, "Communication with MMC5603 failed!"); + } else if (this->error_code_ == ID_REGISTERS) { + ESP_LOGE(TAG, "The ID registers don't match - Is this really an MMC5603?"); + } + 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 MMC5603Component::get_setup_priority() const { return setup_priority::DATA; } + +void MMC5603Component::update() { + if (!this->write_byte(MMC56X3_CTRL0_REG, 0x01)) { + this->status_set_warning(); + return; + } + uint8_t status = 0; + if (!this->read_byte(MMC56X3_STATUS_REG, &status)) { + this->status_set_warning(); + return; + } + + uint8_t buffer[9] = {0}; + + if (!this->read_byte(MMC56X3_ADDR_XOUT0, &buffer[0]) || !this->read_byte(MMC56X3_ADDR_XOUT1, &buffer[1]) || + !this->read_byte(MMC56X3_ADDR_XOUT2, &buffer[2])) { + this->status_set_warning(); + return; + } + + if (!this->read_byte(MMC56X3_ADDR_YOUT0, &buffer[3]) || !this->read_byte(MMC56X3_ADDR_YOUT1, &buffer[4]) || + !this->read_byte(MMC56X3_ADDR_YOUT2, &buffer[5])) { + this->status_set_warning(); + return; + } + + if (!this->read_byte(MMC56X3_ADDR_ZOUT0, &buffer[6]) || !this->read_byte(MMC56X3_ADDR_ZOUT1, &buffer[7]) || + !this->read_byte(MMC56X3_ADDR_ZOUT2, &buffer[8])) { + this->status_set_warning(); + return; + } + + int32_t raw_x = 0; + raw_x |= buffer[0] << 12; + raw_x |= buffer[1] << 4; + raw_x |= buffer[2] << 0; + + const float x = 0.0625 * (raw_x - 524288); + + int32_t raw_y = 0; + raw_y |= buffer[3] << 12; + raw_y |= buffer[4] << 4; + raw_y |= buffer[5] << 0; + + const float y = 0.0625 * (raw_y - 524288); + + int32_t raw_z = 0; + raw_z |= buffer[6] << 12; + raw_z |= buffer[7] << 4; + raw_z |= buffer[8] << 0; + + const float z = 0.0625 * (raw_z - 524288); + + const 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°", x, y, z, heading); + + 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); +} + +} // namespace mmc5603 +} // namespace esphome diff --git a/esphome/components/mmc5603/mmc5603.h b/esphome/components/mmc5603/mmc5603.h new file mode 100644 index 0000000000..cd0893053c --- /dev/null +++ b/esphome/components/mmc5603/mmc5603.h @@ -0,0 +1,43 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace mmc5603 { + +enum MMC5603Datarate { + MMC5603_DATARATE_75_0_HZ, + MMC5603_DATARATE_150_0_HZ, + MMC5603_DATARATE_255_0_HZ, +}; + +class MMC5603Component : 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(MMC5603Datarate datarate) { datarate_ = datarate; } + 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: + MMC5603Datarate datarate_; + sensor::Sensor *x_sensor_{nullptr}; + sensor::Sensor *y_sensor_{nullptr}; + sensor::Sensor *z_sensor_{nullptr}; + sensor::Sensor *heading_sensor_{nullptr}; + enum ErrorCode { + NONE = 0, + COMMUNICATION_FAILED, + ID_REGISTERS, + } error_code_; +}; + +} // namespace mmc5603 +} // namespace esphome diff --git a/esphome/components/mmc5603/sensor.py b/esphome/components/mmc5603/sensor.py new file mode 100644 index 0000000000..348a0e7dcc --- /dev/null +++ b/esphome/components/mmc5603/sensor.py @@ -0,0 +1,91 @@ +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, + ICON_MAGNET, + STATE_CLASS_MEASUREMENT, + UNIT_MICROTESLA, + UNIT_DEGREES, + ICON_SCREEN_ROTATION, + CONF_UPDATE_INTERVAL, +) + +DEPENDENCIES = ["i2c"] + +mmc5603_ns = cg.esphome_ns.namespace("mmc5603") + +CONF_FIELD_STRENGTH_X = "field_strength_x" +CONF_FIELD_STRENGTH_Y = "field_strength_y" +CONF_FIELD_STRENGTH_Z = "field_strength_z" +CONF_HEADING = "heading" + +MMC5603Component = mmc5603_ns.class_( + "MMC5603Component", cg.PollingComponent, i2c.I2CDevice +) + + +MMC5603Datarate = mmc5603_ns.enum("MMC5603Datarate") +MMC5603Datarates = { + 75: MMC5603Datarate.MMC5603_DATARATE_75_0_HZ, + 150: MMC5603Datarate.MMC5603_DATARATE_150_0_HZ, + 255: MMC5603Datarate.MMC5603_DATARATE_255_0_HZ, +} + + +field_strength_schema = sensor.sensor_schema( + unit_of_measurement=UNIT_MICROTESLA, + icon=ICON_MAGNET, + accuracy_decimals=1, + state_class=STATE_CLASS_MEASUREMENT, +) +heading_schema = sensor.sensor_schema( + unit_of_measurement=UNIT_DEGREES, + icon=ICON_SCREEN_ROTATION, + accuracy_decimals=1, +) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(MMC5603Component), + cv.Optional(CONF_ADDRESS): cv.i2c_address, + 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(0x1E)) +) + + +def auto_data_rate(config): + interval_msec = config[CONF_UPDATE_INTERVAL].total_milliseconds + interval_hz = 1000.0 / interval_msec + for datarate in sorted(MMC5603Datarates.keys()): + if float(datarate) >= interval_hz: + return MMC5603Datarates[datarate] + return MMC5603Datarates[75] + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) + + cg.add(var.set_datarate(auto_data_rate(config))) + if CONF_FIELD_STRENGTH_X in config: + sens = await sensor.new_sensor(config[CONF_FIELD_STRENGTH_X]) + cg.add(var.set_x_sensor(sens)) + if CONF_FIELD_STRENGTH_Y in config: + sens = await sensor.new_sensor(config[CONF_FIELD_STRENGTH_Y]) + cg.add(var.set_y_sensor(sens)) + if CONF_FIELD_STRENGTH_Z in config: + sens = await sensor.new_sensor(config[CONF_FIELD_STRENGTH_Z]) + cg.add(var.set_z_sensor(sens)) + if CONF_HEADING in config: + sens = await sensor.new_sensor(config[CONF_HEADING]) + cg.add(var.set_heading_sensor(sens)) diff --git a/tests/test1.yaml b/tests/test1.yaml index 5be6395729..c2a2ed5c95 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -831,6 +831,15 @@ sensor: temperature: name: MPU6886 Temperature i2c_id: i2c_bus + - platform: mmc5603 + address: 0x30 + field_strength_x: + name: HMC5883L Field Strength X + field_strength_y: + name: HMC5883L Field Strength Y + field_strength_z: + name: HMC5883L Field Strength Z + i2c_id: i2c_bus - platform: dps310 temperature: name: DPS310 Temperature