mirror of
https://github.com/esphome/esphome.git
synced 2025-09-07 13:52:20 +01:00
Merge remote-tracking branch 'upstream/dev' into integration
This commit is contained in:
@@ -29,6 +29,19 @@ static const int8_t SEN5X_INDEX_SCALE_FACTOR = 10; //
|
||||
static const int8_t SEN5X_MIN_INDEX_VALUE = 1 * SEN5X_INDEX_SCALE_FACTOR; // must be adjusted by the scale factor
|
||||
static const int16_t SEN5X_MAX_INDEX_VALUE = 500 * SEN5X_INDEX_SCALE_FACTOR; // must be adjusted by the scale factor
|
||||
|
||||
static const LogString *rht_accel_mode_to_string(RhtAccelerationMode mode) {
|
||||
switch (mode) {
|
||||
case LOW_ACCELERATION:
|
||||
return LOG_STR("LOW");
|
||||
case MEDIUM_ACCELERATION:
|
||||
return LOG_STR("MEDIUM");
|
||||
case HIGH_ACCELERATION:
|
||||
return LOG_STR("HIGH");
|
||||
default:
|
||||
return LOG_STR("UNKNOWN");
|
||||
}
|
||||
}
|
||||
|
||||
void SEN5XComponent::setup() {
|
||||
// the sensor needs 1000 ms to enter the idle state
|
||||
this->set_timeout(1000, [this]() {
|
||||
@@ -50,7 +63,7 @@ void SEN5XComponent::setup() {
|
||||
uint32_t stop_measurement_delay = 0;
|
||||
// In order to query the device periodic measurement must be ceased
|
||||
if (raw_read_status) {
|
||||
ESP_LOGD(TAG, "Sensor has data available, stopping periodic measurement");
|
||||
ESP_LOGD(TAG, "Data is available; stopping periodic measurement");
|
||||
if (!this->write_command(SEN5X_CMD_STOP_MEASUREMENTS)) {
|
||||
ESP_LOGE(TAG, "Failed to stop measurements");
|
||||
this->mark_failed();
|
||||
@@ -71,7 +84,8 @@ void SEN5XComponent::setup() {
|
||||
this->serial_number_[0] = static_cast<bool>(uint16_t(raw_serial_number[0]) & 0xFF);
|
||||
this->serial_number_[1] = static_cast<uint16_t>(raw_serial_number[0] & 0xFF);
|
||||
this->serial_number_[2] = static_cast<uint16_t>(raw_serial_number[1] >> 8);
|
||||
ESP_LOGD(TAG, "Serial number %02d.%02d.%02d", serial_number_[0], serial_number_[1], serial_number_[2]);
|
||||
ESP_LOGV(TAG, "Serial number %02d.%02d.%02d", this->serial_number_[0], this->serial_number_[1],
|
||||
this->serial_number_[2]);
|
||||
|
||||
uint16_t raw_product_name[16];
|
||||
if (!this->get_register(SEN5X_CMD_GET_PRODUCT_NAME, raw_product_name, 16, 20)) {
|
||||
@@ -88,45 +102,43 @@ void SEN5XComponent::setup() {
|
||||
// first char
|
||||
current_char = *current_int >> 8;
|
||||
if (current_char) {
|
||||
product_name_.push_back(current_char);
|
||||
this->product_name_.push_back(current_char);
|
||||
// second char
|
||||
current_char = *current_int & 0xFF;
|
||||
if (current_char) {
|
||||
product_name_.push_back(current_char);
|
||||
this->product_name_.push_back(current_char);
|
||||
}
|
||||
}
|
||||
current_int++;
|
||||
} while (current_char && --max);
|
||||
|
||||
Sen5xType sen5x_type = UNKNOWN;
|
||||
if (product_name_ == "SEN50") {
|
||||
if (this->product_name_ == "SEN50") {
|
||||
sen5x_type = SEN50;
|
||||
} else {
|
||||
if (product_name_ == "SEN54") {
|
||||
if (this->product_name_ == "SEN54") {
|
||||
sen5x_type = SEN54;
|
||||
} else {
|
||||
if (product_name_ == "SEN55") {
|
||||
if (this->product_name_ == "SEN55") {
|
||||
sen5x_type = SEN55;
|
||||
}
|
||||
}
|
||||
ESP_LOGD(TAG, "Productname %s", product_name_.c_str());
|
||||
ESP_LOGD(TAG, "Product name: %s", this->product_name_.c_str());
|
||||
}
|
||||
if (this->humidity_sensor_ && sen5x_type == SEN50) {
|
||||
ESP_LOGE(TAG, "For Relative humidity a SEN54 OR SEN55 is required. You are using a <%s> sensor",
|
||||
this->product_name_.c_str());
|
||||
ESP_LOGE(TAG, "Relative humidity requires a SEN54 or SEN55");
|
||||
this->humidity_sensor_ = nullptr; // mark as not used
|
||||
}
|
||||
if (this->temperature_sensor_ && sen5x_type == SEN50) {
|
||||
ESP_LOGE(TAG, "For Temperature a SEN54 OR SEN55 is required. You are using a <%s> sensor",
|
||||
this->product_name_.c_str());
|
||||
ESP_LOGE(TAG, "Temperature requires a SEN54 or SEN55");
|
||||
this->temperature_sensor_ = nullptr; // mark as not used
|
||||
}
|
||||
if (this->voc_sensor_ && sen5x_type == SEN50) {
|
||||
ESP_LOGE(TAG, "For VOC a SEN54 OR SEN55 is required. You are using a <%s> sensor", this->product_name_.c_str());
|
||||
ESP_LOGE(TAG, "VOC requires a SEN54 or SEN55");
|
||||
this->voc_sensor_ = nullptr; // mark as not used
|
||||
}
|
||||
if (this->nox_sensor_ && sen5x_type != SEN55) {
|
||||
ESP_LOGE(TAG, "For NOx a SEN55 is required. You are using a <%s> sensor", this->product_name_.c_str());
|
||||
ESP_LOGE(TAG, "NOx requires a SEN55");
|
||||
this->nox_sensor_ = nullptr; // mark as not used
|
||||
}
|
||||
|
||||
@@ -137,7 +149,7 @@ void SEN5XComponent::setup() {
|
||||
return;
|
||||
}
|
||||
this->firmware_version_ >>= 8;
|
||||
ESP_LOGD(TAG, "Firmware version %d", this->firmware_version_);
|
||||
ESP_LOGV(TAG, "Firmware version %d", this->firmware_version_);
|
||||
|
||||
if (this->voc_sensor_ && this->store_baseline_) {
|
||||
uint32_t combined_serial =
|
||||
@@ -150,7 +162,7 @@ void SEN5XComponent::setup() {
|
||||
|
||||
if (this->pref_.load(&this->voc_baselines_storage_)) {
|
||||
ESP_LOGI(TAG, "Loaded VOC baseline state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32,
|
||||
this->voc_baselines_storage_.state0, voc_baselines_storage_.state1);
|
||||
this->voc_baselines_storage_.state0, this->voc_baselines_storage_.state1);
|
||||
}
|
||||
|
||||
// Initialize storage timestamp
|
||||
@@ -158,13 +170,13 @@ void SEN5XComponent::setup() {
|
||||
|
||||
if (this->voc_baselines_storage_.state0 > 0 && this->voc_baselines_storage_.state1 > 0) {
|
||||
ESP_LOGI(TAG, "Setting VOC baseline from save state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32,
|
||||
this->voc_baselines_storage_.state0, voc_baselines_storage_.state1);
|
||||
this->voc_baselines_storage_.state0, this->voc_baselines_storage_.state1);
|
||||
uint16_t states[4];
|
||||
|
||||
states[0] = voc_baselines_storage_.state0 >> 16;
|
||||
states[1] = voc_baselines_storage_.state0 & 0xFFFF;
|
||||
states[2] = voc_baselines_storage_.state1 >> 16;
|
||||
states[3] = voc_baselines_storage_.state1 & 0xFFFF;
|
||||
states[0] = this->voc_baselines_storage_.state0 >> 16;
|
||||
states[1] = this->voc_baselines_storage_.state0 & 0xFFFF;
|
||||
states[2] = this->voc_baselines_storage_.state1 >> 16;
|
||||
states[3] = this->voc_baselines_storage_.state1 & 0xFFFF;
|
||||
|
||||
if (!this->write_command(SEN5X_CMD_VOC_ALGORITHM_STATE, states, 4)) {
|
||||
ESP_LOGE(TAG, "Failed to set VOC baseline from saved state");
|
||||
@@ -182,11 +194,11 @@ void SEN5XComponent::setup() {
|
||||
delay(20);
|
||||
uint16_t secs[2];
|
||||
if (this->read_data(secs, 2)) {
|
||||
auto_cleaning_interval_ = secs[0] << 16 | secs[1];
|
||||
this->auto_cleaning_interval_ = secs[0] << 16 | secs[1];
|
||||
}
|
||||
}
|
||||
if (acceleration_mode_.has_value()) {
|
||||
result = this->write_command(SEN5X_CMD_RHT_ACCELERATION_MODE, acceleration_mode_.value());
|
||||
if (this->acceleration_mode_.has_value()) {
|
||||
result = this->write_command(SEN5X_CMD_RHT_ACCELERATION_MODE, this->acceleration_mode_.value());
|
||||
} else {
|
||||
result = this->write_command(SEN5X_CMD_RHT_ACCELERATION_MODE);
|
||||
}
|
||||
@@ -197,7 +209,7 @@ void SEN5XComponent::setup() {
|
||||
return;
|
||||
}
|
||||
delay(20);
|
||||
if (!acceleration_mode_.has_value()) {
|
||||
if (!this->acceleration_mode_.has_value()) {
|
||||
uint16_t mode;
|
||||
if (this->read_data(mode)) {
|
||||
this->acceleration_mode_ = RhtAccelerationMode(mode);
|
||||
@@ -227,19 +239,18 @@ void SEN5XComponent::setup() {
|
||||
}
|
||||
|
||||
if (!this->write_command(cmd)) {
|
||||
ESP_LOGE(TAG, "Error starting continuous measurements.");
|
||||
ESP_LOGE(TAG, "Error starting continuous measurements");
|
||||
this->error_code_ = MEASUREMENT_INIT_FAILED;
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
initialized_ = true;
|
||||
ESP_LOGD(TAG, "Sensor initialized");
|
||||
this->initialized_ = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void SEN5XComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "sen5x:");
|
||||
ESP_LOGCONFIG(TAG, "SEN5X:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
if (this->is_failed()) {
|
||||
switch (this->error_code_) {
|
||||
@@ -247,16 +258,16 @@ void SEN5XComponent::dump_config() {
|
||||
ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
break;
|
||||
case MEASUREMENT_INIT_FAILED:
|
||||
ESP_LOGW(TAG, "Measurement Initialization failed");
|
||||
ESP_LOGW(TAG, "Measurement initialization failed");
|
||||
break;
|
||||
case SERIAL_NUMBER_IDENTIFICATION_FAILED:
|
||||
ESP_LOGW(TAG, "Unable to read sensor serial id");
|
||||
ESP_LOGW(TAG, "Unable to read serial ID");
|
||||
break;
|
||||
case PRODUCT_NAME_FAILED:
|
||||
ESP_LOGW(TAG, "Unable to read product name");
|
||||
break;
|
||||
case FIRMWARE_FAILED:
|
||||
ESP_LOGW(TAG, "Unable to read sensor firmware version");
|
||||
ESP_LOGW(TAG, "Unable to read firmware version");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "Unknown setup error");
|
||||
@@ -264,26 +275,17 @@ void SEN5XComponent::dump_config() {
|
||||
}
|
||||
}
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Productname: %s\n"
|
||||
" Product name: %s\n"
|
||||
" Firmware version: %d\n"
|
||||
" Serial number %02d.%02d.%02d",
|
||||
this->product_name_.c_str(), this->firmware_version_, serial_number_[0], serial_number_[1],
|
||||
serial_number_[2]);
|
||||
this->product_name_.c_str(), this->firmware_version_, this->serial_number_[0], this->serial_number_[1],
|
||||
this->serial_number_[2]);
|
||||
if (this->auto_cleaning_interval_.has_value()) {
|
||||
ESP_LOGCONFIG(TAG, " Auto cleaning interval %" PRId32 " seconds", auto_cleaning_interval_.value());
|
||||
ESP_LOGCONFIG(TAG, " Auto cleaning interval: %" PRId32 "s", this->auto_cleaning_interval_.value());
|
||||
}
|
||||
if (this->acceleration_mode_.has_value()) {
|
||||
switch (this->acceleration_mode_.value()) {
|
||||
case LOW_ACCELERATION:
|
||||
ESP_LOGCONFIG(TAG, " Low RH/T acceleration mode");
|
||||
break;
|
||||
case MEDIUM_ACCELERATION:
|
||||
ESP_LOGCONFIG(TAG, " Medium RH/T acceleration mode");
|
||||
break;
|
||||
case HIGH_ACCELERATION:
|
||||
ESP_LOGCONFIG(TAG, " High RH/T acceleration mode");
|
||||
break;
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " RH/T acceleration mode: %s",
|
||||
LOG_STR_ARG(rht_accel_mode_to_string(this->acceleration_mode_.value())));
|
||||
}
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
LOG_SENSOR(" ", "PM 1.0", this->pm_1_0_sensor_);
|
||||
@@ -297,7 +299,7 @@ void SEN5XComponent::dump_config() {
|
||||
}
|
||||
|
||||
void SEN5XComponent::update() {
|
||||
if (!initialized_) {
|
||||
if (!this->initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -320,8 +322,8 @@ void SEN5XComponent::update() {
|
||||
this->voc_baselines_storage_.state1 = state1;
|
||||
|
||||
if (this->pref_.save(&this->voc_baselines_storage_)) {
|
||||
ESP_LOGI(TAG, "Stored VOC baseline state0: 0x%04" PRIX32 " ,state1: 0x%04" PRIX32,
|
||||
this->voc_baselines_storage_.state0, voc_baselines_storage_.state1);
|
||||
ESP_LOGI(TAG, "Stored VOC baseline state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32,
|
||||
this->voc_baselines_storage_.state0, this->voc_baselines_storage_.state1);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Could not store VOC baselines");
|
||||
}
|
||||
@@ -333,7 +335,7 @@ void SEN5XComponent::update() {
|
||||
|
||||
if (!this->write_command(SEN5X_CMD_READ_MEASUREMENT)) {
|
||||
this->status_set_warning();
|
||||
ESP_LOGD(TAG, "write error read measurement (%d)", this->last_error_);
|
||||
ESP_LOGD(TAG, "Write error: read measurement (%d)", this->last_error_);
|
||||
return;
|
||||
}
|
||||
this->set_timeout(20, [this]() {
|
||||
@@ -341,7 +343,7 @@ void SEN5XComponent::update() {
|
||||
|
||||
if (!this->read_data(measurements, 8)) {
|
||||
this->status_set_warning();
|
||||
ESP_LOGD(TAG, "read data error (%d)", this->last_error_);
|
||||
ESP_LOGD(TAG, "Read data error (%d)", this->last_error_);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -413,7 +415,7 @@ bool SEN5XComponent::write_tuning_parameters_(uint16_t i2c_command, const GasTun
|
||||
params[5] = tuning.gain_factor;
|
||||
auto result = write_command(i2c_command, params, 6);
|
||||
if (!result) {
|
||||
ESP_LOGE(TAG, "set tuning parameters failed. i2c command=%0xX, err=%d", i2c_command, this->last_error_);
|
||||
ESP_LOGE(TAG, "Set tuning parameters failed (command=%0xX, err=%d)", i2c_command, this->last_error_);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -424,7 +426,7 @@ bool SEN5XComponent::write_temperature_compensation_(const TemperatureCompensati
|
||||
params[1] = compensation.normalized_offset_slope;
|
||||
params[2] = compensation.time_constant;
|
||||
if (!write_command(SEN5X_CMD_TEMPERATURE_COMPENSATION, params, 3)) {
|
||||
ESP_LOGE(TAG, "set temperature_compensation failed. Err=%d", this->last_error_);
|
||||
ESP_LOGE(TAG, "Set temperature_compensation failed (%d)", this->last_error_);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -433,7 +435,7 @@ bool SEN5XComponent::write_temperature_compensation_(const TemperatureCompensati
|
||||
bool SEN5XComponent::start_fan_cleaning() {
|
||||
if (!write_command(SEN5X_CMD_START_CLEANING_FAN)) {
|
||||
this->status_set_warning();
|
||||
ESP_LOGE(TAG, "write error start fan (%d)", this->last_error_);
|
||||
ESP_LOGE(TAG, "Start fan cleaning failed (%d)", this->last_error_);
|
||||
return false;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "Fan auto clean started");
|
||||
|
@@ -9,7 +9,7 @@
|
||||
namespace esphome {
|
||||
namespace sen5x {
|
||||
|
||||
enum ERRORCODE {
|
||||
enum ERRORCODE : uint8_t {
|
||||
COMMUNICATION_FAILED,
|
||||
SERIAL_NUMBER_IDENTIFICATION_FAILED,
|
||||
MEASUREMENT_INIT_FAILED,
|
||||
@@ -18,19 +18,17 @@ enum ERRORCODE {
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
// Shortest time interval of 3H for storing baseline values.
|
||||
// Prevents wear of the flash because of too many write operations
|
||||
const uint32_t SHORTEST_BASELINE_STORE_INTERVAL = 10800;
|
||||
// Store anyway if the baseline difference exceeds the max storage diff value
|
||||
const uint32_t MAXIMUM_STORAGE_DIFF = 50;
|
||||
enum RhtAccelerationMode : uint16_t {
|
||||
LOW_ACCELERATION = 0,
|
||||
MEDIUM_ACCELERATION = 1,
|
||||
HIGH_ACCELERATION = 2,
|
||||
};
|
||||
|
||||
struct Sen5xBaselines {
|
||||
int32_t state0;
|
||||
int32_t state1;
|
||||
} PACKED; // NOLINT
|
||||
|
||||
enum RhtAccelerationMode : uint16_t { LOW_ACCELERATION = 0, MEDIUM_ACCELERATION = 1, HIGH_ACCELERATION = 2 };
|
||||
|
||||
struct GasTuning {
|
||||
uint16_t index_offset;
|
||||
uint16_t learning_time_offset_hours;
|
||||
@@ -46,6 +44,12 @@ struct TemperatureCompensation {
|
||||
uint16_t time_constant;
|
||||
};
|
||||
|
||||
// Shortest time interval of 3H for storing baseline values.
|
||||
// Prevents wear of the flash because of too many write operations
|
||||
static const uint32_t SHORTEST_BASELINE_STORE_INTERVAL = 10800;
|
||||
// Store anyway if the baseline difference exceeds the max storage diff value
|
||||
static const uint32_t MAXIMUM_STORAGE_DIFF = 50;
|
||||
|
||||
class SEN5XComponent : public PollingComponent, public sensirion_common::SensirionI2CDevice {
|
||||
public:
|
||||
void setup() override;
|
||||
@@ -102,8 +106,14 @@ class SEN5XComponent : public PollingComponent, public sensirion_common::Sensiri
|
||||
protected:
|
||||
bool write_tuning_parameters_(uint16_t i2c_command, const GasTuning &tuning);
|
||||
bool write_temperature_compensation_(const TemperatureCompensation &compensation);
|
||||
|
||||
uint32_t seconds_since_last_store_;
|
||||
uint16_t firmware_version_;
|
||||
ERRORCODE error_code_;
|
||||
uint8_t serial_number_[4];
|
||||
bool initialized_{false};
|
||||
bool store_baseline_;
|
||||
|
||||
sensor::Sensor *pm_1_0_sensor_{nullptr};
|
||||
sensor::Sensor *pm_2_5_sensor_{nullptr};
|
||||
sensor::Sensor *pm_4_0_sensor_{nullptr};
|
||||
@@ -115,18 +125,14 @@ class SEN5XComponent : public PollingComponent, public sensirion_common::Sensiri
|
||||
// SEN55 only
|
||||
sensor::Sensor *nox_sensor_{nullptr};
|
||||
|
||||
std::string product_name_;
|
||||
uint8_t serial_number_[4];
|
||||
uint16_t firmware_version_;
|
||||
Sen5xBaselines voc_baselines_storage_;
|
||||
bool store_baseline_;
|
||||
uint32_t seconds_since_last_store_;
|
||||
ESPPreferenceObject pref_;
|
||||
optional<RhtAccelerationMode> acceleration_mode_;
|
||||
optional<uint32_t> auto_cleaning_interval_;
|
||||
optional<GasTuning> voc_tuning_params_;
|
||||
optional<GasTuning> nox_tuning_params_;
|
||||
optional<TemperatureCompensation> temperature_compensation_;
|
||||
ESPPreferenceObject pref_;
|
||||
std::string product_name_;
|
||||
Sen5xBaselines voc_baselines_storage_;
|
||||
};
|
||||
|
||||
} // namespace sen5x
|
||||
|
@@ -11,21 +11,22 @@ static const char *const TAG = "sensirion_i2c";
|
||||
// To avoid memory allocations for small writes a stack buffer is used
|
||||
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;
|
||||
std::vector<uint8_t> buf(num_bytes);
|
||||
uint8_t buf[num_bytes];
|
||||
|
||||
last_error_ = this->read(buf.data(), num_bytes);
|
||||
if (last_error_ != i2c::ERROR_OK) {
|
||||
this->last_error_ = this->read(buf, num_bytes);
|
||||
if (this->last_error_ != i2c::ERROR_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < len; 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]) {
|
||||
ESP_LOGE(TAG, "CRC8 Checksum invalid at pos %d! 0x%02X != 0x%02X", i, buf[j + 2], crc);
|
||||
last_error_ = i2c::ERROR_CRC;
|
||||
ESP_LOGE(TAG, "CRC invalid @ %d! 0x%02X != 0x%02X", i, buf[j + 2], crc);
|
||||
this->last_error_ = i2c::ERROR_CRC;
|
||||
return false;
|
||||
}
|
||||
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
|
||||
* 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,
|
||||
uint8_t data_len) {
|
||||
const uint8_t data_len) {
|
||||
uint8_t temp_stack[BUFFER_STACK_SIZE];
|
||||
std::unique_ptr<uint8_t[]> temp_heap;
|
||||
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] >> 8;
|
||||
#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);
|
||||
return last_error_ == i2c::ERROR_OK;
|
||||
this->last_error_ = this->write(temp, raw_idx);
|
||||
return this->last_error_ == i2c::ERROR_OK;
|
||||
}
|
||||
|
||||
bool SensirionI2CDevice::get_register_(uint16_t reg, CommandLen command_len, uint16_t *data, uint8_t len,
|
||||
uint8_t delay_ms) {
|
||||
bool SensirionI2CDevice::get_register_(uint16_t reg, CommandLen command_len, uint16_t *data, const uint8_t len,
|
||||
const uint8_t delay_ms) {
|
||||
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;
|
||||
}
|
||||
delay(delay_ms);
|
||||
bool result = this->read_data(data, len);
|
||||
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;
|
||||
}
|
||||
|
||||
// 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 esphome
|
||||
|
@@ -8,10 +8,10 @@ namespace esphome {
|
||||
namespace sensirion_common {
|
||||
|
||||
/**
|
||||
* Implementation of a i2c functions for Sensirion sensors
|
||||
* Sensirion data requires crc checking.
|
||||
* Implementation of I2C functions for Sensirion sensors
|
||||
* Sensirion data requires CRC checking.
|
||||
* 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:
|
||||
* | 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:
|
||||
enum CommandLen : uint8_t { ADDR_8_BIT = 1, ADDR_16_BIT = 2 };
|
||||
|
||||
/** Read data words from i2c device.
|
||||
* handles crc check used by Sensirion sensors
|
||||
/** Read data words from I2C device.
|
||||
* handles CRC check used by Sensirion sensors
|
||||
* @param data pointer to raw result
|
||||
* @param len number of words to read
|
||||
* @return true if reading succeeded
|
||||
*/
|
||||
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
|
||||
* @return true if reading succeeded
|
||||
*/
|
||||
bool read_data(uint16_t &data) { return this->read_data(&data, 1); }
|
||||
|
||||
/** get data words from i2c register.
|
||||
* handles crc check used by Sensirion sensors
|
||||
* @param i2c register
|
||||
/** get data words from I2C register.
|
||||
* handles CRC check used by Sensirion sensors
|
||||
* @param I2C register
|
||||
* @param data pointer to raw result
|
||||
* @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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
/** Read 1 data word from 16 bit i2c register.
|
||||
* @param i2c register
|
||||
/** Read 1 data word from 16 bit I2C register.
|
||||
* @param I2C register
|
||||
* @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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/** get data words from i2c register.
|
||||
* handles crc check used by Sensirion sensors
|
||||
* @param i2c register
|
||||
/** get data words from I2C register.
|
||||
* handles CRC check used by Sensirion sensors
|
||||
* @param I2C register
|
||||
* @param data pointer to raw result
|
||||
* @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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/** Read 1 data word from 8 bit i2c register.
|
||||
* @param i2c register
|
||||
/** Read 1 data word from 8 bit I2C register.
|
||||
* @param I2C register
|
||||
* @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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/** Write a command to the i2c device.
|
||||
* @param command i2c command to send
|
||||
/** Write a command to the I2C device.
|
||||
* @param command I2C command to send
|
||||
* @return true if reading succeeded
|
||||
*/
|
||||
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 .
|
||||
* @param command i2c command to send
|
||||
* @param data argument for the i2c command
|
||||
/** Write a command and one data word to the I2C device .
|
||||
* @param command I2C command to send
|
||||
* @param data argument for the I2C command
|
||||
* @return true if reading succeeded
|
||||
*/
|
||||
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
|
||||
* @param i2c_register i2c command to send - an be uint8_t or uint16_t
|
||||
* @param data vector<uint16> arguments for the i2c command
|
||||
* @param i2c_register I2C command to send - an be uint8_t or uint16_t
|
||||
* @param data vector<uint16> arguments for the I2C command
|
||||
* @return true if reading succeeded
|
||||
*/
|
||||
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
|
||||
* @param i2c_register i2c command to send - an be uint8_t or uint16_t
|
||||
* @param data arguments for the i2c command
|
||||
* @param i2c_register I2C command to send - an be uint8_t or uint16_t
|
||||
* @param data arguments for the I2C command
|
||||
* @param len number of arguments (words)
|
||||
* @return true if reading succeeded
|
||||
*/
|
||||
template<class T> bool write_command(T i2c_register, const uint16_t *data, uint8_t len) {
|
||||
// limit to 8 or 16 bit only
|
||||
static_assert(sizeof(i2c_register) == 1 || sizeof(i2c_register) == 2,
|
||||
"only 8 or 16 bit command types are supported.");
|
||||
static_assert(sizeof(i2c_register) == 1 || sizeof(i2c_register) == 2, "Only 8 or 16 bit command types supported");
|
||||
return write_command_(i2c_register, CommandLen(sizeof(T)), data, len);
|
||||
}
|
||||
|
||||
protected:
|
||||
uint8_t crc_polynomial_{0x31u}; // default for sensirion
|
||||
/** 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 data arguments for the i2c command
|
||||
* @param data arguments for the I2C command
|
||||
* @param data_len number of arguments (words)
|
||||
* @return true if reading succeeded
|
||||
*/
|
||||
bool write_command_(uint16_t command, CommandLen command_len, const uint16_t *data, uint8_t data_len);
|
||||
|
||||
/** get data words from i2c register.
|
||||
* handles crc check used by Sensirion sensors
|
||||
* @param i2c register
|
||||
/** get data words from I2C register.
|
||||
* handles CRC check used by Sensirion sensors
|
||||
* @param I2C register
|
||||
* @param command_len either 1 for short 8 bit command or 2 for 16 bit command codes
|
||||
* @param data pointer to raw result
|
||||
* @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
|
||||
*/
|
||||
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
|
||||
* @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
|
||||
/** last error code from I2C operation
|
||||
*/
|
||||
i2c::ErrorCode last_error_;
|
||||
};
|
||||
|
Reference in New Issue
Block a user