mirror of
https://github.com/esphome/esphome.git
synced 2025-09-26 07:02:21 +01:00
[sensirion_common] Tidy up, optimize (#10604)
This commit is contained in:
@@ -11,21 +11,22 @@ static const char *const TAG = "sensirion_i2c";
|
|||||||
// To avoid memory allocations for small writes a stack buffer is used
|
// To avoid memory allocations for small writes a stack buffer is used
|
||||||
static const size_t BUFFER_STACK_SIZE = 16;
|
static const size_t BUFFER_STACK_SIZE = 16;
|
||||||
|
|
||||||
bool SensirionI2CDevice::read_data(uint16_t *data, uint8_t len) {
|
bool SensirionI2CDevice::read_data(uint16_t *data, const uint8_t len) {
|
||||||
const uint8_t num_bytes = len * 3;
|
const uint8_t num_bytes = len * 3;
|
||||||
std::vector<uint8_t> buf(num_bytes);
|
uint8_t buf[num_bytes];
|
||||||
|
|
||||||
last_error_ = this->read(buf.data(), num_bytes);
|
this->last_error_ = this->read(buf, num_bytes);
|
||||||
if (last_error_ != i2c::ERROR_OK) {
|
if (this->last_error_ != i2c::ERROR_OK) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint8_t i = 0; i < len; i++) {
|
for (uint8_t i = 0; i < len; i++) {
|
||||||
const uint8_t j = 3 * i;
|
const uint8_t j = 3 * i;
|
||||||
uint8_t crc = sht_crc_(buf[j], buf[j + 1]);
|
// Use MSB first since Sensirion devices use CRC-8 with MSB first
|
||||||
|
uint8_t crc = crc8(&buf[j], 2, 0xFF, CRC_POLYNOMIAL, true);
|
||||||
if (crc != buf[j + 2]) {
|
if (crc != buf[j + 2]) {
|
||||||
ESP_LOGE(TAG, "CRC8 Checksum invalid at pos %d! 0x%02X != 0x%02X", i, buf[j + 2], crc);
|
ESP_LOGE(TAG, "CRC invalid @ %d! 0x%02X != 0x%02X", i, buf[j + 2], crc);
|
||||||
last_error_ = i2c::ERROR_CRC;
|
this->last_error_ = i2c::ERROR_CRC;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
data[i] = encode_uint16(buf[j], buf[j + 1]);
|
data[i] = encode_uint16(buf[j], buf[j + 1]);
|
||||||
@@ -34,10 +35,10 @@ bool SensirionI2CDevice::read_data(uint16_t *data, uint8_t len) {
|
|||||||
}
|
}
|
||||||
/***
|
/***
|
||||||
* write command with parameters and insert crc
|
* write command with parameters and insert crc
|
||||||
* use stack array for less than 4 parameters. Most sensirion i2c commands have less parameters
|
* use stack array for less than 4 parameters. Most Sensirion I2C commands have less parameters
|
||||||
*/
|
*/
|
||||||
bool SensirionI2CDevice::write_command_(uint16_t command, CommandLen command_len, const uint16_t *data,
|
bool SensirionI2CDevice::write_command_(uint16_t command, CommandLen command_len, const uint16_t *data,
|
||||||
uint8_t data_len) {
|
const uint8_t data_len) {
|
||||||
uint8_t temp_stack[BUFFER_STACK_SIZE];
|
uint8_t temp_stack[BUFFER_STACK_SIZE];
|
||||||
std::unique_ptr<uint8_t[]> temp_heap;
|
std::unique_ptr<uint8_t[]> temp_heap;
|
||||||
uint8_t *temp;
|
uint8_t *temp;
|
||||||
@@ -74,56 +75,26 @@ bool SensirionI2CDevice::write_command_(uint16_t command, CommandLen command_len
|
|||||||
temp[raw_idx++] = data[i] & 0xFF;
|
temp[raw_idx++] = data[i] & 0xFF;
|
||||||
temp[raw_idx++] = data[i] >> 8;
|
temp[raw_idx++] = data[i] >> 8;
|
||||||
#endif
|
#endif
|
||||||
temp[raw_idx++] = sht_crc_(data[i]);
|
// Use MSB first since Sensirion devices use CRC-8 with MSB first
|
||||||
|
temp[raw_idx++] = crc8(&temp[raw_idx - 2], 2, 0xFF, CRC_POLYNOMIAL, true);
|
||||||
}
|
}
|
||||||
last_error_ = this->write(temp, raw_idx);
|
this->last_error_ = this->write(temp, raw_idx);
|
||||||
return last_error_ == i2c::ERROR_OK;
|
return this->last_error_ == i2c::ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SensirionI2CDevice::get_register_(uint16_t reg, CommandLen command_len, uint16_t *data, uint8_t len,
|
bool SensirionI2CDevice::get_register_(uint16_t reg, CommandLen command_len, uint16_t *data, const uint8_t len,
|
||||||
uint8_t delay_ms) {
|
const uint8_t delay_ms) {
|
||||||
if (!this->write_command_(reg, command_len, nullptr, 0)) {
|
if (!this->write_command_(reg, command_len, nullptr, 0)) {
|
||||||
ESP_LOGE(TAG, "Failed to write i2c register=0x%X (%d) err=%d,", reg, command_len, this->last_error_);
|
ESP_LOGE(TAG, "Write failed: reg=0x%X (%d) err=%d,", reg, command_len, this->last_error_);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
delay(delay_ms);
|
delay(delay_ms);
|
||||||
bool result = this->read_data(data, len);
|
bool result = this->read_data(data, len);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
ESP_LOGE(TAG, "Failed to read data from register=0x%X err=%d,", reg, this->last_error_);
|
ESP_LOGE(TAG, "Read failed: reg=0x%X err=%d,", reg, this->last_error_);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The 8-bit CRC checksum is transmitted after each data word
|
|
||||||
uint8_t SensirionI2CDevice::sht_crc_(uint16_t data) {
|
|
||||||
uint8_t bit;
|
|
||||||
uint8_t crc = 0xFF;
|
|
||||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
|
||||||
crc ^= data >> 8;
|
|
||||||
#else
|
|
||||||
crc ^= data & 0xFF;
|
|
||||||
#endif
|
|
||||||
for (bit = 8; bit > 0; --bit) {
|
|
||||||
if (crc & 0x80) {
|
|
||||||
crc = (crc << 1) ^ crc_polynomial_;
|
|
||||||
} else {
|
|
||||||
crc = (crc << 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
|
||||||
crc ^= data & 0xFF;
|
|
||||||
#else
|
|
||||||
crc ^= data >> 8;
|
|
||||||
#endif
|
|
||||||
for (bit = 8; bit > 0; --bit) {
|
|
||||||
if (crc & 0x80) {
|
|
||||||
crc = (crc << 1) ^ crc_polynomial_;
|
|
||||||
} else {
|
|
||||||
crc = (crc << 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return crc;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace sensirion_common
|
} // namespace sensirion_common
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
@@ -8,10 +8,10 @@ namespace esphome {
|
|||||||
namespace sensirion_common {
|
namespace sensirion_common {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of a i2c functions for Sensirion sensors
|
* Implementation of I2C functions for Sensirion sensors
|
||||||
* Sensirion data requires crc checking.
|
* Sensirion data requires CRC checking.
|
||||||
* Each 16 bit word is/must be followed 8 bit CRC code
|
* Each 16 bit word is/must be followed 8 bit CRC code
|
||||||
* (Applies to read and write - note the i2c command code doesn't need a CRC)
|
* (Applies to read and write - note the I2C command code doesn't need a CRC)
|
||||||
* Format:
|
* Format:
|
||||||
* | 16 Bit Command Code | 16 bit Data word 1 | CRC of DW 1 | 16 bit Data word 1 | CRC of DW 2 | ..
|
* | 16 Bit Command Code | 16 bit Data word 1 | CRC of DW 1 | 16 bit Data word 1 | CRC of DW 2 | ..
|
||||||
*/
|
*/
|
||||||
@@ -21,79 +21,79 @@ class SensirionI2CDevice : public i2c::I2CDevice {
|
|||||||
public:
|
public:
|
||||||
enum CommandLen : uint8_t { ADDR_8_BIT = 1, ADDR_16_BIT = 2 };
|
enum CommandLen : uint8_t { ADDR_8_BIT = 1, ADDR_16_BIT = 2 };
|
||||||
|
|
||||||
/** Read data words from i2c device.
|
/** Read data words from I2C device.
|
||||||
* handles crc check used by Sensirion sensors
|
* handles CRC check used by Sensirion sensors
|
||||||
* @param data pointer to raw result
|
* @param data pointer to raw result
|
||||||
* @param len number of words to read
|
* @param len number of words to read
|
||||||
* @return true if reading succeeded
|
* @return true if reading succeeded
|
||||||
*/
|
*/
|
||||||
bool read_data(uint16_t *data, uint8_t len);
|
bool read_data(uint16_t *data, uint8_t len);
|
||||||
|
|
||||||
/** Read 1 data word from i2c device.
|
/** Read 1 data word from I2C device.
|
||||||
* @param data reference to raw result
|
* @param data reference to raw result
|
||||||
* @return true if reading succeeded
|
* @return true if reading succeeded
|
||||||
*/
|
*/
|
||||||
bool read_data(uint16_t &data) { return this->read_data(&data, 1); }
|
bool read_data(uint16_t &data) { return this->read_data(&data, 1); }
|
||||||
|
|
||||||
/** get data words from i2c register.
|
/** get data words from I2C register.
|
||||||
* handles crc check used by Sensirion sensors
|
* handles CRC check used by Sensirion sensors
|
||||||
* @param i2c register
|
* @param I2C register
|
||||||
* @param data pointer to raw result
|
* @param data pointer to raw result
|
||||||
* @param len number of words to read
|
* @param len number of words to read
|
||||||
* @param delay milliseconds to to wait between sending the i2c command and reading the result
|
* @param delay milliseconds to to wait between sending the I2C command and reading the result
|
||||||
* @return true if reading succeeded
|
* @return true if reading succeeded
|
||||||
*/
|
*/
|
||||||
bool get_register(uint16_t command, uint16_t *data, uint8_t len, uint8_t delay = 0) {
|
bool get_register(uint16_t command, uint16_t *data, uint8_t len, uint8_t delay = 0) {
|
||||||
return get_register_(command, ADDR_16_BIT, data, len, delay);
|
return get_register_(command, ADDR_16_BIT, data, len, delay);
|
||||||
}
|
}
|
||||||
/** Read 1 data word from 16 bit i2c register.
|
/** Read 1 data word from 16 bit I2C register.
|
||||||
* @param i2c register
|
* @param I2C register
|
||||||
* @param data reference to raw result
|
* @param data reference to raw result
|
||||||
* @param delay milliseconds to to wait between sending the i2c command and reading the result
|
* @param delay milliseconds to to wait between sending the I2C command and reading the result
|
||||||
* @return true if reading succeeded
|
* @return true if reading succeeded
|
||||||
*/
|
*/
|
||||||
bool get_register(uint16_t i2c_register, uint16_t &data, uint8_t delay = 0) {
|
bool get_register(uint16_t i2c_register, uint16_t &data, uint8_t delay = 0) {
|
||||||
return this->get_register_(i2c_register, ADDR_16_BIT, &data, 1, delay);
|
return this->get_register_(i2c_register, ADDR_16_BIT, &data, 1, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** get data words from i2c register.
|
/** get data words from I2C register.
|
||||||
* handles crc check used by Sensirion sensors
|
* handles CRC check used by Sensirion sensors
|
||||||
* @param i2c register
|
* @param I2C register
|
||||||
* @param data pointer to raw result
|
* @param data pointer to raw result
|
||||||
* @param len number of words to read
|
* @param len number of words to read
|
||||||
* @param delay milliseconds to to wait between sending the i2c command and reading the result
|
* @param delay milliseconds to to wait between sending the I2C command and reading the result
|
||||||
* @return true if reading succeeded
|
* @return true if reading succeeded
|
||||||
*/
|
*/
|
||||||
bool get_8bit_register(uint8_t i2c_register, uint16_t *data, uint8_t len, uint8_t delay = 0) {
|
bool get_8bit_register(uint8_t i2c_register, uint16_t *data, uint8_t len, uint8_t delay = 0) {
|
||||||
return get_register_(i2c_register, ADDR_8_BIT, data, len, delay);
|
return get_register_(i2c_register, ADDR_8_BIT, data, len, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Read 1 data word from 8 bit i2c register.
|
/** Read 1 data word from 8 bit I2C register.
|
||||||
* @param i2c register
|
* @param I2C register
|
||||||
* @param data reference to raw result
|
* @param data reference to raw result
|
||||||
* @param delay milliseconds to to wait between sending the i2c command and reading the result
|
* @param delay milliseconds to to wait between sending the I2C command and reading the result
|
||||||
* @return true if reading succeeded
|
* @return true if reading succeeded
|
||||||
*/
|
*/
|
||||||
bool get_8bit_register(uint8_t i2c_register, uint16_t &data, uint8_t delay = 0) {
|
bool get_8bit_register(uint8_t i2c_register, uint16_t &data, uint8_t delay = 0) {
|
||||||
return this->get_register_(i2c_register, ADDR_8_BIT, &data, 1, delay);
|
return this->get_register_(i2c_register, ADDR_8_BIT, &data, 1, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Write a command to the i2c device.
|
/** Write a command to the I2C device.
|
||||||
* @param command i2c command to send
|
* @param command I2C command to send
|
||||||
* @return true if reading succeeded
|
* @return true if reading succeeded
|
||||||
*/
|
*/
|
||||||
template<class T> bool write_command(T i2c_register) { return write_command(i2c_register, nullptr, 0); }
|
template<class T> bool write_command(T i2c_register) { return write_command(i2c_register, nullptr, 0); }
|
||||||
|
|
||||||
/** Write a command and one data word to the i2c device .
|
/** Write a command and one data word to the I2C device .
|
||||||
* @param command i2c command to send
|
* @param command I2C command to send
|
||||||
* @param data argument for the i2c command
|
* @param data argument for the I2C command
|
||||||
* @return true if reading succeeded
|
* @return true if reading succeeded
|
||||||
*/
|
*/
|
||||||
template<class T> bool write_command(T i2c_register, uint16_t data) { return write_command(i2c_register, &data, 1); }
|
template<class T> bool write_command(T i2c_register, uint16_t data) { return write_command(i2c_register, &data, 1); }
|
||||||
|
|
||||||
/** Write a command with arguments as words
|
/** Write a command with arguments as words
|
||||||
* @param i2c_register i2c command to send - an be uint8_t or uint16_t
|
* @param i2c_register I2C command to send - an be uint8_t or uint16_t
|
||||||
* @param data vector<uint16> arguments for the i2c command
|
* @param data vector<uint16> arguments for the I2C command
|
||||||
* @return true if reading succeeded
|
* @return true if reading succeeded
|
||||||
*/
|
*/
|
||||||
template<class T> bool write_command(T i2c_register, const std::vector<uint16_t> &data) {
|
template<class T> bool write_command(T i2c_register, const std::vector<uint16_t> &data) {
|
||||||
@@ -101,57 +101,39 @@ class SensirionI2CDevice : public i2c::I2CDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Write a command with arguments as words
|
/** Write a command with arguments as words
|
||||||
* @param i2c_register i2c command to send - an be uint8_t or uint16_t
|
* @param i2c_register I2C command to send - an be uint8_t or uint16_t
|
||||||
* @param data arguments for the i2c command
|
* @param data arguments for the I2C command
|
||||||
* @param len number of arguments (words)
|
* @param len number of arguments (words)
|
||||||
* @return true if reading succeeded
|
* @return true if reading succeeded
|
||||||
*/
|
*/
|
||||||
template<class T> bool write_command(T i2c_register, const uint16_t *data, uint8_t len) {
|
template<class T> bool write_command(T i2c_register, const uint16_t *data, uint8_t len) {
|
||||||
// limit to 8 or 16 bit only
|
// limit to 8 or 16 bit only
|
||||||
static_assert(sizeof(i2c_register) == 1 || sizeof(i2c_register) == 2,
|
static_assert(sizeof(i2c_register) == 1 || sizeof(i2c_register) == 2, "Only 8 or 16 bit command types supported");
|
||||||
"only 8 or 16 bit command types are supported.");
|
|
||||||
return write_command_(i2c_register, CommandLen(sizeof(T)), data, len);
|
return write_command_(i2c_register, CommandLen(sizeof(T)), data, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint8_t crc_polynomial_{0x31u}; // default for sensirion
|
|
||||||
/** Write a command with arguments as words
|
/** Write a command with arguments as words
|
||||||
* @param command i2c command to send can be uint8_t or uint16_t
|
* @param command I2C command to send can be uint8_t or uint16_t
|
||||||
* @param command_len either 1 for short 8 bit command or 2 for 16 bit command codes
|
* @param command_len either 1 for short 8 bit command or 2 for 16 bit command codes
|
||||||
* @param data arguments for the i2c command
|
* @param data arguments for the I2C command
|
||||||
* @param data_len number of arguments (words)
|
* @param data_len number of arguments (words)
|
||||||
* @return true if reading succeeded
|
* @return true if reading succeeded
|
||||||
*/
|
*/
|
||||||
bool write_command_(uint16_t command, CommandLen command_len, const uint16_t *data, uint8_t data_len);
|
bool write_command_(uint16_t command, CommandLen command_len, const uint16_t *data, uint8_t data_len);
|
||||||
|
|
||||||
/** get data words from i2c register.
|
/** get data words from I2C register.
|
||||||
* handles crc check used by Sensirion sensors
|
* handles CRC check used by Sensirion sensors
|
||||||
* @param i2c register
|
* @param I2C register
|
||||||
* @param command_len either 1 for short 8 bit command or 2 for 16 bit command codes
|
* @param command_len either 1 for short 8 bit command or 2 for 16 bit command codes
|
||||||
* @param data pointer to raw result
|
* @param data pointer to raw result
|
||||||
* @param len number of words to read
|
* @param len number of words to read
|
||||||
* @param delay milliseconds to to wait between sending the i2c command and reading the result
|
* @param delay milliseconds to to wait between sending the I2C command and reading the result
|
||||||
* @return true if reading succeeded
|
* @return true if reading succeeded
|
||||||
*/
|
*/
|
||||||
bool get_register_(uint16_t reg, CommandLen command_len, uint16_t *data, uint8_t len, uint8_t delay);
|
bool get_register_(uint16_t reg, CommandLen command_len, uint16_t *data, uint8_t len, uint8_t delay);
|
||||||
|
|
||||||
/** 8-bit CRC checksum that is transmitted after each data word for read and write operation
|
/** last error code from I2C operation
|
||||||
* @param command i2c command to send
|
|
||||||
* @param data data word for which the crc8 checksum is calculated
|
|
||||||
* @param len number of arguments (words)
|
|
||||||
* @return 8 Bit CRC
|
|
||||||
*/
|
|
||||||
uint8_t sht_crc_(uint16_t data);
|
|
||||||
|
|
||||||
/** 8-bit CRC checksum that is transmitted after each data word for read and write operation
|
|
||||||
* @param command i2c command to send
|
|
||||||
* @param data1 high byte of data word
|
|
||||||
* @param data2 low byte of data word
|
|
||||||
* @return 8 Bit CRC
|
|
||||||
*/
|
|
||||||
uint8_t sht_crc_(uint8_t data1, uint8_t data2) { return sht_crc_(encode_uint16(data1, data2)); }
|
|
||||||
|
|
||||||
/** last error code from i2c operation
|
|
||||||
*/
|
*/
|
||||||
i2c::ErrorCode last_error_;
|
i2c::ErrorCode last_error_;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user