ESPHome 2025.6.3
Loading...
Searching...
No Matches
i2c_sensirion.cpp
Go to the documentation of this file.
1#include "i2c_sensirion.h"
2#include "esphome/core/hal.h"
4#include "esphome/core/log.h"
5#include <cinttypes>
6
7namespace esphome {
8namespace sensirion_common {
9
10static const char *const TAG = "sensirion_i2c";
11// To avoid memory allocations for small writes a stack buffer is used
12static const size_t BUFFER_STACK_SIZE = 16;
13
14bool SensirionI2CDevice::read_data(uint16_t *data, uint8_t len) {
15 const uint8_t num_bytes = len * 3;
16 std::vector<uint8_t> buf(num_bytes);
17
18 last_error_ = this->read(buf.data(), num_bytes);
20 return false;
21 }
22
23 for (uint8_t i = 0; i < len; i++) {
24 const uint8_t j = 3 * i;
25 uint8_t crc = sht_crc_(buf[j], buf[j + 1]);
26 if (crc != buf[j + 2]) {
27 ESP_LOGE(TAG, "CRC8 Checksum invalid at pos %d! 0x%02X != 0x%02X", i, buf[j + 2], crc);
29 return false;
30 }
31 data[i] = encode_uint16(buf[j], buf[j + 1]);
32 }
33 return true;
34}
35/***
36 * write command with parameters and insert crc
37 * use stack array for less than 4 parameters. Most sensirion i2c commands have less parameters
38 */
39bool SensirionI2CDevice::write_command_(uint16_t command, CommandLen command_len, const uint16_t *data,
40 uint8_t data_len) {
41 uint8_t temp_stack[BUFFER_STACK_SIZE];
42 std::unique_ptr<uint8_t[]> temp_heap;
43 uint8_t *temp;
44 size_t required_buffer_len = data_len * 3 + 2;
45
46 // Is a dynamic allocation required ?
47 if (required_buffer_len >= BUFFER_STACK_SIZE) {
48 temp_heap = std::unique_ptr<uint8_t[]>(new uint8_t[required_buffer_len]);
49 temp = temp_heap.get();
50 } else {
51 temp = temp_stack;
52 }
53 // First byte or word is the command
54 uint8_t raw_idx = 0;
55 if (command_len == 1) {
56 temp[raw_idx++] = command & 0xFF;
57 } else {
58 // command is 2 bytes
59#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
60 temp[raw_idx++] = command >> 8;
61 temp[raw_idx++] = command & 0xFF;
62#else
63 temp[raw_idx++] = command & 0xFF;
64 temp[raw_idx++] = command >> 8;
65#endif
66 }
67 // add parameters followed by crc
68 // skipped if len == 0
69 for (size_t i = 0; i < data_len; i++) {
70#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
71 temp[raw_idx++] = data[i] >> 8;
72 temp[raw_idx++] = data[i] & 0xFF;
73#else
74 temp[raw_idx++] = data[i] & 0xFF;
75 temp[raw_idx++] = data[i] >> 8;
76#endif
77 temp[raw_idx++] = sht_crc_(data[i]);
78 }
79 last_error_ = this->write(temp, raw_idx);
80 return last_error_ == i2c::ERROR_OK;
81}
82
83bool SensirionI2CDevice::get_register_(uint16_t reg, CommandLen command_len, uint16_t *data, uint8_t len,
84 uint8_t delay_ms) {
85 if (!this->write_command_(reg, command_len, nullptr, 0)) {
86 ESP_LOGE(TAG, "Failed to write i2c register=0x%X (%d) err=%d,", reg, command_len, this->last_error_);
87 return false;
88 }
89 delay(delay_ms);
90 bool result = this->read_data(data, len);
91 if (!result) {
92 ESP_LOGE(TAG, "Failed to read data from register=0x%X err=%d,", reg, this->last_error_);
93 }
94 return result;
95}
96
97// The 8-bit CRC checksum is transmitted after each data word
98uint8_t SensirionI2CDevice::sht_crc_(uint16_t data) {
99 uint8_t bit;
100 uint8_t crc = 0xFF;
101#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
102 crc ^= data >> 8;
103#else
104 crc ^= data & 0xFF;
105#endif
106 for (bit = 8; bit > 0; --bit) {
107 if (crc & 0x80) {
108 crc = (crc << 1) ^ crc_polynomial_;
109 } else {
110 crc = (crc << 1);
111 }
112 }
113#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
114 crc ^= data & 0xFF;
115#else
116 crc ^= data >> 8;
117#endif
118 for (bit = 8; bit > 0; --bit) {
119 if (crc & 0x80) {
120 crc = (crc << 1) ^ crc_polynomial_;
121 } else {
122 crc = (crc << 1);
123 }
124 }
125 return crc;
126}
127
128} // namespace sensirion_common
129} // namespace esphome
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
I2CRegister reg(uint8_t a_register)
calls the I2CRegister constructor
Definition i2c.h:153
ErrorCode read(uint8_t *data, size_t len)
reads an array of bytes from the device using an I2CBus
Definition i2c.h:164
bool get_register_(uint16_t reg, CommandLen command_len, uint16_t *data, uint8_t len, uint8_t delay)
get data words from i2c register.
i2c::ErrorCode last_error_
last error code from i2c operation
uint8_t sht_crc_(uint16_t data)
8-bit CRC checksum that is transmitted after each data word for read and write operation
bool read_data(uint16_t *data, uint8_t len)
Read data words from i2c device.
bool write_command_(uint16_t command, CommandLen command_len, const uint16_t *data, uint8_t data_len)
Write a command with arguments as words.
@ ERROR_CRC
bytes received with a CRC error
Definition i2c_bus.h:20
@ ERROR_OK
No error found during execution of method.
Definition i2c_bus.h:13
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string size_t len
Definition helpers.h:302
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:192
void IRAM_ATTR HOT delay(uint32_t ms)
Definition core.cpp:29