ESPHome  2024.4.0
qmc5883l.cpp
Go to the documentation of this file.
1 #include "qmc5883l.h"
2 #include "esphome/core/log.h"
3 #include "esphome/core/hal.h"
4 #include <cmath>
5 
6 namespace esphome {
7 namespace qmc5883l {
8 
9 static const char *const TAG = "qmc5883l";
10 static const uint8_t QMC5883L_ADDRESS = 0x0D;
11 
12 static const uint8_t QMC5883L_REGISTER_DATA_X_LSB = 0x00;
13 static const uint8_t QMC5883L_REGISTER_DATA_X_MSB = 0x01;
14 static const uint8_t QMC5883L_REGISTER_DATA_Y_LSB = 0x02;
15 static const uint8_t QMC5883L_REGISTER_DATA_Y_MSB = 0x03;
16 static const uint8_t QMC5883L_REGISTER_DATA_Z_LSB = 0x04;
17 static const uint8_t QMC5883L_REGISTER_DATA_Z_MSB = 0x05;
18 static const uint8_t QMC5883L_REGISTER_STATUS = 0x06;
19 static const uint8_t QMC5883L_REGISTER_TEMPERATURE_LSB = 0x07;
20 static const uint8_t QMC5883L_REGISTER_TEMPERATURE_MSB = 0x08;
21 static const uint8_t QMC5883L_REGISTER_CONTROL_1 = 0x09;
22 static const uint8_t QMC5883L_REGISTER_CONTROL_2 = 0x0A;
23 static const uint8_t QMC5883L_REGISTER_PERIOD = 0x0B;
24 
26  ESP_LOGCONFIG(TAG, "Setting up QMC5883L...");
27  // Soft Reset
28  if (!this->write_byte(QMC5883L_REGISTER_CONTROL_2, 1 << 7)) {
30  this->mark_failed();
31  return;
32  }
33  delay(10);
34 
35  uint8_t control_1 = 0;
36  control_1 |= 0b01 << 0; // MODE (Mode) -> 0b00=standby, 0b01=continuous
37  control_1 |= this->datarate_ << 2;
38  control_1 |= this->range_ << 4;
39  control_1 |= this->oversampling_ << 6;
40  if (!this->write_byte(QMC5883L_REGISTER_CONTROL_1, control_1)) {
42  this->mark_failed();
43  return;
44  }
45 
46  uint8_t control_2 = 0;
47  control_2 |= 0b0 << 7; // SOFT_RST (Soft Reset) -> 0b00=disabled, 0b01=enabled
48  control_2 |= 0b0 << 6; // ROL_PNT (Pointer Roll Over) -> 0b00=disabled, 0b01=enabled
49  control_2 |= 0b0 << 0; // INT_ENB (Interrupt) -> 0b00=disabled, 0b01=enabled
50  if (!this->write_byte(QMC5883L_REGISTER_CONTROL_2, control_2)) {
52  this->mark_failed();
53  return;
54  }
55 
56  uint8_t period = 0x01; // recommended value
57  if (!this->write_byte(QMC5883L_REGISTER_PERIOD, period)) {
59  this->mark_failed();
60  return;
61  }
62 }
64  ESP_LOGCONFIG(TAG, "QMC5883L:");
65  LOG_I2C_DEVICE(this);
66  if (this->error_code_ == COMMUNICATION_FAILED) {
67  ESP_LOGE(TAG, "Communication with QMC5883L failed!");
68  }
69  LOG_UPDATE_INTERVAL(this);
70 
71  LOG_SENSOR(" ", "X Axis", this->x_sensor_);
72  LOG_SENSOR(" ", "Y Axis", this->y_sensor_);
73  LOG_SENSOR(" ", "Z Axis", this->z_sensor_);
74  LOG_SENSOR(" ", "Heading", this->heading_sensor_);
75  LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
76 }
79  uint8_t status = false;
80  if (ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG)
81  this->read_byte(QMC5883L_REGISTER_STATUS, &status);
82 
83  float mg_per_bit;
84  switch (this->range_) {
86  mg_per_bit = 0.0833f;
87  break;
89  mg_per_bit = 0.333f;
90  break;
91  default:
92  mg_per_bit = NAN;
93  }
94 
95  // in µT
96  float x = NAN, y = NAN, z = NAN;
97  if (this->x_sensor_ != nullptr || this->heading_sensor_ != nullptr) {
98  uint16_t raw_x;
99  if (!this->read_byte_16_(QMC5883L_REGISTER_DATA_X_LSB, &raw_x)) {
100  this->status_set_warning();
101  return;
102  }
103  x = int16_t(raw_x) * mg_per_bit * 0.1f;
104  }
105  if (this->y_sensor_ != nullptr || this->heading_sensor_ != nullptr) {
106  uint16_t raw_y;
107  if (!this->read_byte_16_(QMC5883L_REGISTER_DATA_Y_LSB, &raw_y)) {
108  this->status_set_warning();
109  return;
110  }
111  y = int16_t(raw_y) * mg_per_bit * 0.1f;
112  }
113  if (this->z_sensor_ != nullptr) {
114  uint16_t raw_z;
115  if (!this->read_byte_16_(QMC5883L_REGISTER_DATA_Z_LSB, &raw_z)) {
116  this->status_set_warning();
117  return;
118  }
119  z = int16_t(raw_z) * mg_per_bit * 0.1f;
120  }
121 
122  float heading = NAN;
123  if (this->heading_sensor_ != nullptr) {
124  heading = atan2f(0.0f - x, y) * 180.0f / M_PI;
125  }
126 
127  float temp = NAN;
128  if (this->temperature_sensor_ != nullptr) {
129  uint16_t raw_temp;
130  if (!this->read_byte_16_(QMC5883L_REGISTER_TEMPERATURE_LSB, &raw_temp)) {
131  this->status_set_warning();
132  return;
133  }
134  temp = int16_t(raw_temp) * 0.01f;
135  }
136 
137  ESP_LOGD(TAG, "Got x=%0.02fµT y=%0.02fµT z=%0.02fµT heading=%0.01f° temperature=%0.01f°C status=%u", x, y, z, heading,
138  temp, status);
139 
140  if (this->x_sensor_ != nullptr)
141  this->x_sensor_->publish_state(x);
142  if (this->y_sensor_ != nullptr)
143  this->y_sensor_->publish_state(y);
144  if (this->z_sensor_ != nullptr)
145  this->z_sensor_->publish_state(z);
146  if (this->heading_sensor_ != nullptr)
147  this->heading_sensor_->publish_state(heading);
148  if (this->temperature_sensor_ != nullptr)
149  this->temperature_sensor_->publish_state(temp);
150 }
151 
152 bool QMC5883LComponent::read_byte_16_(uint8_t a_register, uint16_t *data) {
153  if (!this->read_byte_16(a_register, data))
154  return false;
155  *data = (*data & 0x00FF) << 8 | (*data & 0xFF00) >> 8; // Flip Byte order, LSB first;
156  return true;
157 }
158 
159 } // namespace qmc5883l
160 } // namespace esphome
bool read_byte(uint8_t a_register, uint8_t *data, bool stop=true)
Definition: i2c.h:235
bool read_byte_16(uint8_t a_register, uint16_t *data)
Definition: i2c.h:246
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:19
sensor::Sensor * heading_sensor_
Definition: qmc5883l.h:52
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
uint16_t x
Definition: tt21100.cpp:17
uint16_t y
Definition: tt21100.cpp:18
enum esphome::qmc5883l::QMC5883LComponent::ErrorCode error_code_
float get_setup_priority() const override
Definition: qmc5883l.cpp:77
bool read_byte_16_(uint8_t a_register, uint16_t *data)
Definition: qmc5883l.cpp:152
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
sensor::Sensor * temperature_sensor_
Definition: qmc5883l.h:53
u_int8_t raw_temp
uint8_t status
Definition: bl0942.h:23
bool write_byte(uint8_t a_register, uint8_t data, bool stop=true)
Definition: i2c.h:262
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:118
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
QMC5883LOversampling oversampling_
Definition: qmc5883l.h:48
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26