mirror of
https://github.com/esphome/esphome.git
synced 2025-09-02 11:22:24 +01:00
[helper] Make crc8 function more flexible to avoid reimplementation in individual components (#10201)
This commit is contained in:
@@ -89,7 +89,7 @@ void AGS10Component::dump_config() {
|
|||||||
bool AGS10Component::new_i2c_address(uint8_t newaddress) {
|
bool AGS10Component::new_i2c_address(uint8_t newaddress) {
|
||||||
uint8_t rev_newaddress = ~newaddress;
|
uint8_t rev_newaddress = ~newaddress;
|
||||||
std::array<uint8_t, 5> data{newaddress, rev_newaddress, newaddress, rev_newaddress, 0};
|
std::array<uint8_t, 5> data{newaddress, rev_newaddress, newaddress, rev_newaddress, 0};
|
||||||
data[4] = calc_crc8_(data, 4);
|
data[4] = crc8(data.data(), 4, 0xFF, 0x31, true);
|
||||||
if (!this->write_bytes(REG_ADDRESS, data)) {
|
if (!this->write_bytes(REG_ADDRESS, data)) {
|
||||||
this->error_code_ = COMMUNICATION_FAILED;
|
this->error_code_ = COMMUNICATION_FAILED;
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
@@ -109,7 +109,7 @@ bool AGS10Component::set_zero_point_with_current_resistance() { return this->set
|
|||||||
|
|
||||||
bool AGS10Component::set_zero_point_with(uint16_t value) {
|
bool AGS10Component::set_zero_point_with(uint16_t value) {
|
||||||
std::array<uint8_t, 5> data{0x00, 0x0C, (uint8_t) ((value >> 8) & 0xFF), (uint8_t) (value & 0xFF), 0};
|
std::array<uint8_t, 5> data{0x00, 0x0C, (uint8_t) ((value >> 8) & 0xFF), (uint8_t) (value & 0xFF), 0};
|
||||||
data[4] = calc_crc8_(data, 4);
|
data[4] = crc8(data.data(), 4, 0xFF, 0x31, true);
|
||||||
if (!this->write_bytes(REG_CALIBRATION, data)) {
|
if (!this->write_bytes(REG_CALIBRATION, data)) {
|
||||||
this->error_code_ = COMMUNICATION_FAILED;
|
this->error_code_ = COMMUNICATION_FAILED;
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
@@ -184,7 +184,7 @@ template<size_t N> optional<std::array<uint8_t, N>> AGS10Component::read_and_che
|
|||||||
auto res = *data;
|
auto res = *data;
|
||||||
auto crc_byte = res[len];
|
auto crc_byte = res[len];
|
||||||
|
|
||||||
if (crc_byte != calc_crc8_(res, len)) {
|
if (crc_byte != crc8(res.data(), len, 0xFF, 0x31, true)) {
|
||||||
this->error_code_ = CRC_CHECK_FAILED;
|
this->error_code_ = CRC_CHECK_FAILED;
|
||||||
ESP_LOGE(TAG, "Reading AGS10 version failed: crc error!");
|
ESP_LOGE(TAG, "Reading AGS10 version failed: crc error!");
|
||||||
return optional<std::array<uint8_t, N>>();
|
return optional<std::array<uint8_t, N>>();
|
||||||
@@ -192,20 +192,5 @@ template<size_t N> optional<std::array<uint8_t, N>> AGS10Component::read_and_che
|
|||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t N> uint8_t AGS10Component::calc_crc8_(std::array<uint8_t, N> dat, uint8_t num) {
|
|
||||||
uint8_t i, byte1, crc = 0xFF;
|
|
||||||
for (byte1 = 0; byte1 < num; byte1++) {
|
|
||||||
crc ^= (dat[byte1]);
|
|
||||||
for (i = 0; i < 8; i++) {
|
|
||||||
if (crc & 0x80) {
|
|
||||||
crc = (crc << 1) ^ 0x31;
|
|
||||||
} else {
|
|
||||||
crc = (crc << 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return crc;
|
|
||||||
}
|
|
||||||
} // namespace ags10
|
} // namespace ags10
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
#include "esphome/core/automation.h"
|
#include "esphome/core/automation.h"
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/components/sensor/sensor.h"
|
|
||||||
#include "esphome/components/i2c/i2c.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ags10 {
|
namespace ags10 {
|
||||||
@@ -99,16 +99,6 @@ class AGS10Component : public PollingComponent, public i2c::I2CDevice {
|
|||||||
* Read, checks and returns data from the sensor.
|
* Read, checks and returns data from the sensor.
|
||||||
*/
|
*/
|
||||||
template<size_t N> optional<std::array<uint8_t, N>> read_and_check_(uint8_t a_register);
|
template<size_t N> optional<std::array<uint8_t, N>> read_and_check_(uint8_t a_register);
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates CRC8 value.
|
|
||||||
*
|
|
||||||
* CRC8 calculation, initial value: 0xFF, polynomial: 0x31 (x8+ x5+ x4+1)
|
|
||||||
*
|
|
||||||
* @param[in] dat the data buffer
|
|
||||||
* @param num number of bytes in the buffer
|
|
||||||
*/
|
|
||||||
template<size_t N> uint8_t calc_crc8_(std::array<uint8_t, N> dat, uint8_t num);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class AGS10NewI2cAddressAction : public Action<Ts...>, public Parented<AGS10Component> {
|
template<typename... Ts> class AGS10NewI2cAddressAction : public Action<Ts...>, public Parented<AGS10Component> {
|
||||||
|
@@ -29,22 +29,6 @@ namespace am2315c {
|
|||||||
|
|
||||||
static const char *const TAG = "am2315c";
|
static const char *const TAG = "am2315c";
|
||||||
|
|
||||||
uint8_t AM2315C::crc8_(uint8_t *data, uint8_t len) {
|
|
||||||
uint8_t crc = 0xFF;
|
|
||||||
while (len--) {
|
|
||||||
crc ^= *data++;
|
|
||||||
for (uint8_t i = 0; i < 8; i++) {
|
|
||||||
if (crc & 0x80) {
|
|
||||||
crc <<= 1;
|
|
||||||
crc ^= 0x31;
|
|
||||||
} else {
|
|
||||||
crc <<= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return crc;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AM2315C::reset_register_(uint8_t reg) {
|
bool AM2315C::reset_register_(uint8_t reg) {
|
||||||
// code based on demo code sent by www.aosong.com
|
// code based on demo code sent by www.aosong.com
|
||||||
// no further documentation.
|
// no further documentation.
|
||||||
@@ -86,7 +70,7 @@ bool AM2315C::convert_(uint8_t *data, float &humidity, float &temperature) {
|
|||||||
humidity = raw * 9.5367431640625e-5;
|
humidity = raw * 9.5367431640625e-5;
|
||||||
raw = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
|
raw = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
|
||||||
temperature = raw * 1.9073486328125e-4 - 50;
|
temperature = raw * 1.9073486328125e-4 - 50;
|
||||||
return this->crc8_(data, 6) == data[6];
|
return crc8(data, 6, 0xFF, 0x31, true) == data[6];
|
||||||
}
|
}
|
||||||
|
|
||||||
void AM2315C::setup() {
|
void AM2315C::setup() {
|
||||||
|
@@ -21,9 +21,9 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/components/sensor/sensor.h"
|
|
||||||
#include "esphome/components/i2c/i2c.h"
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace am2315c {
|
namespace am2315c {
|
||||||
@@ -39,7 +39,6 @@ class AM2315C : public PollingComponent, public i2c::I2CDevice {
|
|||||||
void set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; }
|
void set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint8_t crc8_(uint8_t *data, uint8_t len);
|
|
||||||
bool convert_(uint8_t *data, float &humidity, float &temperature);
|
bool convert_(uint8_t *data, float &humidity, float &temperature);
|
||||||
bool reset_register_(uint8_t reg);
|
bool reset_register_(uint8_t reg);
|
||||||
|
|
||||||
|
@@ -12,7 +12,7 @@ void HTE501Component::setup() {
|
|||||||
this->write(address, 2, false);
|
this->write(address, 2, false);
|
||||||
uint8_t identification[9];
|
uint8_t identification[9];
|
||||||
this->read(identification, 9);
|
this->read(identification, 9);
|
||||||
if (identification[8] != calc_crc8_(identification, 0, 7)) {
|
if (identification[8] != crc8(identification, 8, 0xFF, 0x31, true)) {
|
||||||
this->error_code_ = CRC_CHECK_FAILED;
|
this->error_code_ = CRC_CHECK_FAILED;
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
@@ -46,7 +46,8 @@ void HTE501Component::update() {
|
|||||||
this->set_timeout(50, [this]() {
|
this->set_timeout(50, [this]() {
|
||||||
uint8_t i2c_response[6];
|
uint8_t i2c_response[6];
|
||||||
this->read(i2c_response, 6);
|
this->read(i2c_response, 6);
|
||||||
if (i2c_response[2] != calc_crc8_(i2c_response, 0, 1) && i2c_response[5] != calc_crc8_(i2c_response, 3, 4)) {
|
if (i2c_response[2] != crc8(i2c_response, 2, 0xFF, 0x31, true) &&
|
||||||
|
i2c_response[5] != crc8(i2c_response + 3, 2, 0xFF, 0x31, true)) {
|
||||||
this->error_code_ = CRC_CHECK_FAILED;
|
this->error_code_ = CRC_CHECK_FAILED;
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
return;
|
return;
|
||||||
@@ -67,24 +68,5 @@ void HTE501Component::update() {
|
|||||||
this->status_clear_warning();
|
this->status_clear_warning();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char HTE501Component::calc_crc8_(const unsigned char buf[], unsigned char from, unsigned char to) {
|
|
||||||
unsigned char crc_val = 0xFF;
|
|
||||||
unsigned char i = 0;
|
|
||||||
unsigned char j = 0;
|
|
||||||
for (i = from; i <= to; i++) {
|
|
||||||
int cur_val = buf[i];
|
|
||||||
for (j = 0; j < 8; j++) {
|
|
||||||
if (((crc_val ^ cur_val) & 0x80) != 0) // If MSBs are not equal
|
|
||||||
{
|
|
||||||
crc_val = ((crc_val << 1) ^ 0x31);
|
|
||||||
} else {
|
|
||||||
crc_val = (crc_val << 1);
|
|
||||||
}
|
|
||||||
cur_val = cur_val << 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return crc_val;
|
|
||||||
}
|
|
||||||
} // namespace hte501
|
} // namespace hte501
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/components/sensor/sensor.h"
|
|
||||||
#include "esphome/components/i2c/i2c.h"
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace hte501 {
|
namespace hte501 {
|
||||||
@@ -19,7 +19,6 @@ class HTE501Component : public PollingComponent, public i2c::I2CDevice {
|
|||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
unsigned char calc_crc8_(const unsigned char buf[], unsigned char from, unsigned char to);
|
|
||||||
sensor::Sensor *temperature_sensor_;
|
sensor::Sensor *temperature_sensor_;
|
||||||
sensor::Sensor *humidity_sensor_;
|
sensor::Sensor *humidity_sensor_;
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
#include "esphome/core/log.h"
|
|
||||||
#include "lc709203f.h"
|
#include "lc709203f.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace lc709203f {
|
namespace lc709203f {
|
||||||
@@ -189,7 +190,7 @@ uint8_t Lc709203f::get_register_(uint8_t register_to_read, uint16_t *register_va
|
|||||||
// Error on the i2c bus
|
// Error on the i2c bus
|
||||||
this->status_set_warning(
|
this->status_set_warning(
|
||||||
str_sprintf("Error code %d when reading from register 0x%02X", return_code, register_to_read).c_str());
|
str_sprintf("Error code %d when reading from register 0x%02X", return_code, register_to_read).c_str());
|
||||||
} else if (this->crc8_(read_buffer, 5) != read_buffer[5]) {
|
} else if (crc8(read_buffer, 5, 0x00, 0x07, true) != read_buffer[5]) {
|
||||||
// I2C indicated OK, but the CRC of the data does not matcth.
|
// I2C indicated OK, but the CRC of the data does not matcth.
|
||||||
this->status_set_warning(str_sprintf("CRC error reading from register 0x%02X", register_to_read).c_str());
|
this->status_set_warning(str_sprintf("CRC error reading from register 0x%02X", register_to_read).c_str());
|
||||||
} else {
|
} else {
|
||||||
@@ -220,7 +221,7 @@ uint8_t Lc709203f::set_register_(uint8_t register_to_set, uint16_t value_to_set)
|
|||||||
write_buffer[1] = register_to_set;
|
write_buffer[1] = register_to_set;
|
||||||
write_buffer[2] = value_to_set & 0xFF; // Low byte
|
write_buffer[2] = value_to_set & 0xFF; // Low byte
|
||||||
write_buffer[3] = (value_to_set >> 8) & 0xFF; // High byte
|
write_buffer[3] = (value_to_set >> 8) & 0xFF; // High byte
|
||||||
write_buffer[4] = this->crc8_(write_buffer, 4);
|
write_buffer[4] = crc8(write_buffer, 4, 0x00, 0x07, true);
|
||||||
|
|
||||||
for (uint8_t i = 0; i <= LC709203F_I2C_RETRY_COUNT; i++) {
|
for (uint8_t i = 0; i <= LC709203F_I2C_RETRY_COUNT; i++) {
|
||||||
// Note: we don't write the first byte of the write buffer to the device.
|
// Note: we don't write the first byte of the write buffer to the device.
|
||||||
@@ -239,20 +240,6 @@ uint8_t Lc709203f::set_register_(uint8_t register_to_set, uint16_t value_to_set)
|
|||||||
return return_code;
|
return return_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t Lc709203f::crc8_(uint8_t *byte_buffer, uint8_t length_of_crc) {
|
|
||||||
uint8_t crc = 0x00;
|
|
||||||
const uint8_t polynomial(0x07);
|
|
||||||
|
|
||||||
for (uint8_t j = length_of_crc; j; --j) {
|
|
||||||
crc ^= *byte_buffer++;
|
|
||||||
|
|
||||||
for (uint8_t i = 8; i; --i) {
|
|
||||||
crc = (crc & 0x80) ? (crc << 1) ^ polynomial : (crc << 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return crc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lc709203f::set_pack_size(uint16_t pack_size) {
|
void Lc709203f::set_pack_size(uint16_t pack_size) {
|
||||||
static const uint16_t PACK_SIZE_ARRAY[6] = {100, 200, 500, 1000, 2000, 3000};
|
static const uint16_t PACK_SIZE_ARRAY[6] = {100, 200, 500, 1000, 2000, 3000};
|
||||||
static const uint16_t APA_ARRAY[6] = {0x08, 0x0B, 0x10, 0x19, 0x2D, 0x36};
|
static const uint16_t APA_ARRAY[6] = {0x08, 0x0B, 0x10, 0x19, 0x2D, 0x36};
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/components/sensor/sensor.h"
|
|
||||||
#include "esphome/components/i2c/i2c.h"
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace lc709203f {
|
namespace lc709203f {
|
||||||
@@ -38,7 +38,6 @@ class Lc709203f : public sensor::Sensor, public PollingComponent, public i2c::I2
|
|||||||
private:
|
private:
|
||||||
uint8_t get_register_(uint8_t register_to_read, uint16_t *register_value);
|
uint8_t get_register_(uint8_t register_to_read, uint16_t *register_value);
|
||||||
uint8_t set_register_(uint8_t register_to_set, uint16_t value_to_set);
|
uint8_t set_register_(uint8_t register_to_set, uint16_t value_to_set);
|
||||||
uint8_t crc8_(uint8_t *byte_buffer, uint8_t length_of_crc);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
sensor::Sensor *voltage_sensor_{nullptr};
|
sensor::Sensor *voltage_sensor_{nullptr};
|
||||||
|
@@ -50,28 +50,13 @@ bool MLX90614Component::write_emissivity_() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t MLX90614Component::crc8_pec_(const uint8_t *data, uint8_t len) {
|
|
||||||
uint8_t crc = 0;
|
|
||||||
for (uint8_t i = 0; i < len; i++) {
|
|
||||||
uint8_t in = data[i];
|
|
||||||
for (uint8_t j = 0; j < 8; j++) {
|
|
||||||
bool carry = (crc ^ in) & 0x80;
|
|
||||||
crc <<= 1;
|
|
||||||
if (carry)
|
|
||||||
crc ^= 0x07;
|
|
||||||
in <<= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return crc;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MLX90614Component::write_bytes_(uint8_t reg, uint16_t data) {
|
bool MLX90614Component::write_bytes_(uint8_t reg, uint16_t data) {
|
||||||
uint8_t buf[5];
|
uint8_t buf[5];
|
||||||
buf[0] = this->address_ << 1;
|
buf[0] = this->address_ << 1;
|
||||||
buf[1] = reg;
|
buf[1] = reg;
|
||||||
buf[2] = data & 0xFF;
|
buf[2] = data & 0xFF;
|
||||||
buf[3] = data >> 8;
|
buf[3] = data >> 8;
|
||||||
buf[4] = this->crc8_pec_(buf, 4);
|
buf[4] = crc8(buf, 4, 0x00, 0x07, true);
|
||||||
return this->write_bytes(reg, buf + 2, 3);
|
return this->write_bytes(reg, buf + 2, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -22,7 +22,6 @@ class MLX90614Component : public PollingComponent, public i2c::I2CDevice {
|
|||||||
protected:
|
protected:
|
||||||
bool write_emissivity_();
|
bool write_emissivity_();
|
||||||
|
|
||||||
uint8_t crc8_pec_(const uint8_t *data, uint8_t len);
|
|
||||||
bool write_bytes_(uint8_t reg, uint16_t data);
|
bool write_bytes_(uint8_t reg, uint16_t data);
|
||||||
|
|
||||||
sensor::Sensor *ambient_sensor_{nullptr};
|
sensor::Sensor *ambient_sensor_{nullptr};
|
||||||
|
@@ -12,7 +12,7 @@ void TEE501Component::setup() {
|
|||||||
this->write(address, 2, false);
|
this->write(address, 2, false);
|
||||||
uint8_t identification[9];
|
uint8_t identification[9];
|
||||||
this->read(identification, 9);
|
this->read(identification, 9);
|
||||||
if (identification[8] != calc_crc8_(identification, 0, 7)) {
|
if (identification[8] != crc8(identification, 8, 0xFF, 0x31, true)) {
|
||||||
this->error_code_ = CRC_CHECK_FAILED;
|
this->error_code_ = CRC_CHECK_FAILED;
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
@@ -45,7 +45,7 @@ void TEE501Component::update() {
|
|||||||
this->set_timeout(50, [this]() {
|
this->set_timeout(50, [this]() {
|
||||||
uint8_t i2c_response[3];
|
uint8_t i2c_response[3];
|
||||||
this->read(i2c_response, 3);
|
this->read(i2c_response, 3);
|
||||||
if (i2c_response[2] != calc_crc8_(i2c_response, 0, 1)) {
|
if (i2c_response[2] != crc8(i2c_response, 2, 0xFF, 0x31, true)) {
|
||||||
this->error_code_ = CRC_CHECK_FAILED;
|
this->error_code_ = CRC_CHECK_FAILED;
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
return;
|
return;
|
||||||
@@ -62,24 +62,5 @@ void TEE501Component::update() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char TEE501Component::calc_crc8_(const unsigned char buf[], unsigned char from, unsigned char to) {
|
|
||||||
unsigned char crc_val = 0xFF;
|
|
||||||
unsigned char i = 0;
|
|
||||||
unsigned char j = 0;
|
|
||||||
for (i = from; i <= to; i++) {
|
|
||||||
int cur_val = buf[i];
|
|
||||||
for (j = 0; j < 8; j++) {
|
|
||||||
if (((crc_val ^ cur_val) & 0x80) != 0) // If MSBs are not equal
|
|
||||||
{
|
|
||||||
crc_val = ((crc_val << 1) ^ 0x31);
|
|
||||||
} else {
|
|
||||||
crc_val = (crc_val << 1);
|
|
||||||
}
|
|
||||||
cur_val = cur_val << 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return crc_val;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace tee501
|
} // namespace tee501
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/components/sensor/sensor.h"
|
|
||||||
#include "esphome/components/i2c/i2c.h"
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace tee501 {
|
namespace tee501 {
|
||||||
@@ -16,8 +16,6 @@ class TEE501Component : public sensor::Sensor, public PollingComponent, public i
|
|||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
unsigned char calc_crc8_(const unsigned char buf[], unsigned char from, unsigned char to);
|
|
||||||
|
|
||||||
enum ErrorCode { NONE = 0, COMMUNICATION_FAILED, CRC_CHECK_FAILED } error_code_{NONE};
|
enum ErrorCode { NONE = 0, COMMUNICATION_FAILED, CRC_CHECK_FAILED } error_code_{NONE};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -41,17 +41,28 @@ static const uint16_t CRC16_1021_BE_LUT_H[] = {0x0000, 0x1231, 0x2462, 0x3653, 0
|
|||||||
|
|
||||||
// Mathematics
|
// Mathematics
|
||||||
|
|
||||||
uint8_t crc8(const uint8_t *data, uint8_t len) {
|
uint8_t crc8(const uint8_t *data, uint8_t len, uint8_t crc, uint8_t poly, bool msb_first) {
|
||||||
uint8_t crc = 0;
|
|
||||||
|
|
||||||
while ((len--) != 0u) {
|
while ((len--) != 0u) {
|
||||||
uint8_t inbyte = *data++;
|
uint8_t inbyte = *data++;
|
||||||
for (uint8_t i = 8; i != 0u; i--) {
|
if (msb_first) {
|
||||||
bool mix = (crc ^ inbyte) & 0x01;
|
// MSB first processing (for polynomials like 0x31, 0x07)
|
||||||
crc >>= 1;
|
crc ^= inbyte;
|
||||||
if (mix)
|
for (uint8_t i = 8; i != 0u; i--) {
|
||||||
crc ^= 0x8C;
|
if (crc & 0x80) {
|
||||||
inbyte >>= 1;
|
crc = (crc << 1) ^ poly;
|
||||||
|
} else {
|
||||||
|
crc <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// LSB first processing (default for Dallas/Maxim 0x8C)
|
||||||
|
for (uint8_t i = 8; i != 0u; i--) {
|
||||||
|
bool mix = (crc ^ inbyte) & 0x01;
|
||||||
|
crc >>= 1;
|
||||||
|
if (mix)
|
||||||
|
crc ^= poly;
|
||||||
|
inbyte >>= 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return crc;
|
return crc;
|
||||||
|
@@ -145,8 +145,8 @@ template<typename T, typename U> T remap(U value, U min, U max, T min_out, T max
|
|||||||
return (value - min) * (max_out - min_out) / (max - min) + min_out;
|
return (value - min) * (max_out - min_out) / (max - min) + min_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate a CRC-8 checksum of \p data with size \p len using the CRC-8-Dallas/Maxim polynomial.
|
/// Calculate a CRC-8 checksum of \p data with size \p len.
|
||||||
uint8_t crc8(const uint8_t *data, uint8_t len);
|
uint8_t crc8(const uint8_t *data, uint8_t len, uint8_t crc = 0x00, uint8_t poly = 0x8C, bool msb_first = false);
|
||||||
|
|
||||||
/// Calculate a CRC-16 checksum of \p data with size \p len.
|
/// Calculate a CRC-16 checksum of \p data with size \p len.
|
||||||
uint16_t crc16(const uint8_t *data, uint16_t len, uint16_t crc = 0xffff, uint16_t reverse_poly = 0xa001,
|
uint16_t crc16(const uint8_t *data, uint16_t len, uint16_t crc = 0xffff, uint16_t reverse_poly = 0xa001,
|
||||||
|
17
tests/integration/fixtures/crc8_helper.yaml
Normal file
17
tests/integration/fixtures/crc8_helper.yaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
esphome:
|
||||||
|
name: crc8-helper-test
|
||||||
|
|
||||||
|
host:
|
||||||
|
|
||||||
|
api:
|
||||||
|
|
||||||
|
logger:
|
||||||
|
level: INFO
|
||||||
|
|
||||||
|
external_components:
|
||||||
|
- source:
|
||||||
|
type: local
|
||||||
|
path: EXTERNAL_COMPONENT_PATH
|
||||||
|
components: [crc8_test_component]
|
||||||
|
|
||||||
|
crc8_test_component:
|
@@ -0,0 +1,17 @@
|
|||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
|
crc8_test_component_ns = cg.esphome_ns.namespace("crc8_test_component")
|
||||||
|
CRC8TestComponent = crc8_test_component_ns.class_("CRC8TestComponent", cg.Component)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(CRC8TestComponent),
|
||||||
|
}
|
||||||
|
).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
@@ -0,0 +1,170 @@
|
|||||||
|
#include "crc8_test_component.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace crc8_test_component {
|
||||||
|
|
||||||
|
static const char *const TAG = "crc8_test";
|
||||||
|
|
||||||
|
void CRC8TestComponent::setup() {
|
||||||
|
ESP_LOGI(TAG, "CRC8 Helper Function Integration Test Starting");
|
||||||
|
|
||||||
|
// Run all test suites
|
||||||
|
test_crc8_dallas_maxim();
|
||||||
|
test_crc8_sensirion_style();
|
||||||
|
test_crc8_pec_style();
|
||||||
|
test_crc8_parameter_equivalence();
|
||||||
|
test_crc8_edge_cases();
|
||||||
|
test_component_compatibility();
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "CRC8 Integration Test Complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRC8TestComponent::test_crc8_dallas_maxim() {
|
||||||
|
ESP_LOGI(TAG, "Testing Dallas/Maxim CRC8 (default parameters)");
|
||||||
|
|
||||||
|
// Test vectors for Dallas/Maxim CRC8 (polynomial 0x8C, LSB-first, init 0x00)
|
||||||
|
const uint8_t test1[] = {0x01};
|
||||||
|
const uint8_t test2[] = {0xFF};
|
||||||
|
const uint8_t test3[] = {0x12, 0x34};
|
||||||
|
const uint8_t test4[] = {0xAA, 0xBB, 0xCC};
|
||||||
|
const uint8_t test5[] = {0x01, 0x02, 0x03, 0x04, 0x05};
|
||||||
|
|
||||||
|
bool all_passed = true;
|
||||||
|
all_passed &= verify_crc8("Dallas [0x01]", test1, sizeof(test1), 0x5E);
|
||||||
|
all_passed &= verify_crc8("Dallas [0xFF]", test2, sizeof(test2), 0x35);
|
||||||
|
all_passed &= verify_crc8("Dallas [0x12, 0x34]", test3, sizeof(test3), 0xA2);
|
||||||
|
all_passed &= verify_crc8("Dallas [0xAA, 0xBB, 0xCC]", test4, sizeof(test4), 0xD4);
|
||||||
|
all_passed &= verify_crc8("Dallas [0x01...0x05]", test5, sizeof(test5), 0x2A);
|
||||||
|
|
||||||
|
log_test_result("Dallas/Maxim CRC8", all_passed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRC8TestComponent::test_crc8_sensirion_style() {
|
||||||
|
ESP_LOGI(TAG, "Testing Sensirion CRC8 (0x31 poly, MSB-first, init 0xFF)");
|
||||||
|
|
||||||
|
const uint8_t test1[] = {0x00};
|
||||||
|
const uint8_t test2[] = {0x01};
|
||||||
|
const uint8_t test3[] = {0xFF};
|
||||||
|
const uint8_t test4[] = {0x12, 0x34};
|
||||||
|
const uint8_t test5[] = {0xBE, 0xEF};
|
||||||
|
|
||||||
|
bool all_passed = true;
|
||||||
|
all_passed &= verify_crc8("Sensirion [0x00]", test1, sizeof(test1), 0xAC, 0xFF, 0x31, true);
|
||||||
|
all_passed &= verify_crc8("Sensirion [0x01]", test2, sizeof(test2), 0x9D, 0xFF, 0x31, true);
|
||||||
|
all_passed &= verify_crc8("Sensirion [0xFF]", test3, sizeof(test3), 0x00, 0xFF, 0x31, true);
|
||||||
|
all_passed &= verify_crc8("Sensirion [0x12, 0x34]", test4, sizeof(test4), 0x37, 0xFF, 0x31, true);
|
||||||
|
all_passed &= verify_crc8("Sensirion [0xBE, 0xEF]", test5, sizeof(test5), 0x92, 0xFF, 0x31, true);
|
||||||
|
|
||||||
|
log_test_result("Sensirion CRC8", all_passed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRC8TestComponent::test_crc8_pec_style() {
|
||||||
|
ESP_LOGI(TAG, "Testing PEC CRC8 (0x07 poly, MSB-first, init 0x00)");
|
||||||
|
|
||||||
|
const uint8_t test1[] = {0x00};
|
||||||
|
const uint8_t test2[] = {0x01};
|
||||||
|
const uint8_t test3[] = {0xFF};
|
||||||
|
const uint8_t test4[] = {0x12, 0x34};
|
||||||
|
const uint8_t test5[] = {0xAA, 0xBB};
|
||||||
|
|
||||||
|
bool all_passed = true;
|
||||||
|
all_passed &= verify_crc8("PEC [0x00]", test1, sizeof(test1), 0x00, 0x00, 0x07, true);
|
||||||
|
all_passed &= verify_crc8("PEC [0x01]", test2, sizeof(test2), 0x07, 0x00, 0x07, true);
|
||||||
|
all_passed &= verify_crc8("PEC [0xFF]", test3, sizeof(test3), 0xF3, 0x00, 0x07, true);
|
||||||
|
all_passed &= verify_crc8("PEC [0x12, 0x34]", test4, sizeof(test4), 0xF1, 0x00, 0x07, true);
|
||||||
|
all_passed &= verify_crc8("PEC [0xAA, 0xBB]", test5, sizeof(test5), 0xB2, 0x00, 0x07, true);
|
||||||
|
|
||||||
|
log_test_result("PEC CRC8", all_passed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRC8TestComponent::test_crc8_parameter_equivalence() {
|
||||||
|
ESP_LOGI(TAG, "Testing parameter equivalence");
|
||||||
|
|
||||||
|
const uint8_t test_data[] = {0x12, 0x34, 0x56, 0x78};
|
||||||
|
|
||||||
|
// Test that default parameters work as expected
|
||||||
|
uint8_t default_result = crc8(test_data, sizeof(test_data));
|
||||||
|
uint8_t explicit_result = crc8(test_data, sizeof(test_data), 0x00, 0x8C, false);
|
||||||
|
|
||||||
|
bool passed = (default_result == explicit_result);
|
||||||
|
if (!passed) {
|
||||||
|
ESP_LOGE(TAG, "Parameter equivalence FAILED: default=0x%02X, explicit=0x%02X", default_result, explicit_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_test_result("Parameter equivalence", passed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRC8TestComponent::test_crc8_edge_cases() {
|
||||||
|
ESP_LOGI(TAG, "Testing edge cases");
|
||||||
|
|
||||||
|
bool all_passed = true;
|
||||||
|
|
||||||
|
// Empty array test
|
||||||
|
const uint8_t empty[] = {};
|
||||||
|
uint8_t empty_result = crc8(empty, 0);
|
||||||
|
bool empty_passed = (empty_result == 0x00); // Should return init value
|
||||||
|
if (!empty_passed) {
|
||||||
|
ESP_LOGE(TAG, "Empty array test FAILED: expected 0x00, got 0x%02X", empty_result);
|
||||||
|
}
|
||||||
|
all_passed &= empty_passed;
|
||||||
|
|
||||||
|
// Single byte tests
|
||||||
|
const uint8_t single_zero[] = {0x00};
|
||||||
|
const uint8_t single_ff[] = {0xFF};
|
||||||
|
all_passed &= verify_crc8("Single [0x00]", single_zero, 1, 0x00);
|
||||||
|
all_passed &= verify_crc8("Single [0xFF]", single_ff, 1, 0x35);
|
||||||
|
|
||||||
|
log_test_result("Edge cases", all_passed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRC8TestComponent::test_component_compatibility() {
|
||||||
|
ESP_LOGI(TAG, "Testing component compatibility");
|
||||||
|
|
||||||
|
// Test specific component use cases
|
||||||
|
bool all_passed = true;
|
||||||
|
|
||||||
|
// AGS10-style data (Sensirion CRC8)
|
||||||
|
const uint8_t ags10_data[] = {0x12, 0x34, 0x56};
|
||||||
|
uint8_t ags10_result = crc8(ags10_data, sizeof(ags10_data), 0xFF, 0x31, true);
|
||||||
|
ESP_LOGI(TAG, "AGS10-style CRC8: 0x%02X", ags10_result);
|
||||||
|
|
||||||
|
// LC709203F-style data (PEC CRC8)
|
||||||
|
const uint8_t lc_data[] = {0xAA, 0xBB};
|
||||||
|
uint8_t lc_result = crc8(lc_data, sizeof(lc_data), 0x00, 0x07, true);
|
||||||
|
ESP_LOGI(TAG, "LC709203F-style CRC8: 0x%02X", lc_result);
|
||||||
|
|
||||||
|
// DallasTemperature-style data (Dallas CRC8)
|
||||||
|
const uint8_t dallas_data[] = {0x28, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC};
|
||||||
|
uint8_t dallas_result = crc8(dallas_data, sizeof(dallas_data));
|
||||||
|
ESP_LOGI(TAG, "Dallas-style CRC8: 0x%02X", dallas_result);
|
||||||
|
|
||||||
|
all_passed = true; // These are just demonstration tests
|
||||||
|
log_test_result("Component compatibility", all_passed);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CRC8TestComponent::verify_crc8(const char *test_name, const uint8_t *data, uint8_t len, uint8_t expected,
|
||||||
|
uint8_t crc, uint8_t poly, bool msb_first) {
|
||||||
|
uint8_t result = esphome::crc8(data, len, crc, poly, msb_first);
|
||||||
|
bool passed = (result == expected);
|
||||||
|
|
||||||
|
if (passed) {
|
||||||
|
ESP_LOGI(TAG, "%s: PASS (0x%02X)", test_name, result);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "%s: FAIL - expected 0x%02X, got 0x%02X", test_name, expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return passed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRC8TestComponent::log_test_result(const char *test_name, bool passed) {
|
||||||
|
if (passed) {
|
||||||
|
ESP_LOGI(TAG, "%s: ALL TESTS PASSED", test_name);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "%s: SOME TESTS FAILED", test_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace crc8_test_component
|
||||||
|
} // namespace esphome
|
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace crc8_test_component {
|
||||||
|
|
||||||
|
class CRC8TestComponent : public Component {
|
||||||
|
public:
|
||||||
|
void setup() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void test_crc8_dallas_maxim();
|
||||||
|
void test_crc8_sensirion_style();
|
||||||
|
void test_crc8_pec_style();
|
||||||
|
void test_crc8_parameter_equivalence();
|
||||||
|
void test_crc8_edge_cases();
|
||||||
|
void test_component_compatibility();
|
||||||
|
void test_old_vs_new_implementations();
|
||||||
|
|
||||||
|
void log_test_result(const char *test_name, bool passed);
|
||||||
|
bool verify_crc8(const char *test_name, const uint8_t *data, uint8_t len, uint8_t expected, uint8_t crc = 0x00,
|
||||||
|
uint8_t poly = 0x8C, bool msb_first = false);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace crc8_test_component
|
||||||
|
} // namespace esphome
|
100
tests/integration/test_crc8_helper.py
Normal file
100
tests/integration/test_crc8_helper.py
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
"""Integration test for CRC8 helper function."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from .types import APIClientConnectedFactory, RunCompiledFunction
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_crc8_helper(
|
||||||
|
yaml_config: str,
|
||||||
|
run_compiled: RunCompiledFunction,
|
||||||
|
api_client_connected: APIClientConnectedFactory,
|
||||||
|
) -> None:
|
||||||
|
"""Test the CRC8 helper function through integration testing."""
|
||||||
|
# Get the path to the external components directory
|
||||||
|
external_components_path = str(
|
||||||
|
Path(__file__).parent / "fixtures" / "external_components"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Replace the placeholder in the YAML config with the actual path
|
||||||
|
yaml_config = yaml_config.replace(
|
||||||
|
"EXTERNAL_COMPONENT_PATH", external_components_path
|
||||||
|
)
|
||||||
|
|
||||||
|
# Track test completion with asyncio.Event
|
||||||
|
test_complete = asyncio.Event()
|
||||||
|
|
||||||
|
# Track test results
|
||||||
|
test_results = {
|
||||||
|
"dallas_maxim": False,
|
||||||
|
"sensirion": False,
|
||||||
|
"pec": False,
|
||||||
|
"parameter_equivalence": False,
|
||||||
|
"edge_cases": False,
|
||||||
|
"component_compatibility": False,
|
||||||
|
"setup_started": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
def on_log_line(line):
|
||||||
|
"""Process log lines to track test progress and results."""
|
||||||
|
# Track test start
|
||||||
|
if "CRC8 Helper Function Integration Test Starting" in line:
|
||||||
|
test_results["setup_started"] = True
|
||||||
|
|
||||||
|
# Track test completion
|
||||||
|
elif "CRC8 Integration Test Complete" in line:
|
||||||
|
test_complete.set()
|
||||||
|
|
||||||
|
# Track individual test results
|
||||||
|
elif "ALL TESTS PASSED" in line:
|
||||||
|
if "Dallas/Maxim CRC8" in line:
|
||||||
|
test_results["dallas_maxim"] = True
|
||||||
|
elif "Sensirion CRC8" in line:
|
||||||
|
test_results["sensirion"] = True
|
||||||
|
elif "PEC CRC8" in line:
|
||||||
|
test_results["pec"] = True
|
||||||
|
elif "Parameter equivalence" in line:
|
||||||
|
test_results["parameter_equivalence"] = True
|
||||||
|
elif "Edge cases" in line:
|
||||||
|
test_results["edge_cases"] = True
|
||||||
|
elif "Component compatibility" in line:
|
||||||
|
test_results["component_compatibility"] = True
|
||||||
|
|
||||||
|
# Log failures for debugging
|
||||||
|
elif "TEST FAILED:" in line or "SUBTEST FAILED:" in line:
|
||||||
|
print(f"CRC8 Test Failure: {line}")
|
||||||
|
|
||||||
|
# Compile and run the test
|
||||||
|
async with (
|
||||||
|
run_compiled(yaml_config, line_callback=on_log_line),
|
||||||
|
api_client_connected() as client,
|
||||||
|
):
|
||||||
|
# Verify device info
|
||||||
|
device_info = await client.device_info()
|
||||||
|
assert device_info is not None
|
||||||
|
assert device_info.name == "crc8-helper-test"
|
||||||
|
|
||||||
|
# Wait for tests to complete with timeout
|
||||||
|
try:
|
||||||
|
await asyncio.wait_for(test_complete.wait(), timeout=5.0)
|
||||||
|
except TimeoutError:
|
||||||
|
pytest.fail("CRC8 integration test timed out after 5 seconds")
|
||||||
|
|
||||||
|
# Verify all tests passed
|
||||||
|
assert test_results["setup_started"], "CRC8 test setup never started"
|
||||||
|
assert test_results["dallas_maxim"], "Dallas/Maxim CRC8 test failed"
|
||||||
|
assert test_results["sensirion"], "Sensirion CRC8 test failed"
|
||||||
|
assert test_results["pec"], "PEC CRC8 test failed"
|
||||||
|
assert test_results["parameter_equivalence"], (
|
||||||
|
"Parameter equivalence test failed"
|
||||||
|
)
|
||||||
|
assert test_results["edge_cases"], "Edge cases test failed"
|
||||||
|
assert test_results["component_compatibility"], (
|
||||||
|
"Component compatibility test failed"
|
||||||
|
)
|
Reference in New Issue
Block a user