ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
icm20689.cpp
Go to the documentation of this file.
1#include "icm20689.h"
2#include "esphome/core/hal.h"
3#include "esphome/core/log.h"
4
5#include <cstddef>
6#include <cstdint>
7
9
10ESPHOME_LOG_TAG(TAG, "icm20689");
11
12namespace {
13
14// Range enum values 0..3 map to full-scale settings of 2x/4x/8x/16x the base range.
15constexpr float accel_scale(uint8_t range) { return (2.0f * (1U << range)) / 32768.0f; }
16
17constexpr float gyro_scale(uint8_t range) { return (250.0f * (1U << range)) / 32768.0f; }
18
19} // namespace
20
21void ICM20689Component::write_register_(uint8_t reg, uint8_t value) {
22 this->enable();
23 this->write_byte(reg & 0x7F);
24 this->write_byte(value);
25 this->disable();
26}
27
28void ICM20689Component::read_registers_(uint8_t reg, uint8_t *data, size_t len) {
29 this->enable();
30 this->write_byte(reg | ICM20689_READ_BIT);
31 this->read_array(data, len);
32 this->disable();
33}
34
36 MotionComponent::setup();
37
38 this->spi_setup();
39
40 if (!this->spi_is_ready()) {
41 this->mark_failed(LOG_STR("SPI device is not ready"));
42 return;
43 }
44
45 // Datasheet 4.20: a soft reset must be the first operation after power-up,
46 // before any register initialization. WHO_AM_I is only valid afterwards.
47 this->write_register_(ICM20689_REG_PWR_MGMT_1, ICM20689_SOFT_RESET);
48 delay(100); // NOLINT
49
50 // The chip comes out of reset in sleep mode: wake it and select the
51 // auto-selected best clock source (required for full gyro performance).
52 this->write_register_(ICM20689_REG_PWR_MGMT_1, ICM20689_CLKSEL_AUTO);
53
54 // USER_CTRL is not writable in sleep mode, so I2C_IF_DIS is set once awake.
55 // Datasheet 6.1: disable the I2C interface when using SPI.
56 this->write_register_(ICM20689_REG_USER_CTRL, ICM20689_I2C_IF_DIS);
57
58 uint8_t who_am_i = 0;
59 this->read_registers_(ICM20689_REG_WHO_AM_I, &who_am_i, 1);
60
61 if (who_am_i != ICM20689_WHO_AM_I_VALUE) {
62 ESP_LOGE(TAG, "Unexpected WHO_AM_I 0x%02X (expected 0x%02X) -- check wiring/CS", who_am_i, ICM20689_WHO_AM_I_VALUE);
63 this->mark_failed(LOG_STR("Wrong chip ID"));
64 return;
65 }
66
67 // Allow the sensor to settle after waking from sleep.
68 delay(50); // NOLINT
69
70 // Configure the accelerometer and gyroscope full-scale ranges.
71 this->write_register_(ICM20689_REG_ACCEL_CONFIG, static_cast<uint8_t>(this->accel_range_) << 3);
72 this->write_register_(ICM20689_REG_GYRO_CONFIG, static_cast<uint8_t>(this->gyro_range_) << 3);
73}
74
76 ESP_LOGCONFIG(TAG, "ICM20689 IMU:");
77 LOG_PIN(" CS Pin: ", this->cs_);
78
79 if (this->is_failed())
80 return;
81
82 ESP_LOGCONFIG(TAG, " Accel range: ±%dg", 2 << static_cast<uint8_t>(this->accel_range_));
83 ESP_LOGCONFIG(TAG, " Gyro range: ±%d°/s", 250 << static_cast<uint8_t>(this->gyro_range_));
84
85 MotionComponent::dump_config();
86}
87
89 if (this->is_failed())
90 return false;
91
92 const uint8_t accel_range = static_cast<uint8_t>(this->accel_range_);
93 const uint8_t gyro_range = static_cast<uint8_t>(this->gyro_range_);
94
95 // Read one contiguous sample:
96 // 0..5 accelerometer X/Y/Z
97 // 6..7 temperature
98 // 8..13 gyroscope X/Y/Z
99 uint8_t raw_data[14] = {};
100 this->read_registers_(ICM20689_REG_ACCEL_XOUT_H, raw_data, sizeof(raw_data));
101
102 const float accel_factor = accel_scale(accel_range);
103
104 data.acceleration[motion::X_AXIS] = static_cast<int16_t>(encode_uint16(raw_data[0], raw_data[1])) * accel_factor;
105 data.acceleration[motion::Y_AXIS] = static_cast<int16_t>(encode_uint16(raw_data[2], raw_data[3])) * accel_factor;
106 data.acceleration[motion::Z_AXIS] = static_cast<int16_t>(encode_uint16(raw_data[4], raw_data[5])) * accel_factor;
107
108 const float gyro_factor = gyro_scale(gyro_range);
109
110 data.angular_rate[motion::X_AXIS] = static_cast<int16_t>(encode_uint16(raw_data[8], raw_data[9])) * gyro_factor;
111 data.angular_rate[motion::Y_AXIS] = static_cast<int16_t>(encode_uint16(raw_data[10], raw_data[11])) * gyro_factor;
112 data.angular_rate[motion::Z_AXIS] = static_cast<int16_t>(encode_uint16(raw_data[12], raw_data[13])) * gyro_factor;
113
114 if (!this->temperature_callback_.empty()) {
115 // Datasheet formula: temperature (°C) = raw / 326.8 + 25.
116 const float temperature = static_cast<int16_t>(encode_uint16(raw_data[6], raw_data[7])) / 326.8f + 25.0f;
117 this->temperature_callback_.call(temperature);
118 }
119
120 return true;
121}
122
123} // namespace esphome::icm20689
void mark_failed()
Mark this component as failed.
bool is_failed() const
Definition component.h:274
void write_register_(uint8_t reg, uint8_t value)
Definition icm20689.cpp:21
bool update_data(motion::MotionData &data) override
Definition icm20689.cpp:88
void read_registers_(uint8_t reg, uint8_t *data, size_t len)
Definition icm20689.cpp:28
ICM20689AccelRange accel_range_
Definition icm20689.h:60
LazyCallbackManager< void(float)> temperature_callback_
Definition icm20689.h:63
Range range
Definition msa3xx.h:0
ESPHOME_LOG_TAG(TAG, "icm20689")
const void size_t len
Definition hal.h:68
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
uint16_t temperature
Definition sun_gtil2.cpp:12