ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
sensor_mlx90393.cpp
Go to the documentation of this file.
1#ifndef USE_BK72XX
2
3#include "sensor_mlx90393.h"
4#include "esphome/core/log.h"
5
7
8ESPHOME_LOG_TAG(TAG, "mlx90393");
9
10static constexpr uint32_t VERIFY_SETTINGS_TIMEOUT_ID = 0;
11
12const LogString *settings_to_string(MLX90393Setting setting) {
13 switch (setting) {
15 return LOG_STR("gain");
17 return LOG_STR("resolution");
19 return LOG_STR("oversampling");
21 return LOG_STR("digital filtering");
23 return LOG_STR("temperature oversampling");
25 return LOG_STR("temperature compensation");
27 return LOG_STR("hallconf");
28 case MLX90393_LAST:
29 return LOG_STR("error");
30 default:
31 return LOG_STR("unknown");
32 }
33};
34
35bool MLX90393Cls::transceive(const uint8_t *request, size_t request_size, uint8_t *response, size_t response_size) {
36 i2c::ErrorCode e = this->write(request, request_size);
37 if (e != i2c::ErrorCode::ERROR_OK) {
38 ESP_LOGV(TAG, "i2c failed to write %u", e);
39 return false;
40 }
41 e = this->read(response, response_size);
42 if (e != i2c::ErrorCode::ERROR_OK) {
43 ESP_LOGV(TAG, "i2c failed to read %u", e);
44 return false;
45 }
46 return true;
47}
48
49bool MLX90393Cls::has_drdy_pin() { return this->drdy_pin_ != nullptr; }
50
52 if (this->drdy_pin_ == nullptr) {
53 return false;
54 } else {
55 return this->drdy_pin_->digital_read();
56 }
57}
60
62 uint8_t ret = -1;
63 switch (which) {
65 ret = this->mlx_.setGainSel(this->gain_);
66 break;
68 ret = this->mlx_.setResolution(this->resolutions_[0], this->resolutions_[1], this->resolutions_[2]);
69 break;
71 ret = this->mlx_.setOverSampling(this->oversampling_);
72 break;
74 ret = this->mlx_.setDigitalFiltering(this->filter_);
75 break;
77 ret = this->mlx_.setTemperatureOverSampling(this->temperature_oversampling_);
78 break;
80 ret = this->mlx_.setTemperatureCompensation(this->temperature_compensation_);
81 break;
83 ret = this->mlx_.setHallConf(this->hallconf_);
84 break;
85 default:
86 break;
87 }
88 if (ret != MLX90393::STATUS_OK) {
89 ESP_LOGE(TAG, "failed to apply %s", LOG_STR_ARG(settings_to_string(which)));
90 }
91 return ret;
92}
93
95 // perform dummy read after reset
96 // first one always gets NAK even tough everything is fine
97 uint8_t ignore = 0;
98 this->mlx_.getGainSel(ignore);
99
100 uint8_t result = MLX90393::STATUS_OK;
101 for (int i = MLX90393_GAIN_SEL; i != MLX90393_LAST; i++) {
102 MLX90393Setting stage = static_cast<MLX90393Setting>(i);
103 result |= this->apply_setting_(stage);
104 }
105 return result == MLX90393::STATUS_OK;
106}
107
109 // note the two arguments A0 and A1 which are used to construct an i2c address
110 // we can hard-code these because we never actually use the constructed address
111 // see the transceive function above, which uses the address from I2CComponent
112 this->mlx_.begin_with_hal(this, 0, 0);
113
114 if (!this->apply_all_settings_()) {
115 this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
116 }
117
118 // start verify settings process
119 this->set_timeout(VERIFY_SETTINGS_TIMEOUT_ID, 3000, [this]() { this->verify_settings_timeout_(MLX90393_GAIN_SEL); });
120}
121
123 ESP_LOGCONFIG(TAG, "MLX90393:");
124 LOG_I2C_DEVICE(this);
125
126 if (this->is_failed())
127 return;
128 LOG_UPDATE_INTERVAL(this);
129
130 LOG_SENSOR(" ", "X Axis", this->x_sensor_);
131 LOG_SENSOR(" ", "Y Axis", this->y_sensor_);
132 LOG_SENSOR(" ", "Z Axis", this->z_sensor_);
133 LOG_SENSOR(" ", "Temperature", this->t_sensor_);
134}
135
137 MLX90393::txyz data;
138
139 if (this->mlx_.readData(data) == MLX90393::STATUS_OK) {
140 ESP_LOGD(TAG, "received %f %f %f", data.x, data.y, data.z);
141 if (this->x_sensor_ != nullptr) {
142 this->x_sensor_->publish_state(data.x);
143 }
144 if (this->y_sensor_ != nullptr) {
145 this->y_sensor_->publish_state(data.y);
146 }
147 if (this->z_sensor_ != nullptr) {
148 this->z_sensor_->publish_state(data.z);
149 }
150 if (this->t_sensor_ != nullptr) {
151 this->t_sensor_->publish_state(data.t);
152 }
153 this->status_clear_warning();
154 } else {
155 ESP_LOGE(TAG, "failed to read data");
156 this->status_set_warning();
157 }
158}
159
161 uint8_t read_value = 0xFF;
162 uint8_t expected_value = 0xFF;
163 uint8_t read_status = -1;
164 char read_back_str[33] = {0};
165
166 switch (which) {
167 case MLX90393_GAIN_SEL: {
168 read_status = this->mlx_.getGainSel(read_value);
169 expected_value = this->gain_;
170 break;
171 }
172
173 case MLX90393_RESOLUTION: {
174 uint8_t read_resolutions[3] = {0xFF};
175 read_status = this->mlx_.getResolution(read_resolutions[0], read_resolutions[1], read_resolutions[2]);
176 snprintf(read_back_str, sizeof(read_back_str), "%u %u %u expected %u %u %u", read_resolutions[0],
177 read_resolutions[1], read_resolutions[2], this->resolutions_[0], this->resolutions_[1],
178 this->resolutions_[2]);
179 bool is_correct = true;
180 for (int i = 0; i < 3; i++) {
181 is_correct &= read_resolutions[i] == this->resolutions_[i];
182 }
183 if (is_correct) {
184 // set read_value and expected_value to same number, so the code blow recognizes it is correct
185 read_value = 0;
186 expected_value = 0;
187 } else {
188 // set to different numbers, to show incorrect
189 read_value = 1;
190 expected_value = 0;
191 }
192 break;
193 }
195 read_status = this->mlx_.getOverSampling(read_value);
196 expected_value = this->oversampling_;
197 break;
198 }
200 read_status = this->mlx_.getDigitalFiltering(read_value);
201 expected_value = this->filter_;
202 break;
203 }
205 read_status = this->mlx_.getTemperatureOverSampling(read_value);
206 expected_value = this->temperature_oversampling_;
207 break;
208 }
210 read_status = this->mlx_.getTemperatureCompensation(read_value);
211 expected_value = (bool) this->temperature_compensation_;
212 break;
213 }
214 case MLX90393_HALLCONF: {
215 read_status = this->mlx_.getHallConf(read_value);
216 expected_value = this->hallconf_;
217 break;
218 }
219 default: {
220 return false;
221 }
222 }
223 if (read_status != MLX90393::STATUS_OK) {
224 ESP_LOGE(TAG, "verify error: failed to read %s", LOG_STR_ARG(settings_to_string(which)));
225 return false;
226 }
227 if (read_back_str[0] == 0x0) {
228 snprintf(read_back_str, sizeof(read_back_str), "%u expected %u", read_value, expected_value);
229 }
230 bool is_correct = read_value == expected_value;
231 if (!is_correct) {
232 ESP_LOGW(TAG, "verify failed: read back wrong %s: got %s", LOG_STR_ARG(settings_to_string(which)), read_back_str);
233 return false;
234 }
235 ESP_LOGD(TAG, "verify succeeded for %s. got %s", LOG_STR_ARG(settings_to_string(which)), read_back_str);
236 return true;
237}
238
247 bool is_setting_ok = this->verify_setting_(stage);
248
249 if (!is_setting_ok) {
250 if (this->mlx_.checkStatus(this->mlx_.reset()) != MLX90393::STATUS_OK) {
251 this->mark_failed(LOG_STR("Failed to reset device"));
252 return;
253 }
254
255 if (!this->apply_all_settings_()) {
256 this->mark_failed(LOG_STR("Failed to re-apply settings"));
257 } else {
258 ESP_LOGI(TAG, "reset and re-apply settings completed");
259 }
260 }
261
262 MLX90393Setting next_stage = static_cast<MLX90393Setting>(static_cast<int>(stage) + 1);
263 if (next_stage == MLX90393_LAST) {
264 next_stage = static_cast<MLX90393Setting>(0);
265 }
266
267 this->set_timeout(VERIFY_SETTINGS_TIMEOUT_ID, 3000,
268 [this, next_stage]() { this->verify_settings_timeout_(next_stage); });
269}
270
271} // namespace esphome::mlx90393
272
273#endif // USE_BK72XX
void mark_failed()
Mark this component as failed.
bool is_failed() const
Definition component.h:274
void set_timeout(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a const char* name.
Definition component.cpp:96
void status_clear_warning()
Definition component.h:296
virtual bool digital_read()=0
ErrorCode write(const uint8_t *data, size_t len) const
writes an array of bytes to a device using an I2CBus
Definition i2c.h:195
ErrorCode read(uint8_t *data, size_t len) const
reads an array of bytes from the device using an I2CBus
Definition i2c.h:175
void sleep_micros(uint32_t micros) override
void sleep_millis(uint32_t millis) override
void verify_settings_timeout_(MLX90393Setting stage)
Regularly checks that our settings are still applied.
bool verify_setting_(MLX90393Setting which)
bool transceive(const uint8_t *request, size_t request_size, uint8_t *response, size_t response_size) override
uint8_t apply_setting_(MLX90393Setting which)
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:65
int ret
ErrorCode
Error codes returned by I2CBus and I2CDevice methods.
Definition i2c_bus.h:13
@ ERROR_OK
No error found during execution of method.
Definition i2c_bus.h:15
ESPHOME_LOG_TAG(TAG, "mlx90393")
const LogString * settings_to_string(MLX90393Setting setting)
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition hal.cpp:48
uint32_t IRAM_ATTR HOT micros()
Definition hal.cpp:43
void HOT delay(uint32_t ms)
Definition hal.cpp:85
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:25
static void uint32_t