1
0
mirror of https://github.com/esphome/esphome.git synced 2026-02-10 01:32:06 +00:00

Compare commits

..

3 Commits

Author SHA1 Message Date
J. Nick Koston
50132038a2 Merge remote-tracking branch 'upstream/dev' into beta_premerge 2026-02-09 16:46:16 -06:00
J. Nick Koston
56ba59a41f Merge remote-tracking branch 'upstream/dev' into beta_premerge 2026-02-09 16:45:21 -06:00
J. Nick Koston
61746bd4b3 DNM: Test merge to beta 2026-02-07 17:29:45 -06:00
33 changed files with 293 additions and 423 deletions

View File

@@ -1,6 +1,5 @@
#pragma once
#include <algorithm>
#include <cmath>
#include <limits>
#include "abstract_aqi_calculator.h"
@@ -15,11 +14,7 @@ class AQICalculator : public AbstractAQICalculator {
float pm2_5_index = calculate_index(pm2_5_value, PM2_5_GRID);
float pm10_0_index = calculate_index(pm10_0_value, PM10_0_GRID);
float aqi = std::max(pm2_5_index, pm10_0_index);
if (aqi < 0.0f) {
aqi = 0.0f;
}
return static_cast<uint16_t>(std::lround(aqi));
return static_cast<uint16_t>(std::round((pm2_5_index < pm10_0_index) ? pm10_0_index : pm2_5_index));
}
protected:
@@ -27,27 +22,13 @@ class AQICalculator : public AbstractAQICalculator {
static constexpr int INDEX_GRID[NUM_LEVELS][2] = {{0, 50}, {51, 100}, {101, 150}, {151, 200}, {201, 300}, {301, 500}};
static constexpr float PM2_5_GRID[NUM_LEVELS][2] = {
// clang-format off
{0.0f, 9.1f},
{9.1f, 35.5f},
{35.5f, 55.5f},
{55.5f, 125.5f},
{125.5f, 225.5f},
{225.5f, std::numeric_limits<float>::max()}
// clang-format on
};
static constexpr float PM2_5_GRID[NUM_LEVELS][2] = {{0.0f, 9.0f}, {9.1f, 35.4f},
{35.5f, 55.4f}, {55.5f, 125.4f},
{125.5f, 225.4f}, {225.5f, std::numeric_limits<float>::max()}};
static constexpr float PM10_0_GRID[NUM_LEVELS][2] = {
// clang-format off
{0.0f, 55.0f},
{55.0f, 155.0f},
{155.0f, 255.0f},
{255.0f, 355.0f},
{355.0f, 425.0f},
{425.0f, std::numeric_limits<float>::max()}
// clang-format on
};
static constexpr float PM10_0_GRID[NUM_LEVELS][2] = {{0.0f, 54.0f}, {55.0f, 154.0f},
{155.0f, 254.0f}, {255.0f, 354.0f},
{355.0f, 424.0f}, {425.0f, std::numeric_limits<float>::max()}};
static float calculate_index(float value, const float array[NUM_LEVELS][2]) {
int grid_index = get_grid_index(value, array);
@@ -64,10 +45,7 @@ class AQICalculator : public AbstractAQICalculator {
static int get_grid_index(float value, const float array[NUM_LEVELS][2]) {
for (int i = 0; i < NUM_LEVELS; i++) {
const bool in_range =
(value >= array[i][0]) && ((i == NUM_LEVELS - 1) ? (value <= array[i][1]) // last bucket inclusive
: (value < array[i][1])); // others exclusive on hi
if (in_range) {
if (value >= array[i][0] && value <= array[i][1]) {
return i;
}
}

View File

@@ -1,6 +1,5 @@
#pragma once
#include <algorithm>
#include <cmath>
#include <limits>
#include "abstract_aqi_calculator.h"
@@ -13,11 +12,7 @@ class CAQICalculator : public AbstractAQICalculator {
float pm2_5_index = calculate_index(pm2_5_value, PM2_5_GRID);
float pm10_0_index = calculate_index(pm10_0_value, PM10_0_GRID);
float aqi = std::max(pm2_5_index, pm10_0_index);
if (aqi < 0.0f) {
aqi = 0.0f;
}
return static_cast<uint16_t>(std::lround(aqi));
return static_cast<uint16_t>(std::round((pm2_5_index < pm10_0_index) ? pm10_0_index : pm2_5_index));
}
protected:
@@ -26,24 +21,10 @@ class CAQICalculator : public AbstractAQICalculator {
static constexpr int INDEX_GRID[NUM_LEVELS][2] = {{0, 25}, {26, 50}, {51, 75}, {76, 100}, {101, 400}};
static constexpr float PM2_5_GRID[NUM_LEVELS][2] = {
// clang-format off
{0.0f, 15.1f},
{15.1f, 30.1f},
{30.1f, 55.1f},
{55.1f, 110.1f},
{110.1f, std::numeric_limits<float>::max()}
// clang-format on
};
{0.0f, 15.0f}, {15.1f, 30.0f}, {30.1f, 55.0f}, {55.1f, 110.0f}, {110.1f, std::numeric_limits<float>::max()}};
static constexpr float PM10_0_GRID[NUM_LEVELS][2] = {
// clang-format off
{0.0f, 25.1f},
{25.1f, 50.1f},
{50.1f, 90.1f},
{90.1f, 180.1f},
{180.1f, std::numeric_limits<float>::max()}
// clang-format on
};
{0.0f, 25.0f}, {25.1f, 50.0f}, {50.1f, 90.0f}, {90.1f, 180.0f}, {180.1f, std::numeric_limits<float>::max()}};
static float calculate_index(float value, const float array[NUM_LEVELS][2]) {
int grid_index = get_grid_index(value, array);
@@ -61,10 +42,7 @@ class CAQICalculator : public AbstractAQICalculator {
static int get_grid_index(float value, const float array[NUM_LEVELS][2]) {
for (int i = 0; i < NUM_LEVELS; i++) {
const bool in_range =
(value >= array[i][0]) && ((i == NUM_LEVELS - 1) ? (value <= array[i][1]) // last bucket inclusive
: (value < array[i][1])); // others exclusive on hi
if (in_range) {
if (value >= array[i][0] && value <= array[i][1]) {
return i;
}
}

View File

@@ -16,8 +16,8 @@ void CSE7766Component::loop() {
}
// Early return prevents updating last_transmission_ when no data is available.
size_t avail = this->available();
if (avail == 0) {
int avail = this->available();
if (avail <= 0) {
return;
}
@@ -27,7 +27,7 @@ void CSE7766Component::loop() {
// At 4800 baud (~480 bytes/sec) with ~122 Hz loop rate, typically ~4 bytes per call.
uint8_t buf[CSE7766_RAW_DATA_SIZE];
while (avail > 0) {
size_t to_read = std::min(avail, sizeof(buf));
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}

View File

@@ -133,10 +133,10 @@ void DFPlayer::send_cmd_(uint8_t cmd, uint16_t argument) {
void DFPlayer::loop() {
// Read all available bytes in batches to reduce UART call overhead.
size_t avail = this->available();
int avail = this->available();
uint8_t buf[64];
while (avail > 0) {
size_t to_read = std::min(avail, sizeof(buf));
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}

View File

@@ -28,28 +28,15 @@ void DlmsMeterComponent::dump_config() {
void DlmsMeterComponent::loop() {
// Read while data is available, netznoe uses two frames so allow 2x max frame length
size_t avail = this->available();
if (avail > 0) {
size_t remaining = MBUS_MAX_FRAME_LENGTH * 2 - this->receive_buffer_.size();
if (remaining == 0) {
while (this->available()) {
if (this->receive_buffer_.size() >= MBUS_MAX_FRAME_LENGTH * 2) {
ESP_LOGW(TAG, "Receive buffer full, dropping remaining bytes");
} else {
// Read all available bytes in batches to reduce UART call overhead.
// Cap reads to remaining buffer capacity.
if (avail > remaining) {
avail = remaining;
}
uint8_t buf[64];
while (avail > 0) {
size_t to_read = std::min(avail, sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}
avail -= to_read;
this->receive_buffer_.insert(this->receive_buffer_.end(), buf, buf + to_read);
this->last_read_ = millis();
}
break;
}
uint8_t c;
this->read_byte(&c);
this->receive_buffer_.push_back(c);
this->last_read_ = millis();
}
if (!this->receive_buffer_.empty() && millis() - this->last_read_ > this->read_timeout_) {

View File

@@ -40,7 +40,9 @@ bool Dsmr::ready_to_request_data_() {
this->start_requesting_data_();
}
if (!this->requesting_data_) {
this->drain_rx_buffer_();
while (this->available()) {
this->read();
}
}
}
return this->requesting_data_;
@@ -113,18 +115,10 @@ void Dsmr::stop_requesting_data_() {
} else {
ESP_LOGV(TAG, "Stop reading data from P1 port");
}
this->drain_rx_buffer_();
this->requesting_data_ = false;
}
}
void Dsmr::drain_rx_buffer_() {
uint8_t buf[64];
size_t avail;
while ((avail = this->available()) > 0) {
if (!this->read_array(buf, std::min(avail, sizeof(buf)))) {
break;
while (this->available()) {
this->read();
}
this->requesting_data_ = false;
}
}
@@ -134,148 +128,125 @@ void Dsmr::reset_telegram_() {
this->bytes_read_ = 0;
this->crypt_bytes_read_ = 0;
this->crypt_telegram_len_ = 0;
this->last_read_time_ = 0;
}
void Dsmr::receive_telegram_() {
while (this->available_within_timeout_()) {
// Read all available bytes in batches to reduce UART call overhead.
uint8_t buf[64];
size_t avail = this->available();
while (avail > 0) {
size_t to_read = std::min(avail, sizeof(buf));
if (!this->read_array(buf, to_read))
return;
avail -= to_read;
const char c = this->read();
for (size_t i = 0; i < to_read; i++) {
const char c = static_cast<char>(buf[i]);
// Find a new telegram header, i.e. forward slash.
if (c == '/') {
ESP_LOGV(TAG, "Header of telegram found");
this->reset_telegram_();
this->header_found_ = true;
}
if (!this->header_found_)
continue;
// Find a new telegram header, i.e. forward slash.
if (c == '/') {
ESP_LOGV(TAG, "Header of telegram found");
this->reset_telegram_();
this->header_found_ = true;
}
if (!this->header_found_)
continue;
// Check for buffer overflow.
if (this->bytes_read_ >= this->max_telegram_len_) {
this->reset_telegram_();
ESP_LOGE(TAG, "Error: telegram larger than buffer (%d bytes)", this->max_telegram_len_);
return;
}
// Check for buffer overflow.
if (this->bytes_read_ >= this->max_telegram_len_) {
this->reset_telegram_();
ESP_LOGE(TAG, "Error: telegram larger than buffer (%d bytes)", this->max_telegram_len_);
return;
}
// Some v2.2 or v3 meters will send a new value which starts with '('
// in a new line, while the value belongs to the previous ObisId. For
// proper parsing, remove these new line characters.
if (c == '(') {
while (true) {
auto previous_char = this->telegram_[this->bytes_read_ - 1];
if (previous_char == '\n' || previous_char == '\r') {
this->bytes_read_--;
} else {
break;
}
}
}
// Store the byte in the buffer.
this->telegram_[this->bytes_read_] = c;
this->bytes_read_++;
// Check for a footer, i.e. exclamation mark, followed by a hex checksum.
if (c == '!') {
ESP_LOGV(TAG, "Footer of telegram found");
this->footer_found_ = true;
continue;
}
// Check for the end of the hex checksum, i.e. a newline.
if (this->footer_found_ && c == '\n') {
// Parse the telegram and publish sensor values.
this->parse_telegram();
this->reset_telegram_();
return;
// Some v2.2 or v3 meters will send a new value which starts with '('
// in a new line, while the value belongs to the previous ObisId. For
// proper parsing, remove these new line characters.
if (c == '(') {
while (true) {
auto previous_char = this->telegram_[this->bytes_read_ - 1];
if (previous_char == '\n' || previous_char == '\r') {
this->bytes_read_--;
} else {
break;
}
}
}
// Store the byte in the buffer.
this->telegram_[this->bytes_read_] = c;
this->bytes_read_++;
// Check for a footer, i.e. exclamation mark, followed by a hex checksum.
if (c == '!') {
ESP_LOGV(TAG, "Footer of telegram found");
this->footer_found_ = true;
continue;
}
// Check for the end of the hex checksum, i.e. a newline.
if (this->footer_found_ && c == '\n') {
// Parse the telegram and publish sensor values.
this->parse_telegram();
this->reset_telegram_();
return;
}
}
}
void Dsmr::receive_encrypted_telegram_() {
while (this->available_within_timeout_()) {
// Read all available bytes in batches to reduce UART call overhead.
uint8_t buf[64];
size_t avail = this->available();
while (avail > 0) {
size_t to_read = std::min(avail, sizeof(buf));
if (!this->read_array(buf, to_read))
return;
avail -= to_read;
const char c = this->read();
for (size_t i = 0; i < to_read; i++) {
const char c = static_cast<char>(buf[i]);
// Find a new telegram start byte.
if (!this->header_found_) {
if ((uint8_t) c != 0xDB) {
continue;
}
ESP_LOGV(TAG, "Start byte 0xDB of encrypted telegram found");
this->reset_telegram_();
this->header_found_ = true;
}
// Check for buffer overflow.
if (this->crypt_bytes_read_ >= this->max_telegram_len_) {
this->reset_telegram_();
ESP_LOGE(TAG, "Error: encrypted telegram larger than buffer (%d bytes)", this->max_telegram_len_);
return;
}
// Store the byte in the buffer.
this->crypt_telegram_[this->crypt_bytes_read_] = c;
this->crypt_bytes_read_++;
// Read the length of the incoming encrypted telegram.
if (this->crypt_telegram_len_ == 0 && this->crypt_bytes_read_ > 20) {
// Complete header + data bytes
this->crypt_telegram_len_ = 13 + (this->crypt_telegram_[11] << 8 | this->crypt_telegram_[12]);
ESP_LOGV(TAG, "Encrypted telegram length: %d bytes", this->crypt_telegram_len_);
}
// Check for the end of the encrypted telegram.
if (this->crypt_telegram_len_ == 0 || this->crypt_bytes_read_ != this->crypt_telegram_len_) {
continue;
}
ESP_LOGV(TAG, "End of encrypted telegram found");
// Decrypt the encrypted telegram.
GCM<AES128> *gcmaes128{new GCM<AES128>()};
gcmaes128->setKey(this->decryption_key_.data(), gcmaes128->keySize());
// the iv is 8 bytes of the system title + 4 bytes frame counter
// system title is at byte 2 and frame counter at byte 15
for (int i = 10; i < 14; i++)
this->crypt_telegram_[i] = this->crypt_telegram_[i + 4];
constexpr uint16_t iv_size{12};
gcmaes128->setIV(&this->crypt_telegram_[2], iv_size);
gcmaes128->decrypt(reinterpret_cast<uint8_t *>(this->telegram_),
// the ciphertext start at byte 18
&this->crypt_telegram_[18],
// cipher size
this->crypt_bytes_read_ - 17);
delete gcmaes128; // NOLINT(cppcoreguidelines-owning-memory)
this->bytes_read_ = strnlen(this->telegram_, this->max_telegram_len_);
ESP_LOGV(TAG, "Decrypted telegram size: %d bytes", this->bytes_read_);
ESP_LOGVV(TAG, "Decrypted telegram: %s", this->telegram_);
// Parse the decrypted telegram and publish sensor values.
this->parse_telegram();
this->reset_telegram_();
return;
// Find a new telegram start byte.
if (!this->header_found_) {
if ((uint8_t) c != 0xDB) {
continue;
}
ESP_LOGV(TAG, "Start byte 0xDB of encrypted telegram found");
this->reset_telegram_();
this->header_found_ = true;
}
// Check for buffer overflow.
if (this->crypt_bytes_read_ >= this->max_telegram_len_) {
this->reset_telegram_();
ESP_LOGE(TAG, "Error: encrypted telegram larger than buffer (%d bytes)", this->max_telegram_len_);
return;
}
// Store the byte in the buffer.
this->crypt_telegram_[this->crypt_bytes_read_] = c;
this->crypt_bytes_read_++;
// Read the length of the incoming encrypted telegram.
if (this->crypt_telegram_len_ == 0 && this->crypt_bytes_read_ > 20) {
// Complete header + data bytes
this->crypt_telegram_len_ = 13 + (this->crypt_telegram_[11] << 8 | this->crypt_telegram_[12]);
ESP_LOGV(TAG, "Encrypted telegram length: %d bytes", this->crypt_telegram_len_);
}
// Check for the end of the encrypted telegram.
if (this->crypt_telegram_len_ == 0 || this->crypt_bytes_read_ != this->crypt_telegram_len_) {
continue;
}
ESP_LOGV(TAG, "End of encrypted telegram found");
// Decrypt the encrypted telegram.
GCM<AES128> *gcmaes128{new GCM<AES128>()};
gcmaes128->setKey(this->decryption_key_.data(), gcmaes128->keySize());
// the iv is 8 bytes of the system title + 4 bytes frame counter
// system title is at byte 2 and frame counter at byte 15
for (int i = 10; i < 14; i++)
this->crypt_telegram_[i] = this->crypt_telegram_[i + 4];
constexpr uint16_t iv_size{12};
gcmaes128->setIV(&this->crypt_telegram_[2], iv_size);
gcmaes128->decrypt(reinterpret_cast<uint8_t *>(this->telegram_),
// the ciphertext start at byte 18
&this->crypt_telegram_[18],
// cipher size
this->crypt_bytes_read_ - 17);
delete gcmaes128; // NOLINT(cppcoreguidelines-owning-memory)
this->bytes_read_ = strnlen(this->telegram_, this->max_telegram_len_);
ESP_LOGV(TAG, "Decrypted telegram size: %d bytes", this->bytes_read_);
ESP_LOGVV(TAG, "Decrypted telegram: %s", this->telegram_);
// Parse the decrypted telegram and publish sensor values.
this->parse_telegram();
this->reset_telegram_();
return;
}
}

View File

@@ -85,7 +85,6 @@ class Dsmr : public Component, public uart::UARTDevice {
void receive_telegram_();
void receive_encrypted_telegram_();
void reset_telegram_();
void drain_rx_buffer_();
/// Wait for UART data to become available within the read timeout.
///

View File

@@ -1,16 +1,20 @@
#include "hlk_fm22x.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include <array>
#include <cinttypes>
namespace esphome::hlk_fm22x {
static const char *const TAG = "hlk_fm22x";
// Maximum response size is 36 bytes (VERIFY reply: face_id + 32-byte name)
static constexpr size_t HLK_FM22X_MAX_RESPONSE_SIZE = 36;
void HlkFm22xComponent::setup() {
ESP_LOGCONFIG(TAG, "Setting up HLK-FM22X...");
this->set_enrolling_(false);
while (this->available() > 0) {
while (this->available()) {
this->read();
}
this->defer([this]() { this->send_command_(HlkFm22xCommand::GET_STATUS); });
@@ -31,7 +35,7 @@ void HlkFm22xComponent::update() {
}
void HlkFm22xComponent::enroll_face(const std::string &name, HlkFm22xFaceDirection direction) {
if (name.length() > HLK_FM22X_NAME_SIZE - 1) {
if (name.length() > 31) {
ESP_LOGE(TAG, "enroll_face(): name too long '%s'", name.c_str());
return;
}
@@ -84,7 +88,7 @@ void HlkFm22xComponent::send_command_(HlkFm22xCommand command, const uint8_t *da
}
this->wait_cycles_ = 0;
this->active_command_ = command;
while (this->available() > 0)
while (this->available())
this->read();
this->write((uint8_t) (START_CODE >> 8));
this->write((uint8_t) (START_CODE & 0xFF));
@@ -133,24 +137,17 @@ void HlkFm22xComponent::recv_command_() {
checksum ^= byte;
length |= byte;
if (length > HLK_FM22X_MAX_RESPONSE_SIZE) {
ESP_LOGE(TAG, "Response too large: %u bytes", length);
// Discard exactly the remaining payload and checksum for this frame
for (uint16_t i = 0; i < length + 1 && this->available() > 0; ++i)
this->read();
return;
}
std::vector<uint8_t> data;
data.reserve(length);
for (uint16_t idx = 0; idx < length; ++idx) {
byte = this->read();
checksum ^= byte;
this->recv_buf_[idx] = byte;
data.push_back(byte);
}
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
char hex_buf[format_hex_pretty_size(HLK_FM22X_MAX_RESPONSE_SIZE)];
ESP_LOGV(TAG, "Recv type: 0x%.2X, data: %s", response_type,
format_hex_pretty_to(hex_buf, this->recv_buf_.data(), length));
ESP_LOGV(TAG, "Recv type: 0x%.2X, data: %s", response_type, format_hex_pretty_to(hex_buf, data.data(), data.size()));
#endif
byte = this->read();
@@ -160,10 +157,10 @@ void HlkFm22xComponent::recv_command_() {
}
switch (response_type) {
case HlkFm22xResponseType::NOTE:
this->handle_note_(this->recv_buf_.data(), length);
this->handle_note_(data);
break;
case HlkFm22xResponseType::REPLY:
this->handle_reply_(this->recv_buf_.data(), length);
this->handle_reply_(data);
break;
default:
ESP_LOGW(TAG, "Unexpected response type: 0x%.2X", response_type);
@@ -171,15 +168,11 @@ void HlkFm22xComponent::recv_command_() {
}
}
void HlkFm22xComponent::handle_note_(const uint8_t *data, size_t length) {
if (length < 1) {
ESP_LOGE(TAG, "Empty note data");
return;
}
void HlkFm22xComponent::handle_note_(const std::vector<uint8_t> &data) {
switch (data[0]) {
case HlkFm22xNoteType::FACE_STATE:
if (length < 17) {
ESP_LOGE(TAG, "Invalid face note data size: %zu", length);
if (data.size() < 17) {
ESP_LOGE(TAG, "Invalid face note data size: %u", data.size());
break;
}
{
@@ -216,13 +209,9 @@ void HlkFm22xComponent::handle_note_(const uint8_t *data, size_t length) {
}
}
void HlkFm22xComponent::handle_reply_(const uint8_t *data, size_t length) {
void HlkFm22xComponent::handle_reply_(const std::vector<uint8_t> &data) {
auto expected = this->active_command_;
this->active_command_ = HlkFm22xCommand::NONE;
if (length < 2) {
ESP_LOGE(TAG, "Reply too short: %zu bytes", length);
return;
}
if (data[0] != (uint8_t) expected) {
ESP_LOGE(TAG, "Unexpected response command. Expected: 0x%.2X, Received: 0x%.2X", expected, data[0]);
return;
@@ -249,20 +238,16 @@ void HlkFm22xComponent::handle_reply_(const uint8_t *data, size_t length) {
}
switch (expected) {
case HlkFm22xCommand::VERIFY: {
if (length < 4 + HLK_FM22X_NAME_SIZE) {
ESP_LOGE(TAG, "VERIFY response too short: %zu bytes", length);
break;
}
int16_t face_id = ((int16_t) data[2] << 8) | data[3];
const char *name_ptr = reinterpret_cast<const char *>(data + 4);
ESP_LOGD(TAG, "Face verified. ID: %d, name: %.*s", face_id, (int) HLK_FM22X_NAME_SIZE, name_ptr);
std::string name(data.begin() + 4, data.begin() + 36);
ESP_LOGD(TAG, "Face verified. ID: %d, name: %s", face_id, name.c_str());
if (this->last_face_id_sensor_ != nullptr) {
this->last_face_id_sensor_->publish_state(face_id);
}
if (this->last_face_name_text_sensor_ != nullptr) {
this->last_face_name_text_sensor_->publish_state(name_ptr, HLK_FM22X_NAME_SIZE);
this->last_face_name_text_sensor_->publish_state(name);
}
this->face_scan_matched_callback_.call(face_id, std::string(name_ptr, HLK_FM22X_NAME_SIZE));
this->face_scan_matched_callback_.call(face_id, name);
break;
}
case HlkFm22xCommand::ENROLL: {
@@ -281,8 +266,9 @@ void HlkFm22xComponent::handle_reply_(const uint8_t *data, size_t length) {
this->defer([this]() { this->send_command_(HlkFm22xCommand::GET_VERSION); });
break;
case HlkFm22xCommand::GET_VERSION:
if (this->version_text_sensor_ != nullptr && length > 2) {
this->version_text_sensor_->publish_state(reinterpret_cast<const char *>(data + 2), length - 2);
if (this->version_text_sensor_ != nullptr) {
std::string version(data.begin() + 2, data.end());
this->version_text_sensor_->publish_state(version);
}
this->defer([this]() { this->get_face_count_(); });
break;

View File

@@ -7,15 +7,12 @@
#include "esphome/components/text_sensor/text_sensor.h"
#include "esphome/components/uart/uart.h"
#include <array>
#include <utility>
#include <vector>
namespace esphome::hlk_fm22x {
static const uint16_t START_CODE = 0xEFAA;
static constexpr size_t HLK_FM22X_NAME_SIZE = 32;
// Maximum response payload: command(1) + result(1) + face_id(2) + name(32) = 36
static constexpr size_t HLK_FM22X_MAX_RESPONSE_SIZE = 36;
enum HlkFm22xCommand {
NONE = 0x00,
RESET = 0x10,
@@ -121,11 +118,10 @@ class HlkFm22xComponent : public PollingComponent, public uart::UARTDevice {
void get_face_count_();
void send_command_(HlkFm22xCommand command, const uint8_t *data = nullptr, size_t size = 0);
void recv_command_();
void handle_note_(const uint8_t *data, size_t length);
void handle_reply_(const uint8_t *data, size_t length);
void handle_note_(const std::vector<uint8_t> &data);
void handle_reply_(const std::vector<uint8_t> &data);
void set_enrolling_(bool enrolling);
std::array<uint8_t, HLK_FM22X_MAX_RESPONSE_SIZE> recv_buf_;
HlkFm22xCommand active_command_ = HlkFm22xCommand::NONE;
uint16_t wait_cycles_ = 0;
sensor::Sensor *face_count_sensor_{nullptr};

View File

@@ -276,10 +276,10 @@ void LD2410Component::restart_and_read_all_info() {
void LD2410Component::loop() {
// Read all available bytes in batches to reduce UART call overhead.
size_t avail = this->available();
int avail = this->available();
uint8_t buf[MAX_LINE_LENGTH];
while (avail > 0) {
size_t to_read = std::min(avail, sizeof(buf));
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}

View File

@@ -311,10 +311,10 @@ void LD2412Component::restart_and_read_all_info() {
void LD2412Component::loop() {
// Read all available bytes in batches to reduce UART call overhead.
size_t avail = this->available();
int avail = this->available();
uint8_t buf[MAX_LINE_LENGTH];
while (avail > 0) {
size_t to_read = std::min(avail, sizeof(buf));
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}

View File

@@ -335,10 +335,9 @@ void LD2420Component::revert_config_action() {
void LD2420Component::loop() {
// If there is a active send command do not process it here, the send command call will handle it.
if (this->cmd_active_) {
return;
while (!this->cmd_active_ && this->available()) {
this->readline_(this->read(), this->buffer_data_, MAX_LINE_LENGTH);
}
this->read_batch_(this->buffer_data_);
}
void LD2420Component::update_radar_data(uint16_t const *gate_energy, uint8_t sample_number) {
@@ -540,23 +539,6 @@ void LD2420Component::handle_simple_mode_(const uint8_t *inbuf, int len) {
}
}
void LD2420Component::read_batch_(std::span<uint8_t, MAX_LINE_LENGTH> buffer) {
// Read all available bytes in batches to reduce UART call overhead.
size_t avail = this->available();
uint8_t buf[MAX_LINE_LENGTH];
while (avail > 0) {
size_t to_read = std::min(avail, sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}
avail -= to_read;
for (size_t i = 0; i < to_read; i++) {
this->readline_(buf[i], buffer.data(), buffer.size());
}
}
}
void LD2420Component::handle_ack_data_(uint8_t *buffer, int len) {
this->cmd_reply_.command = buffer[CMD_FRAME_COMMAND];
this->cmd_reply_.length = buffer[CMD_FRAME_DATA_LENGTH];

View File

@@ -4,7 +4,6 @@
#include "esphome/components/uart/uart.h"
#include "esphome/core/automation.h"
#include "esphome/core/helpers.h"
#include <span>
#ifdef USE_TEXT_SENSOR
#include "esphome/components/text_sensor/text_sensor.h"
#endif
@@ -166,7 +165,6 @@ class LD2420Component : public Component, public uart::UARTDevice {
void handle_energy_mode_(uint8_t *buffer, int len);
void handle_ack_data_(uint8_t *buffer, int len);
void readline_(int rx_data, uint8_t *buffer, int len);
void read_batch_(std::span<uint8_t, MAX_LINE_LENGTH> buffer);
void set_calibration_(bool state) { this->calibration_ = state; };
bool get_calibration_() { return this->calibration_; };

View File

@@ -277,10 +277,10 @@ void LD2450Component::dump_config() {
void LD2450Component::loop() {
// Read all available bytes in batches to reduce UART call overhead.
size_t avail = this->available();
int avail = this->available();
uint8_t buf[MAX_LINE_LENGTH];
while (avail > 0) {
size_t to_read = std::min(avail, sizeof(buf));
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}

View File

@@ -36,9 +36,8 @@ void HOT Logger::log_vprintf_(uint8_t level, const char *tag, int line, const ch
#endif
// Fast path: main thread, no recursion (99.9% of all logs)
// Pass nullptr for thread_name since we already know this is the main task
if (is_main_task && !this->main_task_recursion_guard_) [[likely]] {
this->log_message_to_buffer_and_send_(this->main_task_recursion_guard_, level, tag, line, format, args, nullptr);
this->log_message_to_buffer_and_send_(this->main_task_recursion_guard_, level, tag, line, format, args);
return;
}
@@ -48,23 +47,21 @@ void HOT Logger::log_vprintf_(uint8_t level, const char *tag, int line, const ch
}
// Non-main thread handling (~0.1% of logs)
// Resolve thread name once and pass it through the logging chain.
// ESP32/LibreTiny: use TaskHandle_t overload to avoid redundant xTaskGetCurrentTaskHandle()
// (we already have the handle from the main task check above).
// Host: pass a stack buffer for pthread_getname_np to write into.
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
const char *thread_name = get_thread_name_(current_task);
this->log_vprintf_non_main_thread_(level, tag, line, format, args, current_task);
#else // USE_HOST
char thread_name_buf[THREAD_NAME_BUF_SIZE];
const char *thread_name = this->get_thread_name_(thread_name_buf);
this->log_vprintf_non_main_thread_(level, tag, line, format, args);
#endif
this->log_vprintf_non_main_thread_(level, tag, line, format, args, thread_name);
}
// Handles non-main thread logging only
// Kept separate from hot path to improve instruction cache performance
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
void Logger::log_vprintf_non_main_thread_(uint8_t level, const char *tag, int line, const char *format, va_list args,
const char *thread_name) {
TaskHandle_t current_task) {
#else // USE_HOST
void Logger::log_vprintf_non_main_thread_(uint8_t level, const char *tag, int line, const char *format, va_list args) {
#endif
// Check if already in recursion for this non-main thread/task
if (this->is_non_main_task_recursive_()) {
return;
@@ -76,8 +73,12 @@ void Logger::log_vprintf_non_main_thread_(uint8_t level, const char *tag, int li
bool message_sent = false;
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
// For non-main threads/tasks, queue the message for callbacks
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
message_sent =
this->log_buffer_->send_message_thread_safe(level, tag, static_cast<uint16_t>(line), thread_name, format, args);
this->log_buffer_->send_message_thread_safe(level, tag, static_cast<uint16_t>(line), current_task, format, args);
#else // USE_HOST
message_sent = this->log_buffer_->send_message_thread_safe(level, tag, static_cast<uint16_t>(line), format, args);
#endif
if (message_sent) {
// Enable logger loop to process the buffered message
// This is safe to call from any context including ISRs
@@ -100,27 +101,19 @@ void Logger::log_vprintf_non_main_thread_(uint8_t level, const char *tag, int li
#endif
char console_buffer[MAX_CONSOLE_LOG_MSG_SIZE]; // MUST be stack allocated for thread safety
LogBuffer buf{console_buffer, MAX_CONSOLE_LOG_MSG_SIZE};
this->format_log_to_buffer_with_terminator_(level, tag, line, format, args, buf, thread_name);
this->format_log_to_buffer_with_terminator_(level, tag, line, format, args, buf);
this->write_to_console_(buf);
}
// RAII guard automatically resets on return
}
#else
// Implementation for single-task platforms (ESP8266, RP2040, Zephyr)
// TODO: Zephyr may have multiple threads (work queues, etc.) but uses this single-task path.
// Logging calls are NOT thread-safe: global_recursion_guard_ is a plain bool and tx_buffer_ has no locking.
// Not a problem in practice yet since Zephyr has no API support (logs are console-only).
// Implementation for all other platforms (single-task, no threading)
void HOT Logger::log_vprintf_(uint8_t level, const char *tag, int line, const char *format, va_list args) { // NOLINT
if (level > this->level_for(tag) || global_recursion_guard_)
return;
#ifdef USE_ZEPHYR
char tmp[MAX_POINTER_REPRESENTATION];
this->log_message_to_buffer_and_send_(global_recursion_guard_, level, tag, line, format, args,
this->get_thread_name_(tmp));
#else // Other single-task platforms don't have thread names, so pass nullptr
this->log_message_to_buffer_and_send_(global_recursion_guard_, level, tag, line, format, args, nullptr);
#endif
this->log_message_to_buffer_and_send_(global_recursion_guard_, level, tag, line, format, args);
}
#endif // USE_ESP32 / USE_HOST / USE_LIBRETINY
@@ -136,7 +129,7 @@ void Logger::log_vprintf_(uint8_t level, const char *tag, int line, const __Flas
if (level > this->level_for(tag) || global_recursion_guard_)
return;
this->log_message_to_buffer_and_send_(global_recursion_guard_, level, tag, line, format, args, nullptr);
this->log_message_to_buffer_and_send_(global_recursion_guard_, level, tag, line, format, args);
}
#endif // USE_STORE_LOG_STR_IN_FLASH

View File

@@ -2,7 +2,6 @@
#include <cstdarg>
#include <map>
#include <span>
#include <type_traits>
#if defined(USE_ESP32) || defined(USE_HOST)
#include <pthread.h>
@@ -125,10 +124,6 @@ static constexpr uint16_t MAX_HEADER_SIZE = 128;
// "0x" + 2 hex digits per byte + '\0'
static constexpr size_t MAX_POINTER_REPRESENTATION = 2 + sizeof(void *) * 2 + 1;
// Stack buffer size for retrieving thread/task names from the OS
// macOS allows up to 64 bytes, Linux up to 16
static constexpr size_t THREAD_NAME_BUF_SIZE = 64;
// Buffer wrapper for log formatting functions
struct LogBuffer {
char *data;
@@ -413,24 +408,34 @@ class Logger : public Component {
#if defined(USE_ESP32) || defined(USE_HOST) || defined(USE_LIBRETINY)
// Handles non-main thread logging only (~0.1% of calls)
// thread_name is resolved by the caller from the task handle, avoiding redundant lookups
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
// ESP32/LibreTiny: Pass task handle to avoid calling xTaskGetCurrentTaskHandle() twice
void log_vprintf_non_main_thread_(uint8_t level, const char *tag, int line, const char *format, va_list args,
const char *thread_name);
TaskHandle_t current_task);
#else // USE_HOST
// Host: No task handle parameter needed (not used in send_message_thread_safe)
void log_vprintf_non_main_thread_(uint8_t level, const char *tag, int line, const char *format, va_list args);
#endif
#endif
void process_messages_();
void write_msg_(const char *msg, uint16_t len);
// Format a log message with printf-style arguments and write it to a buffer with header, footer, and null terminator
// thread_name: name of the calling thread/task, or nullptr for main task (callers already know which task they're on)
inline void HOT format_log_to_buffer_with_terminator_(uint8_t level, const char *tag, int line, const char *format,
va_list args, LogBuffer &buf, const char *thread_name) {
buf.write_header(level, tag, line, thread_name);
va_list args, LogBuffer &buf) {
#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_HOST)
buf.write_header(level, tag, line, this->get_thread_name_());
#elif defined(USE_ZEPHYR)
char tmp[MAX_POINTER_REPRESENTATION];
buf.write_header(level, tag, line, this->get_thread_name_(tmp));
#else
buf.write_header(level, tag, line, nullptr);
#endif
buf.format_body(format, args);
}
#ifdef USE_STORE_LOG_STR_IN_FLASH
// Format a log message with flash string format and write it to a buffer with header, footer, and null terminator
// ESP8266-only (single-task), thread_name is always nullptr
inline void HOT format_log_to_buffer_with_terminator_P_(uint8_t level, const char *tag, int line,
const __FlashStringHelper *format, va_list args,
LogBuffer &buf) {
@@ -461,10 +466,9 @@ class Logger : public Component {
// Helper to format and send a log message to both console and listeners
// Template handles both const char* (RAM) and __FlashStringHelper* (flash) format strings
// thread_name: name of the calling thread/task, or nullptr for main task
template<typename FormatType>
inline void HOT log_message_to_buffer_and_send_(bool &recursion_guard, uint8_t level, const char *tag, int line,
FormatType format, va_list args, const char *thread_name) {
FormatType format, va_list args) {
RecursionGuard guard(recursion_guard);
LogBuffer buf{this->tx_buffer_, this->tx_buffer_size_};
#ifdef USE_STORE_LOG_STR_IN_FLASH
@@ -473,7 +477,7 @@ class Logger : public Component {
} else
#endif
{
this->format_log_to_buffer_with_terminator_(level, tag, line, format, args, buf, thread_name);
this->format_log_to_buffer_with_terminator_(level, tag, line, format, args, buf);
}
this->notify_listeners_(level, tag, buf);
this->write_log_buffer_to_console_(buf);
@@ -561,57 +565,37 @@ class Logger : public Component {
bool global_recursion_guard_{false}; // Simple global recursion guard for single-task platforms
#endif
// --- get_thread_name_ overloads (per-platform) ---
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
// Primary overload - takes a task handle directly to avoid redundant xTaskGetCurrentTaskHandle() calls
// when the caller already has the handle (e.g. from the main task check in log_vprintf_)
const char *get_thread_name_(TaskHandle_t task) {
if (task == this->main_task_) {
return nullptr; // Main task
}
#if defined(USE_ESP32)
return pcTaskGetName(task);
#elif defined(USE_LIBRETINY)
return pcTaskGetTaskName(task);
#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR)
const char *HOT get_thread_name_(
#ifdef USE_ZEPHYR
char *buff
#endif
}
// Convenience overload - gets the current task handle and delegates
const char *HOT get_thread_name_() { return this->get_thread_name_(xTaskGetCurrentTaskHandle()); }
#elif defined(USE_HOST)
// Takes a caller-provided buffer for the thread name (stack-allocated for thread safety)
const char *HOT get_thread_name_(std::span<char> buff) {
pthread_t current_thread = pthread_self();
if (pthread_equal(current_thread, main_thread_)) {
return nullptr; // Main thread
}
// For non-main threads, get the thread name into the caller-provided buffer
if (pthread_getname_np(current_thread, buff.data(), buff.size()) == 0) {
return buff.data();
}
return nullptr;
}
#elif defined(USE_ZEPHYR)
const char *HOT get_thread_name_(std::span<char> buff) {
) {
#ifdef USE_ZEPHYR
k_tid_t current_task = k_current_get();
#else
TaskHandle_t current_task = xTaskGetCurrentTaskHandle();
#endif
if (current_task == main_task_) {
return nullptr; // Main task
} else {
#if defined(USE_ESP32)
return pcTaskGetName(current_task);
#elif defined(USE_LIBRETINY)
return pcTaskGetTaskName(current_task);
#elif defined(USE_ZEPHYR)
const char *name = k_thread_name_get(current_task);
if (name) {
// zephyr print task names only if debug component is present
return name;
}
std::snprintf(buff, MAX_POINTER_REPRESENTATION, "%p", current_task);
return buff;
#endif
}
const char *name = k_thread_name_get(current_task);
if (name) {
// zephyr print task names only if debug component is present
return name;
}
std::snprintf(buff.data(), buff.size(), "%p", current_task);
return buff.data();
}
#endif
// --- Non-main task recursion guards (per-platform) ---
#if defined(USE_ESP32) || defined(USE_HOST)
// RAII guard for non-main task recursion using pthread TLS
class NonMainTaskRecursionGuard {
@@ -651,6 +635,22 @@ class Logger : public Component {
inline RecursionGuard make_non_main_task_guard_() { return RecursionGuard(non_main_task_recursion_guard_); }
#endif
#ifdef USE_HOST
const char *HOT get_thread_name_() {
pthread_t current_thread = pthread_self();
if (pthread_equal(current_thread, main_thread_)) {
return nullptr; // Main thread
}
// For non-main threads, return the thread name
// We store it in thread-local storage to avoid allocation
static thread_local char thread_name_buf[32];
if (pthread_getname_np(current_thread, thread_name_buf, sizeof(thread_name_buf)) == 0) {
return thread_name_buf;
}
return nullptr;
}
#endif
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
// Disable loop when task buffer is empty (with USB CDC check on ESP32)
inline void disable_loop_when_buffer_empty_() {

View File

@@ -59,7 +59,7 @@ void TaskLogBuffer::release_message_main_loop(void *token) {
last_processed_counter_ = message_counter_.load(std::memory_order_relaxed);
}
bool TaskLogBuffer::send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, const char *thread_name,
bool TaskLogBuffer::send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, TaskHandle_t task_handle,
const char *format, va_list args) {
// First, calculate the exact length needed using a null buffer (no actual writing)
va_list args_copy;
@@ -95,6 +95,7 @@ bool TaskLogBuffer::send_message_thread_safe(uint8_t level, const char *tag, uin
// Store the thread name now instead of waiting until main loop processing
// This avoids crashes if the task completes or is deleted between when this message
// is enqueued and when it's processed by the main loop
const char *thread_name = pcTaskGetName(task_handle);
if (thread_name != nullptr) {
strncpy(msg->thread_name, thread_name, sizeof(msg->thread_name) - 1);
msg->thread_name[sizeof(msg->thread_name) - 1] = '\0'; // Ensure null termination

View File

@@ -58,7 +58,7 @@ class TaskLogBuffer {
void release_message_main_loop(void *token);
// Thread-safe - send a message to the ring buffer from any thread
bool send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, const char *thread_name,
bool send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, TaskHandle_t task_handle,
const char *format, va_list args);
// Check if there are messages ready to be processed using an atomic counter for performance

View File

@@ -70,8 +70,8 @@ void TaskLogBufferHost::commit_write_slot_(int slot_index) {
}
}
bool TaskLogBufferHost::send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, const char *thread_name,
const char *format, va_list args) {
bool TaskLogBufferHost::send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, const char *format,
va_list args) {
// Acquire a slot
int slot_index = this->acquire_write_slot_();
if (slot_index < 0) {
@@ -85,9 +85,11 @@ bool TaskLogBufferHost::send_message_thread_safe(uint8_t level, const char *tag,
msg.tag = tag;
msg.line = line;
// Store the thread name now to avoid crashes if thread exits before processing
if (thread_name != nullptr) {
strncpy(msg.thread_name, thread_name, sizeof(msg.thread_name) - 1);
// Get thread name using pthread
char thread_name_buf[LogMessage::MAX_THREAD_NAME_SIZE];
// pthread_getname_np works the same on Linux and macOS
if (pthread_getname_np(pthread_self(), thread_name_buf, sizeof(thread_name_buf)) == 0) {
strncpy(msg.thread_name, thread_name_buf, sizeof(msg.thread_name) - 1);
msg.thread_name[sizeof(msg.thread_name) - 1] = '\0';
} else {
msg.thread_name[0] = '\0';

View File

@@ -86,8 +86,7 @@ class TaskLogBufferHost {
// Thread-safe - send a message to the buffer from any thread
// Returns true if message was queued, false if buffer is full
bool send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, const char *thread_name,
const char *format, va_list args);
bool send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, const char *format, va_list args);
// Check if there are messages ready to be processed
inline bool HOT has_messages() const {

View File

@@ -101,7 +101,7 @@ void TaskLogBufferLibreTiny::release_message_main_loop() {
}
bool TaskLogBufferLibreTiny::send_message_thread_safe(uint8_t level, const char *tag, uint16_t line,
const char *thread_name, const char *format, va_list args) {
TaskHandle_t task_handle, const char *format, va_list args) {
// First, calculate the exact length needed using a null buffer (no actual writing)
va_list args_copy;
va_copy(args_copy, args);
@@ -162,6 +162,7 @@ bool TaskLogBufferLibreTiny::send_message_thread_safe(uint8_t level, const char
msg->line = line;
// Store the thread name now to avoid crashes if task is deleted before processing
const char *thread_name = pcTaskGetTaskName(task_handle);
if (thread_name != nullptr) {
strncpy(msg->thread_name, thread_name, sizeof(msg->thread_name) - 1);
msg->thread_name[sizeof(msg->thread_name) - 1] = '\0';

View File

@@ -70,7 +70,7 @@ class TaskLogBufferLibreTiny {
void release_message_main_loop();
// Thread-safe - send a message to the buffer from any thread
bool send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, const char *thread_name,
bool send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, TaskHandle_t task_handle,
const char *format, va_list args);
// Fast check using volatile counter - no lock needed

View File

@@ -20,10 +20,10 @@ void Modbus::loop() {
const uint32_t now = App.get_loop_component_start_time();
// Read all available bytes in batches to reduce UART call overhead.
size_t avail = this->available();
int avail = this->available();
uint8_t buf[64];
while (avail > 0) {
size_t to_read = std::min(avail, sizeof(buf));
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}

View File

@@ -398,10 +398,10 @@ bool Nextion::remove_from_q_(bool report_empty) {
void Nextion::process_serial_() {
// Read all available bytes in batches to reduce UART call overhead.
size_t avail = this->available();
int avail = this->available();
uint8_t buf[64];
while (avail > 0) {
size_t to_read = std::min(avail, sizeof(buf));
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}

View File

@@ -14,9 +14,9 @@ void Pipsolar::setup() {
void Pipsolar::empty_uart_buffer_() {
uint8_t buf[64];
size_t avail;
int avail;
while ((avail = this->available()) > 0) {
if (!this->read_array(buf, std::min(avail, sizeof(buf)))) {
if (!this->read_array(buf, std::min(static_cast<size_t>(avail), sizeof(buf)))) {
break;
}
}
@@ -97,10 +97,10 @@ void Pipsolar::loop() {
}
if (this->state_ == STATE_COMMAND || this->state_ == STATE_POLL) {
size_t avail = this->available();
int avail = this->available();
while (avail > 0) {
uint8_t buf[64];
size_t to_read = std::min(avail, sizeof(buf));
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}

View File

@@ -56,23 +56,17 @@ void PylontechComponent::setup() {
void PylontechComponent::update() { this->write_str("pwr\n"); }
void PylontechComponent::loop() {
size_t avail = this->available();
if (avail > 0) {
if (this->available() > 0) {
// pylontech sends a lot of data very suddenly
// we need to quickly put it all into our own buffer, otherwise the uart's buffer will overflow
uint8_t data;
int recv = 0;
uint8_t buf[64];
while (avail > 0) {
size_t to_read = std::min(avail, sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}
avail -= to_read;
recv += to_read;
for (size_t i = 0; i < to_read; i++) {
buffer_[buffer_index_write_] += (char) buf[i];
if (buf[i] == ASCII_LF || buffer_[buffer_index_write_].length() >= MAX_DATA_LENGTH_BYTES) {
while (this->available() > 0) {
if (this->read_byte(&data)) {
buffer_[buffer_index_write_] += (char) data;
recv++;
if (buffer_[buffer_index_write_].back() == static_cast<char>(ASCII_LF) ||
buffer_[buffer_index_write_].length() >= MAX_DATA_LENGTH_BYTES) {
// complete line received
buffer_index_write_ = (buffer_index_write_ + 1) % NUM_BUFFERS;
}

View File

@@ -82,10 +82,10 @@ void RD03DComponent::dump_config() {
void RD03DComponent::loop() {
// Read all available bytes in batches to reduce UART call overhead.
size_t avail = this->available();
int avail = this->available();
uint8_t buf[64];
while (avail > 0) {
size_t to_read = std::min(avail, sizeof(buf));
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}

View File

@@ -136,10 +136,10 @@ void RFBridgeComponent::loop() {
this->last_bridge_byte_ = now;
}
size_t avail = this->available();
int avail = this->available();
while (avail > 0) {
uint8_t buf[64];
size_t to_read = std::min(avail, sizeof(buf));
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}

View File

@@ -107,10 +107,10 @@ void MR24HPC1Component::update_() {
// main loop
void MR24HPC1Component::loop() {
// Read all available bytes in batches to reduce UART call overhead.
size_t avail = this->available();
int avail = this->available();
uint8_t buf[64];
while (avail > 0) {
size_t to_read = std::min(avail, sizeof(buf));
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}

View File

@@ -31,10 +31,10 @@ void MR60BHA2Component::dump_config() {
// main loop
void MR60BHA2Component::loop() {
// Read all available bytes in batches to reduce UART call overhead.
size_t avail = this->available();
int avail = this->available();
uint8_t buf[64];
while (avail > 0) {
size_t to_read = std::min(avail, sizeof(buf));
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}

View File

@@ -50,10 +50,10 @@ void MR60FDA2Component::setup() {
// main loop
void MR60FDA2Component::loop() {
// Read all available bytes in batches to reduce UART call overhead.
size_t avail = this->available();
int avail = this->available();
uint8_t buf[64];
while (avail > 0) {
size_t to_read = std::min(avail, sizeof(buf));
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}

View File

@@ -32,10 +32,10 @@ void Tuya::setup() {
void Tuya::loop() {
// Read all available bytes in batches to reduce UART call overhead.
size_t avail = this->available();
int avail = this->available();
uint8_t buf[64];
while (avail > 0) {
size_t to_read = std::min(avail, sizeof(buf));
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}

View File

@@ -3,16 +3,12 @@
#include "esphome/core/defines.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "esphome/core/progmem.h"
#include <cinttypes>
namespace esphome::uart {
static const char *const TAG = "uart";
// UART parity strings indexed by UARTParityOptions enum (0-2): NONE, EVEN, ODD
PROGMEM_STRING_TABLE(UARTParityStrings, "NONE", "EVEN", "ODD", "UNKNOWN");
void UARTDevice::check_uart_settings(uint32_t baud_rate, uint8_t stop_bits, UARTParityOptions parity,
uint8_t data_bits) {
if (this->parent_->get_baud_rate() != baud_rate) {
@@ -34,7 +30,16 @@ void UARTDevice::check_uart_settings(uint32_t baud_rate, uint8_t stop_bits, UART
}
const LogString *parity_to_str(UARTParityOptions parity) {
return UARTParityStrings::get_log_str(static_cast<uint8_t>(parity), UARTParityStrings::LAST_INDEX);
switch (parity) {
case UART_CONFIG_PARITY_NONE:
return LOG_STR("NONE");
case UART_CONFIG_PARITY_EVEN:
return LOG_STR("EVEN");
case UART_CONFIG_PARITY_ODD:
return LOG_STR("ODD");
default:
return LOG_STR("UNKNOWN");
}
}
} // namespace esphome::uart