ESPHome 2025.5.0
Loading...
Searching...
No Matches
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
7namespace esphome {
8namespace mmc5983 {
9
10static const char *const TAG = "mmc5983";
11
12namespace {
13constexpr uint8_t IC0_ADDR = 0x09;
14constexpr uint8_t IC1_ADDR = 0x0a;
15constexpr uint8_t IC2_ADDR = 0x0b;
16constexpr uint8_t IC3_ADDR = 0x0c;
17constexpr uint8_t PRODUCT_ID_ADDR = 0x2f;
18
19float 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
virtual void mark_failed()
Mark this component as failed.
void status_set_warning(const char *message="unspecified")
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 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
float get_setup_priority() const override
Definition mmc5983.cpp:138
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:39
ErrorCode
Error codes returned by I2CBus and I2CDevice methods.
Definition i2c_bus.h:11
@ ERROR_OK
No error found during execution of method.
Definition i2c_bus.h:13
const float DATA
For components that import data from directly connected sensors like DHT.
Definition component.cpp:19
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
T id(T value)
Helper function to make id(var) known from lambdas work in custom components.
Definition helpers.h:798