ESPHome  2024.12.2
mmc5983.cpp
Go to the documentation of this file.
1 // See https://github.com/sparkfun/SparkFun_MMC5983MA_Magnetometer_Arduino_Library/tree/main
2 // for datasheets and an Arduino implementation.
3 
4 #include "mmc5983.h"
5 #include "esphome/core/log.h"
6 
7 namespace esphome {
8 namespace mmc5983 {
9 
10 static const char *const TAG = "mmc5983";
11 
12 namespace {
13 constexpr uint8_t IC0_ADDR = 0x09;
14 constexpr uint8_t IC1_ADDR = 0x0a;
15 constexpr uint8_t IC2_ADDR = 0x0b;
16 constexpr uint8_t IC3_ADDR = 0x0c;
17 constexpr uint8_t PRODUCT_ID_ADDR = 0x2f;
18 
19 float convert_data_to_millitesla(uint8_t data_17_10, uint8_t data_9_2, uint8_t data_1_0) {
20  int32_t counts = (data_17_10 << 10) | (data_9_2 << 2) | data_1_0;
21  counts -= 131072; // "Null Field Output" from datasheet.
22 
23  // Sensitivity is 16384 counts/gauss, which is 163840 counts/mT.
24  return counts / 163840.0f;
25 }
26 } // namespace
27 
29  // Schedule a SET/RESET. This will recalibrate the sensor.
30  // We are supposed to be able to set this once, and have it automatically continue every reading, but
31  // this does not appear to work in continuous mode, even with En_prd_set turned on in Internal Control 2.
32  // Bit 5 = Auto_SR_en (automatic SET/RESET enable).
33  const uint8_t ic0_value = 0b10000;
34  i2c::ErrorCode err = this->write_register(IC0_ADDR, &ic0_value, 1);
35  if (err != i2c::ErrorCode::ERROR_OK) {
36  ESP_LOGW(TAG, "Writing Internal Control 0 failed with i2c error %d", err);
37  this->status_set_warning();
38  }
39 
40  // Read out the data, 7 bytes starting from 0x00.
41  uint8_t data[7];
42  err = this->read_register(0x00, data, sizeof(data));
43  if (err != i2c::ErrorCode::ERROR_OK) {
44  ESP_LOGW(TAG, "Reading data failed with i2c error %d", err);
45  this->status_set_warning();
46  return;
47  }
48 
49  // Unpack the data and publish to sensors.
50  // Data is in this format:
51  // data[0]: Xout[17:10]
52  // data[1]: Xout[9:2]
53  // data[2]: Yout[17:10]
54  // data[3]: Yout[9:2]
55  // data[4]: Zout[17:10]
56  // data[5]: Zout[9:2]
57  // data[6]: { Xout[1], Xout[0], Yout[1], Yout[0], Zout[1], Zout[0], 0, 0 }
58  if (this->x_sensor_) {
59  this->x_sensor_->publish_state(convert_data_to_millitesla(data[0], data[1], (data[6] & 0b11000000) >> 6));
60  }
61  if (this->y_sensor_) {
62  this->y_sensor_->publish_state(convert_data_to_millitesla(data[2], data[3], (data[6] & 0b00110000) >> 4));
63  }
64  if (this->z_sensor_) {
65  this->z_sensor_->publish_state(convert_data_to_millitesla(data[4], data[5], (data[6] & 0b00001100) >> 2));
66  }
67 }
68 
70  ESP_LOGCONFIG(TAG, "Setting up MMC5983...");
71 
72  // Verify product id.
73  const uint8_t mmc5983_product_id = 0x30;
74  uint8_t id;
75  i2c::ErrorCode err = this->read_register(PRODUCT_ID_ADDR, &id, 1);
76  if (err != i2c::ErrorCode::ERROR_OK) {
77  ESP_LOGE(TAG, "Reading product id failed with i2c error %d", err);
78  this->mark_failed();
79  return;
80  }
81  if (id != mmc5983_product_id) {
82  ESP_LOGE(TAG, "Product id 0x%02x does not match expected value 0x%02x", id, mmc5983_product_id);
83  this->mark_failed();
84  return;
85  }
86 
87  // Initialize Internal Control registers to 0.
88  // Internal Control 0.
89  const uint8_t zero = 0;
90  err = this->write_register(IC0_ADDR, &zero, 1);
91  if (err != i2c::ErrorCode::ERROR_OK) {
92  ESP_LOGE(TAG, "Initializing Internal Control 0 failed with i2c error %d", err);
93  this->mark_failed();
94  return;
95  }
96  // Internal Control 1.
97  err = this->write_register(IC1_ADDR, &zero, 1);
98  if (err != i2c::ErrorCode::ERROR_OK) {
99  ESP_LOGE(TAG, "Initializing Internal Control 1 failed with i2c error %d", err);
100  this->mark_failed();
101  return;
102  }
103  // Internal Control 2.
104  err = this->write_register(IC2_ADDR, &zero, 1);
105  if (err != i2c::ErrorCode::ERROR_OK) {
106  ESP_LOGE(TAG, "Initializing Internal Control 2 failed with i2c error %d", err);
107  this->mark_failed();
108  return;
109  }
110  // Internal Control 3.
111  err = this->write_register(IC3_ADDR, &zero, 1);
112  if (err != i2c::ErrorCode::ERROR_OK) {
113  ESP_LOGE(TAG, "Initializing Internal Control 3 failed with i2c error %d", err);
114  this->mark_failed();
115  return;
116  }
117 
118  // Enable continuous mode at 100 Hz, using Internal Control 2.
119  // Bit 3 = Cmm_en (continuous mode enable).
120  // Bit [2:0] = Cm_freq. 0b101 = 100 Hz, the fastest reading speed at Bandwidth=100 Hz.
121  const uint8_t ic2_value = 0b00001101;
122  err = this->write_register(IC2_ADDR, &ic2_value, 1);
123  if (err != i2c::ErrorCode::ERROR_OK) {
124  ESP_LOGE(TAG, "Writing Internal Control 2 failed with i2c error %d", err);
125  this->mark_failed();
126  return;
127  }
128 }
129 
131  ESP_LOGD(TAG, "MMC5983:");
132  LOG_I2C_DEVICE(this);
133  LOG_SENSOR(" ", "X", this->x_sensor_);
134  LOG_SENSOR(" ", "Y", this->y_sensor_);
135  LOG_SENSOR(" ", "Z", this->z_sensor_);
136 }
137 
139 
140 } // namespace mmc5983
141 } // namespace esphome
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:19
sensor::Sensor * y_sensor_
Definition: mmc5983.h:23
ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len, bool stop=true)
reads an array of bytes from a specific register in the I²C device
Definition: i2c.cpp:10
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
T id(T value)
Helper function to make id(var) known from lambdas work in custom components.
Definition: helpers.h:728
sensor::Sensor * x_sensor_
Definition: mmc5983.h:22
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
sensor::Sensor * z_sensor_
Definition: mmc5983.h:24
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:118
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len, bool stop=true)
writes an array of bytes to a specific register in the I²C device
Definition: i2c.cpp:25
ErrorCode
Error codes returned by I2CBus and I2CDevice methods.
Definition: i2c_bus.h:11
float get_setup_priority() const override
Definition: mmc5983.cpp:138