mirror of
https://github.com/esphome/esphome.git
synced 2026-07-10 17:05:36 +00:00
Merge remote-tracking branch 'upstream/dev' into integration
This commit is contained in:
@@ -868,7 +868,8 @@ jobs:
|
||||
python script/test_build_components.py \
|
||||
-e compile \
|
||||
-c "$component_list" \
|
||||
-t "$platform" 2>&1 | \
|
||||
-t "$platform" \
|
||||
--base-only 2>&1 | \
|
||||
tee /dev/stderr | \
|
||||
python script/ci_memory_impact_extract.py \
|
||||
--output-env \
|
||||
@@ -954,7 +955,8 @@ jobs:
|
||||
python script/test_build_components.py \
|
||||
-e compile \
|
||||
-c "$component_list" \
|
||||
-t "$platform" 2>&1 | \
|
||||
-t "$platform" \
|
||||
--base-only 2>&1 | \
|
||||
tee /dev/stderr | \
|
||||
python script/ci_memory_impact_extract.py \
|
||||
--output-env \
|
||||
|
||||
@@ -11,7 +11,7 @@ ci:
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.15.9
|
||||
rev: v0.15.10
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
|
||||
@@ -195,7 +195,10 @@ class APIFrameHelper {
|
||||
}
|
||||
// Get the frame footer size required by this protocol
|
||||
uint8_t frame_footer_size() const { return frame_footer_size_; }
|
||||
// Check if socket has data ready to read
|
||||
// Check if socket has buffered data ready to read.
|
||||
// Contract: callers must read until it would block (EAGAIN/EWOULDBLOCK)
|
||||
// or track that they stopped early and retry without this check.
|
||||
// See Socket::ready() for details.
|
||||
bool is_socket_ready() const { return socket_ != nullptr && socket_->ready(); }
|
||||
// Release excess memory from internal buffers after initial sync
|
||||
void release_buffers() {
|
||||
|
||||
@@ -150,6 +150,10 @@ void CC1101Component::setup() {
|
||||
if (this->gdo0_pin_ != nullptr) {
|
||||
this->defer([this]() { this->gdo0_pin_->pin_mode(gpio::FLAG_INPUT); });
|
||||
}
|
||||
|
||||
if (this->state_.PKT_FORMAT != static_cast<uint8_t>(PacketFormat::PACKET_FORMAT_FIFO)) {
|
||||
this->disable_loop();
|
||||
}
|
||||
}
|
||||
|
||||
void CC1101Component::call_listeners_(const std::vector<uint8_t> &packet, float freq_offset, float rssi, uint8_t lqi) {
|
||||
@@ -669,6 +673,11 @@ void CC1101Component::set_packet_mode(bool value) {
|
||||
this->state_.GDO0_CFG = 0x0D;
|
||||
}
|
||||
if (this->initialized_) {
|
||||
if (value) {
|
||||
this->enable_loop();
|
||||
} else {
|
||||
this->disable_loop();
|
||||
}
|
||||
this->write_(Register::PKTCTRL0);
|
||||
this->write_(Register::PKTCTRL1);
|
||||
this->write_(Register::IOCFG0);
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace gdk101 {
|
||||
|
||||
static const char *const TAG = "gdk101";
|
||||
static constexpr uint8_t NUMBER_OF_READ_RETRIES = 5;
|
||||
static constexpr uint8_t NUMBER_OF_RESET_RETRIES = 10;
|
||||
static constexpr uint8_t NUMBER_OF_RESET_RETRIES = 30;
|
||||
static constexpr uint32_t RESET_INTERVAL_ID = 0;
|
||||
static constexpr uint32_t RESET_INTERVAL_MS = 1000;
|
||||
|
||||
|
||||
@@ -112,6 +112,8 @@ class BSDSocketImpl {
|
||||
int setblocking(bool blocking);
|
||||
int loop() { return 0; }
|
||||
|
||||
/// Check if the socket has buffered data ready to read.
|
||||
/// See the ready() contract in socket.h — callers must drain or track remaining data.
|
||||
bool ready() const;
|
||||
|
||||
int get_fd() const { return this->fd_; }
|
||||
|
||||
@@ -96,6 +96,8 @@ class LWIPRawImpl : public LWIPRawCommon {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
// Check if the socket has buffered data ready to read.
|
||||
// See the ready() contract in socket.h — callers must drain or track remaining data.
|
||||
// Intentionally unlocked — this is a polling check called every loop iteration.
|
||||
// A stale read at worst delays processing by one loop tick; the actual I/O in
|
||||
// read() holds the lwip lock and re-checks properly. See esphome#10681.
|
||||
|
||||
@@ -78,6 +78,8 @@ class LwIPSocketImpl {
|
||||
int setblocking(bool blocking);
|
||||
int loop() { return 0; }
|
||||
|
||||
/// Check if the socket has buffered data ready to read.
|
||||
/// See the ready() contract in socket.h — callers must drain or track remaining data.
|
||||
bool ready() const;
|
||||
|
||||
int get_fd() const { return this->fd_; }
|
||||
|
||||
@@ -53,6 +53,19 @@ bool socket_ready_fd(int fd, bool loop_monitored);
|
||||
|
||||
// Inline ready() — defined here because it depends on socket_ready/socket_ready_fd
|
||||
// declared above, while the impl headers are included before those declarations.
|
||||
//
|
||||
// Contract (applies to ALL socket implementations — each platform implements
|
||||
// ready() differently, but this contract holds regardless of the mechanism):
|
||||
// ready() checks if the socket has buffered data ready to read. When it returns
|
||||
// true, the caller MUST read until it would block (EAGAIN/EWOULDBLOCK), or until
|
||||
// read() returns 0 to indicate EOF / connection closed, or track that it stopped
|
||||
// early and retry without calling ready(). The next call to ready() will only
|
||||
// report new data correctly if all callers fulfill this contract. Failing to
|
||||
// drain the socket may cause ready() to return false while data remains readable.
|
||||
//
|
||||
// In practice each socket is owned by a single component, so this contract is
|
||||
// straightforward to fulfill — but the owning component must be aware of it,
|
||||
// especially if it limits how many messages it processes per loop iteration.
|
||||
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
|
||||
inline bool Socket::ready() const {
|
||||
#ifdef USE_LWIP_FAST_SELECT
|
||||
|
||||
@@ -383,9 +383,14 @@ void SX127x::set_mode_(uint8_t modulation, uint8_t mode) {
|
||||
if (millis() - start > 20) {
|
||||
ESP_LOGE(TAG, "Set mode failure");
|
||||
this->mark_failed();
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (mode == MODE_RX && (modulation == MOD_LORA || this->packet_mode_)) {
|
||||
this->enable_loop();
|
||||
} else {
|
||||
this->disable_loop();
|
||||
}
|
||||
}
|
||||
|
||||
void SX127x::set_mode_rx() {
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ platformio==6.1.19
|
||||
esptool==5.2.0
|
||||
click==8.3.2
|
||||
esphome-dashboard==20260408.1
|
||||
aioesphomeapi==44.12.0
|
||||
aioesphomeapi==44.13.1
|
||||
zeroconf==0.148.0
|
||||
puremagic==1.30
|
||||
ruamel.yaml==0.19.1 # dashboard_import
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pylint==4.0.5
|
||||
flake8==7.3.0 # also change in .pre-commit-config.yaml when updating
|
||||
ruff==0.15.9 # also change in .pre-commit-config.yaml when updating
|
||||
ruff==0.15.10 # also change in .pre-commit-config.yaml when updating
|
||||
pyupgrade==3.21.2 # also change in .pre-commit-config.yaml when updating
|
||||
pre-commit
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ namespace esphome::benchmarks {
|
||||
static constexpr int kInnerIterations = 2000;
|
||||
|
||||
// --- random_float() ---
|
||||
// Ported from ol.yaml:148 "Random Float Benchmark"
|
||||
|
||||
static void RandomFloat(benchmark::State &state) {
|
||||
for (auto _ : state) {
|
||||
@@ -38,4 +37,274 @@ static void RandomUint32(benchmark::State &state) {
|
||||
}
|
||||
BENCHMARK(RandomUint32);
|
||||
|
||||
// --- format_hex_to() - 6 bytes (MAC address sized) ---
|
||||
|
||||
static void FormatHexTo_6Bytes(benchmark::State &state) {
|
||||
const uint8_t data[] = {0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45};
|
||||
char buffer[13]; // 6 * 2 + 1
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
format_hex_to(buffer, data, 6);
|
||||
}
|
||||
benchmark::DoNotOptimize(buffer);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(FormatHexTo_6Bytes);
|
||||
|
||||
// --- format_hex_to() - 16 bytes (UUID sized) ---
|
||||
|
||||
static void FormatHexTo_16Bytes(benchmark::State &state) {
|
||||
const uint8_t data[] = {0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89,
|
||||
0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10};
|
||||
char buffer[33]; // 16 * 2 + 1
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
format_hex_to(buffer, data, 16);
|
||||
}
|
||||
benchmark::DoNotOptimize(buffer);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(FormatHexTo_16Bytes);
|
||||
|
||||
// --- format_hex_to() - 100 bytes (large payload) ---
|
||||
|
||||
static void FormatHexTo_100Bytes(benchmark::State &state) {
|
||||
uint8_t data[100];
|
||||
for (int i = 0; i < 100; i++) {
|
||||
data[i] = static_cast<uint8_t>(i);
|
||||
}
|
||||
char buffer[201]; // 100 * 2 + 1
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
format_hex_to(buffer, data, 100);
|
||||
}
|
||||
benchmark::DoNotOptimize(buffer);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(FormatHexTo_100Bytes);
|
||||
|
||||
// --- format_hex_pretty_to() - 6 bytes with ':' separator ---
|
||||
|
||||
static void FormatHexPrettyTo_6Bytes(benchmark::State &state) {
|
||||
const uint8_t data[] = {0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45};
|
||||
char buffer[18]; // 6 * 3
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
format_hex_pretty_to(buffer, data, 6);
|
||||
}
|
||||
benchmark::DoNotOptimize(buffer);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(FormatHexPrettyTo_6Bytes);
|
||||
|
||||
// --- format_mac_addr_upper() ---
|
||||
|
||||
static void FormatMacAddrUpper(benchmark::State &state) {
|
||||
const uint8_t mac[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
|
||||
char buffer[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
format_mac_addr_upper(mac, buffer);
|
||||
}
|
||||
benchmark::DoNotOptimize(buffer);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(FormatMacAddrUpper);
|
||||
|
||||
// --- fnv1_hash() - short string ---
|
||||
|
||||
static void Fnv1Hash_Short(benchmark::State &state) {
|
||||
const char *str = "sensor.temperature";
|
||||
for (auto _ : state) {
|
||||
uint32_t result = 0;
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
result ^= fnv1_hash(str);
|
||||
}
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(Fnv1Hash_Short);
|
||||
|
||||
// --- fnv1_hash() - long string ---
|
||||
|
||||
static void Fnv1Hash_Long(benchmark::State &state) {
|
||||
const char *str = "binary_sensor.living_room_motion_sensor_occupancy_detected";
|
||||
for (auto _ : state) {
|
||||
uint32_t result = 0;
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
result ^= fnv1_hash(str);
|
||||
}
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(Fnv1Hash_Long);
|
||||
|
||||
// --- fnv1a_hash() - short string ---
|
||||
// Use DoNotOptimize on the input pointer to prevent constexpr evaluation
|
||||
|
||||
static void Fnv1aHash_Short(benchmark::State &state) {
|
||||
const char *str = "sensor.temperature";
|
||||
benchmark::DoNotOptimize(str);
|
||||
for (auto _ : state) {
|
||||
uint32_t result = 0;
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
result ^= fnv1a_hash(str);
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(Fnv1aHash_Short);
|
||||
|
||||
// --- fnv1a_hash() - long string ---
|
||||
|
||||
static void Fnv1aHash_Long(benchmark::State &state) {
|
||||
const char *str = "binary_sensor.living_room_motion_sensor_occupancy_detected";
|
||||
benchmark::DoNotOptimize(str);
|
||||
for (auto _ : state) {
|
||||
uint32_t result = 0;
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
result ^= fnv1a_hash(str);
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(Fnv1aHash_Long);
|
||||
|
||||
// --- fnv1_hash_object_id() - typical entity name ---
|
||||
|
||||
static void Fnv1HashObjectId(benchmark::State &state) {
|
||||
char name[] = "Living Room Temperature Sensor";
|
||||
size_t len = sizeof(name) - 1;
|
||||
benchmark::DoNotOptimize(name);
|
||||
for (auto _ : state) {
|
||||
uint32_t result = 0;
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
result ^= fnv1_hash_object_id(name, len);
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(Fnv1HashObjectId);
|
||||
|
||||
// --- parse_hex() - 6 bytes from string ---
|
||||
|
||||
static void ParseHex_6Bytes(benchmark::State &state) {
|
||||
const char *hex_str = "ABCDEF012345";
|
||||
uint8_t data[6];
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
parse_hex(hex_str, data, 6);
|
||||
}
|
||||
benchmark::DoNotOptimize(data);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(ParseHex_6Bytes);
|
||||
|
||||
// --- parse_hex() - 16 bytes from string ---
|
||||
|
||||
static void ParseHex_16Bytes(benchmark::State &state) {
|
||||
const char *hex_str = "ABCDEF0123456789FEDCBA9876543210";
|
||||
uint8_t data[16];
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
parse_hex(hex_str, data, 16);
|
||||
}
|
||||
benchmark::DoNotOptimize(data);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(ParseHex_16Bytes);
|
||||
|
||||
// --- crc8() - 8 bytes ---
|
||||
|
||||
static void CRC8_8Bytes(benchmark::State &state) {
|
||||
const uint8_t data[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
|
||||
for (auto _ : state) {
|
||||
uint8_t result = 0;
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
result ^= crc8(data, 8);
|
||||
}
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(CRC8_8Bytes);
|
||||
|
||||
// --- crc16() - 8 bytes ---
|
||||
|
||||
static void CRC16_8Bytes(benchmark::State &state) {
|
||||
const uint8_t data[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
|
||||
for (auto _ : state) {
|
||||
uint16_t result = 0;
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
result ^= crc16(data, 8);
|
||||
}
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(CRC16_8Bytes);
|
||||
|
||||
// --- value_accuracy_to_buf() - typical sensor value ---
|
||||
|
||||
static void ValueAccuracyToBuf(benchmark::State &state) {
|
||||
char raw_buf[VALUE_ACCURACY_MAX_LEN] = {};
|
||||
std::span<char, VALUE_ACCURACY_MAX_LEN> buf(raw_buf);
|
||||
float value = 23.456f;
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
value_accuracy_to_buf(buf, value, 2);
|
||||
}
|
||||
benchmark::DoNotOptimize(raw_buf);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(ValueAccuracyToBuf);
|
||||
|
||||
// --- int8_to_str() ---
|
||||
|
||||
static void Int8ToStr(benchmark::State &state) {
|
||||
char buffer[5] = {};
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
int8_to_str(buffer, static_cast<int8_t>(i & 0xFF));
|
||||
benchmark::DoNotOptimize(buffer);
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(Int8ToStr);
|
||||
|
||||
// --- base64_decode() - into pre-allocated buffer ---
|
||||
|
||||
static void Base64Decode_32Bytes(benchmark::State &state) {
|
||||
// 32 bytes encoded = 44 base64 chars
|
||||
const uint8_t encoded[] = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGx0eHw==";
|
||||
size_t encoded_len = 44;
|
||||
uint8_t output[32];
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
base64_decode(encoded, encoded_len, output, sizeof(output));
|
||||
}
|
||||
benchmark::DoNotOptimize(output);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(Base64Decode_32Bytes);
|
||||
|
||||
} // namespace esphome::benchmarks
|
||||
|
||||
Reference in New Issue
Block a user