Compare commits

...

3 Commits

Author SHA1 Message Date
J. Nick Koston
8f367571f8 Add precondition comment to process_dump_config_() 2026-02-09 20:01:43 -06:00
J. Nick Koston
1213774168 [core] Extract dump_config from Application::loop() hot path
Extract the dump_config block (version/chip info logging + per-component
dump_config iteration) into a separate noinline process_dump_config_()
method.

This code runs once at startup and once on API reconnect, but was
inlined into loop() which runs ~7000 times/minute. On ESP32 the
dump_config block compiled to ~140 bytes (38% of loop's 368 bytes),
including esp_chip_info(), integer division for revision formatting,
and multiple ESP_LOG calls. Extracting it frees ~2 icache lines for
the hot path.
2026-02-09 19:43:25 -06:00
Jonathan Swoboda
dcbb020479 [uart] Fix available() return type to size_t across components (#13898)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 20:02:41 -05:00
18 changed files with 69 additions and 60 deletions

View File

@@ -16,8 +16,8 @@ void CSE7766Component::loop() {
}
// Early return prevents updating last_transmission_ when no data is available.
int avail = this->available();
if (avail <= 0) {
size_t 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(static_cast<size_t>(avail), sizeof(buf));
size_t to_read = std::min(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.
int avail = this->available();
size_t avail = this->available();
uint8_t buf[64];
while (avail > 0) {
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
size_t to_read = std::min(avail, sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}

View File

@@ -120,9 +120,9 @@ void Dsmr::stop_requesting_data_() {
void Dsmr::drain_rx_buffer_() {
uint8_t buf[64];
int avail;
size_t avail;
while ((avail = this->available()) > 0) {
if (!this->read_array(buf, std::min(static_cast<size_t>(avail), sizeof(buf)))) {
if (!this->read_array(buf, std::min(avail, sizeof(buf)))) {
break;
}
}
@@ -140,9 +140,9 @@ void Dsmr::receive_telegram_() {
while (this->available_within_timeout_()) {
// Read all available bytes in batches to reduce UART call overhead.
uint8_t buf[64];
int avail = this->available();
size_t avail = this->available();
while (avail > 0) {
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
size_t to_read = std::min(avail, sizeof(buf));
if (!this->read_array(buf, to_read))
return;
avail -= to_read;
@@ -206,9 +206,9 @@ 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];
int avail = this->available();
size_t avail = this->available();
while (avail > 0) {
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
size_t to_read = std::min(avail, sizeof(buf));
if (!this->read_array(buf, to_read))
return;
avail -= to_read;

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.
int avail = this->available();
size_t avail = this->available();
uint8_t buf[MAX_LINE_LENGTH];
while (avail > 0) {
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
size_t to_read = std::min(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.
int avail = this->available();
size_t avail = this->available();
uint8_t buf[MAX_LINE_LENGTH];
while (avail > 0) {
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
size_t to_read = std::min(avail, sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}

View File

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

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

View File

@@ -56,14 +56,14 @@ void PylontechComponent::setup() {
void PylontechComponent::update() { this->write_str("pwr\n"); }
void PylontechComponent::loop() {
int avail = this->available();
size_t avail = this->available();
if (avail > 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
int recv = 0;
uint8_t buf[64];
while (avail > 0) {
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
size_t to_read = std::min(avail, sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}

View File

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

View File

@@ -204,36 +204,40 @@ void Application::loop() {
this->last_loop_ = last_op_end_time;
if (this->dump_config_at_ < this->components_.size()) {
if (this->dump_config_at_ == 0) {
char build_time_str[Application::BUILD_TIME_STR_SIZE];
this->get_build_time_string(build_time_str);
ESP_LOGI(TAG, "ESPHome version " ESPHOME_VERSION " compiled on %s", build_time_str);
this->process_dump_config_();
}
}
void Application::process_dump_config_() {
if (this->dump_config_at_ == 0) {
char build_time_str[Application::BUILD_TIME_STR_SIZE];
this->get_build_time_string(build_time_str);
ESP_LOGI(TAG, "ESPHome version " ESPHOME_VERSION " compiled on %s", build_time_str);
#ifdef ESPHOME_PROJECT_NAME
ESP_LOGI(TAG, "Project " ESPHOME_PROJECT_NAME " version " ESPHOME_PROJECT_VERSION);
ESP_LOGI(TAG, "Project " ESPHOME_PROJECT_NAME " version " ESPHOME_PROJECT_VERSION);
#endif
#ifdef USE_ESP32
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
ESP_LOGI(TAG, "ESP32 Chip: %s rev%d.%d, %d core(s)", ESPHOME_VARIANT, chip_info.revision / 100,
chip_info.revision % 100, chip_info.cores);
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
ESP_LOGI(TAG, "ESP32 Chip: %s rev%d.%d, %d core(s)", ESPHOME_VARIANT, chip_info.revision / 100,
chip_info.revision % 100, chip_info.cores);
#if defined(USE_ESP32_VARIANT_ESP32) && !defined(USE_ESP32_MIN_CHIP_REVISION_SET)
// Suggest optimization for chips that don't need the PSRAM cache workaround
if (chip_info.revision >= 300) {
// Suggest optimization for chips that don't need the PSRAM cache workaround
if (chip_info.revision >= 300) {
#ifdef USE_PSRAM
ESP_LOGW(TAG, "Set minimum_chip_revision: \"%d.%d\" to save ~10KB IRAM", chip_info.revision / 100,
chip_info.revision % 100);
ESP_LOGW(TAG, "Set minimum_chip_revision: \"%d.%d\" to save ~10KB IRAM", chip_info.revision / 100,
chip_info.revision % 100);
#else
ESP_LOGW(TAG, "Set minimum_chip_revision: \"%d.%d\" to reduce binary size", chip_info.revision / 100,
chip_info.revision % 100);
#endif
}
#endif
ESP_LOGW(TAG, "Set minimum_chip_revision: \"%d.%d\" to reduce binary size", chip_info.revision / 100,
chip_info.revision % 100);
#endif
}
this->components_[this->dump_config_at_]->call_dump_config();
this->dump_config_at_++;
#endif
#endif
}
this->components_[this->dump_config_at_]->call_dump_config();
this->dump_config_at_++;
}
void IRAM_ATTR HOT Application::feed_wdt(uint32_t time) {

View File

@@ -519,6 +519,11 @@ class Application {
void before_loop_tasks_(uint32_t loop_start_time);
void after_loop_tasks_();
/// Process dump_config output one component per loop iteration.
/// Extracted from loop() to keep cold startup/reconnect logging out of the hot path.
/// Caller must ensure dump_config_at_ < components_.size().
void __attribute__((noinline)) process_dump_config_();
void feed_wdt_arch_();
/// Perform a delay while also monitoring socket file descriptors for readiness