forked from esphome/esphome
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement sensor component for MMC5983 (esphome#5361)
- Loading branch information
Showing
10 changed files
with
249 additions
and
9 deletions.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
CODEOWNERS = ["@agoode"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
// See https://github.com/sparkfun/SparkFun_MMC5983MA_Magnetometer_Arduino_Library/tree/main | ||
// for datasheets and an Arduino implementation. | ||
|
||
#include "mmc5983.h" | ||
#include "esphome/core/log.h" | ||
|
||
namespace esphome { | ||
namespace mmc5983 { | ||
|
||
static const char *const TAG = "mmc5983"; | ||
|
||
namespace { | ||
constexpr uint8_t IC0_ADDR = 0x09; | ||
constexpr uint8_t IC1_ADDR = 0x0a; | ||
constexpr uint8_t IC2_ADDR = 0x0b; | ||
constexpr uint8_t IC3_ADDR = 0x0c; | ||
constexpr uint8_t PRODUCT_ID_ADDR = 0x2f; | ||
|
||
float convert_data_to_millitesla(uint8_t data_17_10, uint8_t data_9_2, uint8_t data_1_0) { | ||
int32_t counts = (data_17_10 << 10) | (data_9_2 << 2) | data_1_0; | ||
counts -= 131072; // "Null Field Output" from datasheet. | ||
|
||
// Sensitivity is 16384 counts/gauss, which is 163840 counts/mT. | ||
return counts / 163840.0f; | ||
} | ||
} // namespace | ||
|
||
void MMC5983Component::update() { | ||
// Schedule a SET/RESET. This will recalibrate the sensor. | ||
// We are supposed to be able to set this once, and have it automatically continue every reading, but | ||
// this does not appear to work in continuous mode, even with En_prd_set turned on in Internal Control 2. | ||
// Bit 5 = Auto_SR_en (automatic SET/RESET enable). | ||
const uint8_t ic0_value = 0b10000; | ||
i2c::ErrorCode err = this->write_register(IC0_ADDR, &ic0_value, 1); | ||
if (err != i2c::ErrorCode::ERROR_OK) { | ||
ESP_LOGW(TAG, "Writing Internal Control 0 failed with i2c error %d", err); | ||
this->status_set_warning(); | ||
} | ||
|
||
// Read out the data, 7 bytes starting from 0x00. | ||
uint8_t data[7]; | ||
err = this->read_register(0x00, data, sizeof(data)); | ||
if (err != i2c::ErrorCode::ERROR_OK) { | ||
ESP_LOGW(TAG, "Reading data failed with i2c error %d", err); | ||
this->status_set_warning(); | ||
return; | ||
} | ||
|
||
// Unpack the data and publish to sensors. | ||
// Data is in this format: | ||
// data[0]: Xout[17:10] | ||
// data[1]: Xout[9:2] | ||
// data[2]: Yout[17:10] | ||
// data[3]: Yout[9:2] | ||
// data[4]: Zout[17:10] | ||
// data[5]: Zout[9:2] | ||
// data[6]: { Xout[1], Xout[0], Yout[1], Yout[0], Zout[1], Zout[0], 0, 0 } | ||
if (this->x_sensor_) { | ||
this->x_sensor_->publish_state(convert_data_to_millitesla(data[0], data[1], (data[6] & 0b11000000) >> 6)); | ||
} | ||
if (this->y_sensor_) { | ||
this->y_sensor_->publish_state(convert_data_to_millitesla(data[2], data[3], (data[6] & 0b00110000) >> 4)); | ||
} | ||
if (this->z_sensor_) { | ||
this->z_sensor_->publish_state(convert_data_to_millitesla(data[4], data[5], (data[6] & 0b00001100) >> 2)); | ||
} | ||
} | ||
|
||
void MMC5983Component::setup() { | ||
ESP_LOGCONFIG(TAG, "Setting up MMC5983..."); | ||
|
||
// Verify product id. | ||
const uint8_t mmc5983_product_id = 0x30; | ||
uint8_t id; | ||
i2c::ErrorCode err = this->read_register(PRODUCT_ID_ADDR, &id, 1); | ||
if (err != i2c::ErrorCode::ERROR_OK) { | ||
ESP_LOGE(TAG, "Reading product id failed with i2c error %d", err); | ||
this->mark_failed(); | ||
return; | ||
} | ||
if (id != mmc5983_product_id) { | ||
ESP_LOGE(TAG, "Product id 0x%02x does not match expected value 0x%02x", id, mmc5983_product_id); | ||
this->mark_failed(); | ||
return; | ||
} | ||
|
||
// Initialize Internal Control registers to 0. | ||
// Internal Control 0. | ||
const uint8_t zero = 0; | ||
err = this->write_register(IC0_ADDR, &zero, 1); | ||
if (err != i2c::ErrorCode::ERROR_OK) { | ||
ESP_LOGE(TAG, "Initializing Internal Control 0 failed with i2c error %d", err); | ||
this->mark_failed(); | ||
return; | ||
} | ||
// Internal Control 1. | ||
err = this->write_register(IC1_ADDR, &zero, 1); | ||
if (err != i2c::ErrorCode::ERROR_OK) { | ||
ESP_LOGE(TAG, "Initializing Internal Control 1 failed with i2c error %d", err); | ||
this->mark_failed(); | ||
return; | ||
} | ||
// Internal Control 2. | ||
err = this->write_register(IC2_ADDR, &zero, 1); | ||
if (err != i2c::ErrorCode::ERROR_OK) { | ||
ESP_LOGE(TAG, "Initializing Internal Control 2 failed with i2c error %d", err); | ||
this->mark_failed(); | ||
return; | ||
} | ||
// Internal Control 3. | ||
err = this->write_register(IC3_ADDR, &zero, 1); | ||
if (err != i2c::ErrorCode::ERROR_OK) { | ||
ESP_LOGE(TAG, "Initializing Internal Control 3 failed with i2c error %d", err); | ||
this->mark_failed(); | ||
return; | ||
} | ||
|
||
// Enable continuous mode at 100 Hz, using Internal Control 2. | ||
// Bit 3 = Cmm_en (continuous mode enable). | ||
// Bit [2:0] = Cm_freq. 0b101 = 100 Hz, the fastest reading speed at Bandwidth=100 Hz. | ||
const uint8_t ic2_value = 0b00001101; | ||
err = this->write_register(IC2_ADDR, &ic2_value, 1); | ||
if (err != i2c::ErrorCode::ERROR_OK) { | ||
ESP_LOGE(TAG, "Writing Internal Control 2 failed with i2c error %d", err); | ||
this->mark_failed(); | ||
return; | ||
} | ||
} | ||
|
||
void MMC5983Component::dump_config() { | ||
ESP_LOGD(TAG, "MMC5983:"); | ||
LOG_I2C_DEVICE(this); | ||
LOG_SENSOR(" ", "X", this->x_sensor_); | ||
LOG_SENSOR(" ", "Y", this->y_sensor_); | ||
LOG_SENSOR(" ", "Z", this->z_sensor_); | ||
} | ||
|
||
float MMC5983Component::get_setup_priority() const { return setup_priority::DATA; } | ||
|
||
} // namespace mmc5983 | ||
} // namespace esphome |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#pragma once | ||
|
||
#include "esphome/core/component.h" | ||
#include "esphome/components/sensor/sensor.h" | ||
#include "esphome/components/i2c/i2c.h" | ||
|
||
namespace esphome { | ||
namespace mmc5983 { | ||
|
||
class MMC5983Component : public PollingComponent, public i2c::I2CDevice { | ||
public: | ||
void update() override; | ||
void setup() override; | ||
void dump_config() override; | ||
float get_setup_priority() const override; | ||
|
||
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; } | ||
|
||
protected: | ||
sensor::Sensor *x_sensor_{nullptr}; | ||
sensor::Sensor *y_sensor_{nullptr}; | ||
sensor::Sensor *z_sensor_{nullptr}; | ||
}; | ||
|
||
} // namespace mmc5983 | ||
} // namespace esphome |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import esphome.codegen as cg | ||
import esphome.config_validation as cv | ||
from esphome.components import i2c, sensor | ||
from esphome.const import ( | ||
CONF_FIELD_STRENGTH_X, | ||
CONF_FIELD_STRENGTH_Y, | ||
CONF_FIELD_STRENGTH_Z, | ||
CONF_ID, | ||
ICON_MAGNET, | ||
STATE_CLASS_MEASUREMENT, | ||
UNIT_MICROTESLA, | ||
) | ||
|
||
DEPENDENCIES = ["i2c"] | ||
|
||
mmc5983_ns = cg.esphome_ns.namespace("mmc5983") | ||
MMC5983Component = mmc5983_ns.class_( | ||
"MMC5983Component", cg.PollingComponent, i2c.I2CDevice | ||
) | ||
|
||
field_strength_schema = sensor.sensor_schema( | ||
unit_of_measurement=UNIT_MICROTESLA, | ||
icon=ICON_MAGNET, | ||
accuracy_decimals=4, | ||
state_class=STATE_CLASS_MEASUREMENT, | ||
) | ||
|
||
CONFIG_SCHEMA = ( | ||
cv.Schema( | ||
{ | ||
cv.GenerateID(): cv.declare_id(MMC5983Component), | ||
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, | ||
} | ||
) | ||
.extend(cv.polling_component_schema("60s")) | ||
.extend(i2c.i2c_device_schema(0x30)) | ||
) | ||
|
||
|
||
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) | ||
|
||
if x_config := config.get(CONF_FIELD_STRENGTH_X): | ||
sens = await sensor.new_sensor(x_config) | ||
cg.add(var.set_x_sensor(sens)) | ||
if y_config := config.get(CONF_FIELD_STRENGTH_Y): | ||
sens = await sensor.new_sensor(y_config) | ||
cg.add(var.set_y_sensor(sens)) | ||
if z_config := config.get(CONF_FIELD_STRENGTH_Z): | ||
sens = await sensor.new_sensor(z_config) | ||
cg.add(var.set_z_sensor(sens)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters