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