From c0a693dfce2253565aaef43786aaa5277b85726e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 17 Oct 2026 03:18:24 -1000 Subject: [PATCH] [components] Use xor_bytes and sum16_bytes for checksums (#20922) --- esphome/components/apc1/apc1.cpp | 13 ++---------- esphome/components/dfplayer/dfplayer.cpp | 5 +---- esphome/components/ld2420/ld2420.cpp | 9 ++------- esphome/components/ld6002b/ld6002b.cpp | 8 ++------ esphome/components/pmsa003i/pmsa003i.cpp | 5 +---- esphome/components/pmsx003/pmsx003.cpp | 10 ++-------- esphome/components/rdm6300/rdm6300.cpp | 4 +--- .../seeed_mr60fda2/seeed_mr60fda2.cpp | 20 ++----------------- .../components/shelly_dimmer/stm32flash.cpp | 14 ++++--------- esphome/components/toshiba/toshiba.cpp | 9 ++------- .../components/zwave_proxy/zwave_proxy.cpp | 10 ++-------- 11 files changed, 21 insertions(+), 86 deletions(-) diff --git a/esphome/components/apc1/apc1.cpp b/esphome/components/apc1/apc1.cpp index beca9d85815..dee4bd8d73b 100644 --- a/esphome/components/apc1/apc1.cpp +++ b/esphome/components/apc1/apc1.cpp @@ -28,15 +28,6 @@ static constexpr uint32_t APC1_NO_DATA_TIMEOUT_MS = 15000; // Drop a partial frame when the bytes stop mid-way static constexpr uint32_t APC1_RX_IDLE_TIMEOUT_MS = 200; -// Frames and commands end with the 16-bit sum of every byte before it -static uint16_t apc1_checksum(const uint8_t *data, size_t len) { - uint16_t sum = 0; - for (size_t i = 0; i < len; i++) { - sum += data[i]; - } - return sum; -} - void APC1Component::setup() { if (this->set_pin_ != nullptr) { this->set_pin_->setup(); @@ -157,7 +148,7 @@ void APC1Component::loop() { void APC1Component::parse_frame_(uint16_t total_len) { uint16_t expected_checksum = encode_uint16(this->rx_buffer_[total_len - 2], this->rx_buffer_[total_len - 1]); - uint16_t calculated_checksum = apc1_checksum(this->rx_buffer_.data(), total_len - 2); + uint16_t calculated_checksum = sum16_bytes(this->rx_buffer_.data(), total_len - 2); if (expected_checksum != calculated_checksum) { ESP_LOGW(TAG, "APC1 checksum mismatch: calculated 0x%04X != expected 0x%04X", calculated_checksum, expected_checksum); @@ -343,7 +334,7 @@ void APC1Component::send_command_(APC1Command cmd, uint16_t data) { 0, 0, }; - uint16_t checksum = apc1_checksum(buf.data(), 5); + uint16_t checksum = sum16_bytes(buf.data(), 5); buf[5] = static_cast((checksum >> 8) & 0xFF); buf[6] = static_cast(checksum & 0xFF); this->write_array(buf); diff --git a/esphome/components/dfplayer/dfplayer.cpp b/esphome/components/dfplayer/dfplayer.cpp index c45e895b385..4b031274d26 100644 --- a/esphome/components/dfplayer/dfplayer.cpp +++ b/esphome/components/dfplayer/dfplayer.cpp @@ -124,10 +124,7 @@ void DFPlayer::play_folder(uint16_t folder, uint16_t file) { void DFPlayer::send_cmd_(uint8_t cmd, uint16_t argument) { uint8_t buffer[10]{0x7e, 0xff, 0x06, cmd, 0x01, (uint8_t) (argument >> 8), (uint8_t) argument, 0x00, 0x00, 0xef}; - uint16_t checksum = 0; - for (uint8_t i = 1; i < 7; i++) - checksum += buffer[i]; - checksum = -checksum; + const uint16_t checksum = static_cast(-sum16_bytes(buffer + 1, 6)); buffer[7] = checksum >> 8; buffer[8] = (uint8_t) checksum; diff --git a/esphome/components/ld2420/ld2420.cpp b/esphome/components/ld2420/ld2420.cpp index dec7e42de47..e81f117a1b3 100644 --- a/esphome/components/ld2420/ld2420.cpp +++ b/esphome/components/ld2420/ld2420.cpp @@ -161,13 +161,8 @@ template uint8_t find_uint8(const StringToUint8 (&arr)[N], const std:: return 0xFF; // Not found } -static uint8_t calc_checksum(void *data, size_t size) { - uint8_t checksum = 0; - uint8_t *data_bytes = (uint8_t *) data; - for (size_t i = 0; i < size; i++) { - checksum ^= data_bytes[i]; // XOR operation - } - return checksum; +static uint8_t calc_checksum(const void *data, size_t size) { + return xor_bytes(static_cast(data), size); } static int32_t get_firmware_int(const char *version_string) { diff --git a/esphome/components/ld6002b/ld6002b.cpp b/esphome/components/ld6002b/ld6002b.cpp index 93a2ea341f4..42461228cef 100644 --- a/esphome/components/ld6002b/ld6002b.cpp +++ b/esphome/components/ld6002b/ld6002b.cpp @@ -1256,12 +1256,8 @@ void LD6002BComponent::write_frame_(uint16_t type, const uint8_t *data, uint8_t this->write_byte(static_cast(~header_xor)); if (len > 0 && data != nullptr) { - uint8_t data_xor = 0; - for (uint8_t i = 0; i < len; i++) { - this->write_byte(data[i]); - data_xor ^= data[i]; - } - this->write_byte(static_cast(~data_xor)); + this->write_array(data, len); + this->write_byte(static_cast(~xor_bytes(data, len))); } const uint32_t now = millis(); if (track) { diff --git a/esphome/components/pmsa003i/pmsa003i.cpp b/esphome/components/pmsa003i/pmsa003i.cpp index 6885795e8a7..5ab0516785d 100644 --- a/esphome/components/pmsa003i/pmsa003i.cpp +++ b/esphome/components/pmsa003i/pmsa003i.cpp @@ -124,10 +124,7 @@ bool PMSA003IComponent::read_data_(PM25AQIData *data) { } // Calculate checksum - uint16_t checksum = 0; - for (uint8_t i = 0; i < CHECKSUM_START_INDEX; i++) { - checksum += buffer[i]; - } + const uint16_t checksum = sum16_bytes(buffer, CHECKSUM_START_INDEX); const uint16_t check = encode_uint16(buffer[CHECKSUM_START_INDEX], buffer[CHECKSUM_START_INDEX + 1]); if (checksum != check) { diff --git a/esphome/components/pmsx003/pmsx003.cpp b/esphome/components/pmsx003/pmsx003.cpp index f99e5adaa2c..efc80e8c650 100644 --- a/esphome/components/pmsx003/pmsx003.cpp +++ b/esphome/components/pmsx003/pmsx003.cpp @@ -159,10 +159,7 @@ optional PMSX003Component::check_byte_() { } // checksum is without checksum bytes - uint16_t checksum = 0; - for (uint16_t i = 0; i < total_size - 2; i++) { - checksum += this->data_[i]; - } + const uint16_t checksum = sum16_bytes(this->data_, total_size - 2); const uint16_t check = this->get_16_bit_uint_(total_size - 2); if (checksum != check) { @@ -207,10 +204,7 @@ void PMSX003Component::send_command_(Command cmd, uint16_t data) { }; // Calculate checksum - uint16_t checksum = 0; - for (uint8_t i = 0; i < 5; i++) { - checksum += send_data[i]; - } + const uint16_t checksum = sum16_bytes(send_data, 5); send_data[5] = (checksum >> 8) & 0xFF; // Verify Byte 1 send_data[6] = (checksum >> 0) & 0xFF; // Verify Byte 2 diff --git a/esphome/components/rdm6300/rdm6300.cpp b/esphome/components/rdm6300/rdm6300.cpp index 2bdbde91748..12fc9204cf6 100644 --- a/esphome/components/rdm6300/rdm6300.cpp +++ b/esphome/components/rdm6300/rdm6300.cpp @@ -39,9 +39,7 @@ void rdm6300::RDM6300Component::loop() { ESP_LOGW(TAG, "Invalid end byte from RDM6300!"); this->read_state_ = RDM6300_STATE_WAITING_FOR_START; } else { - uint8_t checksum = 0; - for (uint8_t i = 0; i < 5; i++) - checksum ^= this->buffer_[i]; + const uint8_t checksum = xor_bytes(this->buffer_, 5); this->read_state_ = RDM6300_STATE_WAITING_FOR_START; if (checksum != this->buffer_[5]) { ESP_LOGW(TAG, "Checksum from RDM6300 doesn't match! (0x%02X!=0x%02X)", checksum, this->buffer_[5]); diff --git a/esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp b/esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp index 6b5fb087171..d0e4c635eb2 100644 --- a/esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp +++ b/esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp @@ -62,24 +62,8 @@ void MR60FDA2Component::loop() { } } -/** - * @brief Calculate the checksum for a byte array. - * - * This function calculates the checksum for the provided byte array using an - * XOR-based checksum algorithm. - * - * @param data The byte array to calculate the checksum for. - * @param len The length of the byte array. - * @return The calculated checksum. - */ -static uint8_t calculate_checksum(const uint8_t *data, size_t len) { - uint8_t checksum = 0; - for (size_t i = 0; i < len; i++) { - checksum ^= data[i]; - } - checksum = ~checksum; - return checksum; -} +// The checksum is the inverted XOR of the bytes +static uint8_t calculate_checksum(const uint8_t *data, size_t len) { return ~xor_bytes(data, len); } /** * @brief Validate the checksum of a byte array. diff --git a/esphome/components/shelly_dimmer/stm32flash.cpp b/esphome/components/shelly_dimmer/stm32flash.cpp index 5996929ab84..9d3bd9f1e11 100644 --- a/esphome/components/shelly_dimmer/stm32flash.cpp +++ b/esphome/components/shelly_dimmer/stm32flash.cpp @@ -27,6 +27,7 @@ #include "debug.h" #include "dev_table.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" #include @@ -725,20 +726,13 @@ stm32_err_t stm32_write_memory(const stm32_unique_ptr &stm, uint32_t address, co return STM32_ERR_UNKNOWN; const unsigned int aligned_len = (len + 3) & ~3; - uint8_t cs = aligned_len - 1; uint8_t buf[256 + 2]; buf[0] = aligned_len - 1; - for (unsigned int i = 0; i < len; i++) { - cs ^= data[i]; - buf[i + 1] = data[i]; - } + memcpy(&buf[1], data, len); /* padding data */ - for (auto i = len; i < aligned_len; i++) { - cs ^= 0xFF; - buf[i + 1] = 0xFF; - } - buf[aligned_len + 1] = cs; + memset(&buf[1 + len], 0xFF, aligned_len - len); + buf[aligned_len + 1] = xor_bytes(buf, aligned_len + 1); stream->write_array(buf, aligned_len + 2); stream->flush(); diff --git a/esphome/components/toshiba/toshiba.cpp b/esphome/components/toshiba/toshiba.cpp index eae24641c40..2cf9419ef8e 100644 --- a/esphome/components/toshiba/toshiba.cpp +++ b/esphome/components/toshiba/toshiba.cpp @@ -544,9 +544,7 @@ void ToshibaClimate::transmit_generic_() { } // The last byte is the xor of all bytes from [4] - for (uint8_t i = 4; i < message_length - 1; i++) { - message[message_length - 1] ^= message[i]; - } + message[message_length - 1] = xor_bytes(message + 4, message_length - 5); // Transmit auto transmit = this->transmitter_->transmit(); @@ -1335,13 +1333,10 @@ bool ToshibaClimate::on_receive(remote_base::RemoteReceiveData data) { break; // Generic (old) Toshiba packet default: - uint8_t checksum = 0; // Add back the length of the header (we pruned it above) message_length += TOSHIBA_HEADER_LENGTH; // Validate the second checksum before trusting any more of the message - for (uint8_t i = TOSHIBA_HEADER_LENGTH; i < message_length - 1; i++) { - checksum ^= message[i]; - } + const uint8_t checksum = xor_bytes(message + TOSHIBA_HEADER_LENGTH, message_length - 1 - TOSHIBA_HEADER_LENGTH); // Did our computed checksum and the provided checksum match? if (checksum != message[message_length - 1]) { return false; diff --git a/esphome/components/zwave_proxy/zwave_proxy.cpp b/esphome/components/zwave_proxy/zwave_proxy.cpp index bfa16f857a0..a7b1fa0ced0 100644 --- a/esphome/components/zwave_proxy/zwave_proxy.cpp +++ b/esphome/components/zwave_proxy/zwave_proxy.cpp @@ -34,15 +34,9 @@ static constexpr bool is_bootloader_menu_byte(uint8_t byte) { return byte == 0 || byte == '\r' || byte == '\n' || (byte >= 0x20 && byte <= 0x7E); } -static uint8_t calculate_frame_checksum(const uint8_t *data, uint8_t length) { - // Calculate Z-Wave frame checksum - // XOR all bytes between SOF and checksum position (exclusive) +static uint8_t calculate_frame_checksum(const uint8_t *data, size_t length) { // Initial value is 0xFF per Z-Wave protocol specification - uint8_t checksum = 0xFF; - for (uint8_t i = 1; i < length - 1; i++) { - checksum ^= data[i]; - } - return checksum; + return xor_bytes(data + 1, length - 2, 0xFF); } ZWaveProxy::ZWaveProxy() { global_zwave_proxy = this; }