ESPHome 2026.8.0-dev
Loading...
Searching...
No Matches
qmi8658.cpp
Go to the documentation of this file.
1#include "qmi8658.h"
2#include "esphome/core/log.h"
3#include "esphome/core/hal.h"
4
5namespace esphome::qmi8658 {
6
7static const char *const TAG = "qmi8658";
8
9// Acceleration scale (g per LSB), indexed by accel_range_ >> 4.
10// Full-scale = range_g, mapped over a signed 16-bit value (2^15 counts).
11static constexpr float ACCEL_SCALE[] = {
12 2.0f / 32768.0f,
13 4.0f / 32768.0f,
14 8.0f / 32768.0f,
15 16.0f / 32768.0f,
16};
17
18// Angular rate scale (°/s per LSB), indexed by gyro_range_ >> 4.
19static constexpr float GYRO_SCALE[] = {
20 16.0f / 32768.0f, 32.0f / 32768.0f, 64.0f / 32768.0f, 128.0f / 32768.0f,
21 256.0f / 32768.0f, 512.0f / 32768.0f, 1024.0f / 32768.0f, 2048.0f / 32768.0f,
22};
23
25 MotionComponent::setup();
26
27 // 1. Verify chip ID
28 uint8_t who_am_i = 0;
29 if (!this->read_byte(QMI8658_REG_WHO_AM_I, &who_am_i)) {
30 ESP_LOGE(TAG, "Failed to read chip ID - check wiring / address");
31 this->mark_failed();
32 return;
33 }
34 if (who_am_i != QMI8658_WHO_AM_I_VALUE) {
35 ESP_LOGE(TAG, "Wrong chip ID: 0x%02X (expected 0x%02X)", who_am_i, QMI8658_WHO_AM_I_VALUE);
36 this->mark_failed();
37 return;
38 }
39
40 // 2. Soft reset
41 if (!this->write_byte(QMI8658_REG_RESET, QMI8658_RESET_CMD)) {
42 this->mark_failed();
43 return;
44 }
45 delay(15); // spec: wait for reset to complete
46
47 // 3. Serial interface: enable register address auto-increment
48 if (!this->write_byte(QMI8658_REG_CTRL1, QMI8658_CTRL1_VALUE)) {
49 this->mark_failed(LOG_STR("Failed to write REG_CTRL1"));
50 return;
51 }
52
53 // 4. Configure accelerometer (CTRL2 = range | ODR)
54 if (!this->write_byte(QMI8658_REG_CTRL2, (uint8_t) (this->accel_range_) | (uint8_t) (this->accel_odr_))) {
55 this->mark_failed(LOG_STR("Failed to write REG_CTRL2"));
56 return;
57 }
58
59 // 5. Configure gyroscope (CTRL3 = range | ODR)
60 if (!this->write_byte(QMI8658_REG_CTRL3, (uint8_t) (this->gyro_range_) | (uint8_t) (this->gyro_odr_))) {
61 this->mark_failed(LOG_STR("Failed to write REG_CTRL3"));
62 return;
63 }
64
65 // 6. Disable the built-in low-pass filters (leave raw data to the motion pipeline)
66 if (!this->write_byte(QMI8658_REG_CTRL5, 0x00)) {
67 this->mark_failed(LOG_STR("Failed to write REG_CTRL5"));
68 this->mark_failed();
69 return;
70 }
71
72 // 7. Enable accelerometer and gyroscope
73 if (!this->write_byte(QMI8658_REG_CTRL7, QMI8658_CTRL7_ACC_EN | QMI8658_CTRL7_GYR_EN)) {
74 this->mark_failed(LOG_STR("Failed to write REG_CTRL7"));
75 return;
76 }
77
78 ESP_LOGCONFIG(TAG, "QMI8658 initialised successfully");
79}
80
82 ESP_LOGCONFIG(TAG, "QMI8658 IMU:");
83 LOG_I2C_DEVICE(this);
84 if (this->is_failed()) {
85 ESP_LOGE(TAG, " Communication failed!");
86 return;
87 }
88
89 static constexpr const char *const ACCEL_RANGE_STRS[] = {"±2g", "±4g", "±8g", "±16g"};
90 static constexpr const char *const GYRO_RANGE_STRS[] = {"±16°/s", "±32°/s", "±64°/s", "±128°/s",
91 "±256°/s", "±512°/s", "±1024°/s", "±2048°/s"};
92
93 ESP_LOGCONFIG(TAG, " Accel range : %s", ACCEL_RANGE_STRS[this->accel_range_ >> 4]);
94 ESP_LOGCONFIG(TAG, " Gyro range : %s", GYRO_RANGE_STRS[this->gyro_range_ >> 4]);
95 MotionComponent::dump_config();
96}
97
99 if (this->is_failed())
100 return false;
101
102 // Read temperature + accel + gyro in one contiguous block starting at TEMP_L.
103 uint8_t raw_data[REG_READ_LEN];
104 if (!this->read_bytes(QMI8658_REG_TEMP_L, raw_data, REG_READ_LEN)) {
105 ESP_LOGW(TAG, "Failed to read IMU data");
106 return false;
107 }
108
109 // Data is little-endian (low byte first).
110 float scale = ACCEL_SCALE[this->accel_range_ >> 4];
111 int16_t raw_x = encode_uint16(raw_data[ACC_OFFS + 1], raw_data[ACC_OFFS + 0]);
112 int16_t raw_y = encode_uint16(raw_data[ACC_OFFS + 3], raw_data[ACC_OFFS + 2]);
113 int16_t raw_z = encode_uint16(raw_data[ACC_OFFS + 5], raw_data[ACC_OFFS + 4]);
114 ESP_LOGV(TAG, "Read raw accel data: %d, %d, %d", raw_x, raw_y, raw_z);
115 data.acceleration[motion::X_AXIS] = raw_x * scale;
116 data.acceleration[motion::Y_AXIS] = raw_y * scale;
117 data.acceleration[motion::Z_AXIS] = raw_z * scale;
118
119 scale = GYRO_SCALE[this->gyro_range_ >> 4];
120 raw_x = encode_uint16(raw_data[GYR_OFFS + 1], raw_data[GYR_OFFS + 0]);
121 raw_y = encode_uint16(raw_data[GYR_OFFS + 3], raw_data[GYR_OFFS + 2]);
122 raw_z = encode_uint16(raw_data[GYR_OFFS + 5], raw_data[GYR_OFFS + 4]);
123 ESP_LOGV(TAG, "Read raw gyro data: %d, %d, %d", raw_x, raw_y, raw_z);
124 data.angular_rate[motion::X_AXIS] = raw_x * scale;
125 data.angular_rate[motion::Y_AXIS] = raw_y * scale;
126 data.angular_rate[motion::Z_AXIS] = raw_z * scale;
127
128 if (this->temperature_callback_.empty())
129 return true;
130 // Temperature: signed 16-bit, °C = raw / 256
131 int16_t raw_t = (int16_t) ((raw_data[TEMP_OFFS + 1] << 8) | raw_data[TEMP_OFFS + 0]);
132 this->temperature_callback_.call(raw_t / 256.0f);
133 return true;
134}
135
136} // namespace esphome::qmi8658
void mark_failed()
Mark this component as failed.
bool is_failed() const
Definition component.h:272
bool write_byte(uint8_t a_register, uint8_t data) const
Definition i2c.h:265
bool read_byte(uint8_t a_register, uint8_t *data)
Definition i2c.h:240
bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len)
Compat APIs All methods below have been added for compatibility reasons.
Definition i2c.h:217
QMI8658AccelRange accel_range_
Definition qmi8658.h:104
bool update_data(motion::MotionData &data) override
Definition qmi8658.cpp:98
LazyCallbackManager< void(float)> temperature_callback_
Definition qmi8658.h:109
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
Definition helpers.h:873
void HOT delay(uint32_t ms)
Definition hal.cpp:85