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