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