mirror of
https://github.com/esphome/esphome.git
synced 2026-10-27 08:22:25 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8807ba2138 | ||
|
|
1cb57e7909 | ||
|
|
25befeb570 |
No files matched your search
@@ -1,7 +1,6 @@
|
||||
import logging
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.const import CONF_ON_NUMERIC_COMPARISON_REQUEST
|
||||
from esphome.components.nrf52.const import BOOTLOADER_ADAFRUIT
|
||||
from esphome.components.ota import (
|
||||
BASE_OTA_SCHEMA,
|
||||
@@ -13,7 +12,10 @@ from esphome.components.ota import (
|
||||
)
|
||||
from esphome.components.zephyr import zephyr_data
|
||||
from esphome.components.zephyr.const import KEY_BOOTLOADER
|
||||
from esphome.components.zephyr_ble_server import DOMAIN as ZEPHYR_BLE_SERVER_DOMAIN
|
||||
from esphome.components.zephyr_ble_server import (
|
||||
CONF_ON_NUMERIC_COMPARISON_REQUEST,
|
||||
DOMAIN as ZEPHYR_BLE_SERVER_DOMAIN,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_ON_ERROR, Framework
|
||||
from esphome.core import CoroPriority, coroutine_with_priority
|
||||
|
||||
@@ -5,7 +5,6 @@ from esphome import automation
|
||||
from esphome.automation import maybe_simple_id
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import esp32_ble, esp32_ble_client, esp32_ble_tracker
|
||||
from esphome.components.const import CONF_ACCEPT, CONF_ON_NUMERIC_COMPARISON_REQUEST
|
||||
from esphome.components.esp32_ble import BTLoggers
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
@@ -102,8 +101,10 @@ BLERemoveBondAction = ble_client_ns.class_(
|
||||
)
|
||||
|
||||
CONF_PASSKEY = "passkey"
|
||||
CONF_ACCEPT = "accept"
|
||||
CONF_ON_PASSKEY_REQUEST = "on_passkey_request"
|
||||
CONF_ON_PASSKEY_NOTIFICATION = "on_passkey_notification"
|
||||
CONF_ON_NUMERIC_COMPARISON_REQUEST = "on_numeric_comparison_request"
|
||||
CONF_AUTO_CONNECT = "auto_connect"
|
||||
|
||||
MULTI_CONF = True
|
||||
|
||||
@@ -8,7 +8,6 @@ BYTE_ORDER_BIG = "big_endian"
|
||||
|
||||
CONF_ACCELEROMETER_ODR = "accelerometer_odr"
|
||||
CONF_ACCELEROMETER_RANGE = "accelerometer_range"
|
||||
CONF_ACCEPT = "accept"
|
||||
CONF_ADC_AVERAGING = "adc_averaging"
|
||||
CONF_ADC_TIME = "adc_time"
|
||||
CONF_ALLOWED_IPS = "allowed_ips"
|
||||
@@ -59,7 +58,6 @@ CONF_MAX_PACKET_SIZE = "max_packet_size"
|
||||
CONF_MODEM = "modem"
|
||||
CONF_MODULATION = "modulation"
|
||||
CONF_NOX_INDEX = "nox_index"
|
||||
CONF_ON_NUMERIC_COMPARISON_REQUEST = "on_numeric_comparison_request"
|
||||
CONF_ON_PACKET = "on_packet"
|
||||
CONF_ON_RECEIVE = "on_receive"
|
||||
CONF_ON_SCAN_END = "on_scan_end"
|
||||
|
||||
@@ -25,86 +25,16 @@ extern "C" {
|
||||
#include <freertos/FreeRTOSConfig.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
// Prevent Arduino from releasing BT memory at startup (esp32-hal-misc.c).
|
||||
// Without this, esp_bt_controller_init() fails with ESP_ERR_INVALID_STATE.
|
||||
extern "C" bool btInUse() { return true; } // NOLINT(readability-identifier-naming)
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_BT_CONTROLLER_DISABLED) && \
|
||||
(defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3))
|
||||
// The ESP32-C3 and ESP32-S3 share one controller (lib_esp32c3_family, RivieraWaves link layer).
|
||||
#define USE_ESP32_BLE_TERMINATE_PATCH
|
||||
// Controller internals, reached through r_ip_funcs_p: a table btdm_controller_init() copies into RAM.
|
||||
extern "C" {
|
||||
extern void *r_ip_funcs_p;
|
||||
const char *btdm_controller_get_compile_version();
|
||||
uint8_t r_lld_con_terminate_max_evt_update(uint8_t link_id, uint8_t acked);
|
||||
uint8_t *r_lld_con_cntl_pkt_info_get();
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace esphome::esp32_ble {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "esp32_ble");
|
||||
|
||||
#ifdef USE_ESP32_BLE_TERMINATE_PATCH
|
||||
// The controller ends its terminate procedure after 6 connection events without an acknowledgement and reports
|
||||
// the link closed. A peer using peripheral latency listens only every (latency + 1) events, so it can miss all
|
||||
// six LL_TERMINATE_IND: it keeps the link and searches for the central at full receive current until its own
|
||||
// supervision timeout. The Core spec keeps the procedure going until the acknowledgement or the supervision
|
||||
// timeout; this does that, by holding the controller's no-acknowledgement count below its limit. A peer that
|
||||
// never answers still ends the link at the supervision timeout.
|
||||
static constexpr size_t TERMINATE_MAX_EVT_SLOT = 0x7dc / sizeof(void *);
|
||||
static constexpr uint8_t TERMINATE_MAX_EVT = 5; // the controller gives up once its count exceeds this
|
||||
|
||||
static uint8_t IRAM_ATTR terminate_until_acked(uint8_t link_id, uint8_t acked) {
|
||||
// Per link: [2] terminate in progress, [3] connection events without an acknowledgement.
|
||||
uint8_t *info = r_lld_con_cntl_pkt_info_get() + link_id * 4;
|
||||
if (!acked && info[2] != 0 && info[3] >= TERMINATE_MAX_EVT)
|
||||
info[3] = TERMINATE_MAX_EVT - 1;
|
||||
return r_lld_con_terminate_max_evt_update(link_id, acked);
|
||||
}
|
||||
|
||||
// The controller builds whose r_lld_con_terminate_max_evt_update and r_lld_con_cntl_pkt_info_get the wrapper was
|
||||
// checked against. The table slot alone does not prove the pkt_info layout, so an unknown build is left alone.
|
||||
// btdm_controller_get_compile_version() is what ESP-IDF logs at init as "BT controller compile version [...]".
|
||||
// Recheck on every ESP-IDF bump: add the new build once both functions are confirmed unchanged, or drop the patch once
|
||||
// espressif/esp-idf#19189 is fixed.
|
||||
static const char *const TERMINATE_PATCH_CONTROLLER_BUILDS[] = {
|
||||
"51d9dfd", // ESP-IDF 5.5.5-5.5.6, Arduino 3.3.10-3.3.12
|
||||
"b7de11e", // ESP-IDF 5.5.4, Arduino 3.3.8-3.3.9: both functions identical to 51d9dfd's
|
||||
};
|
||||
|
||||
static void patch_terminate_until_acked() {
|
||||
auto **table = static_cast<void **>(r_ip_funcs_p);
|
||||
if (table == nullptr) {
|
||||
ESP_LOGW(TAG, "Controller layout not recognised; disconnects keep the controller's 6-event limit");
|
||||
return;
|
||||
}
|
||||
const char *build = btdm_controller_get_compile_version();
|
||||
bool known_build = false;
|
||||
for (const char *known : TERMINATE_PATCH_CONTROLLER_BUILDS)
|
||||
known_build = known_build || strcmp(build, known) == 0;
|
||||
if (!known_build) {
|
||||
// Expected after an ESP-IDF bump, and nothing a user can act on
|
||||
ESP_LOGD(TAG,
|
||||
"Controller build %s not checked for the terminate patch; disconnects keep the controller's 6-event limit",
|
||||
build);
|
||||
return;
|
||||
}
|
||||
if (table[TERMINATE_MAX_EVT_SLOT] == reinterpret_cast<void *>(&terminate_until_acked))
|
||||
return; // already patched: the controller stayed enabled across a setup
|
||||
if (table[TERMINATE_MAX_EVT_SLOT] != reinterpret_cast<void *>(&r_lld_con_terminate_max_evt_update)) {
|
||||
ESP_LOGW(TAG, "Controller build %s has an unexpected terminate handler; terminate patch skipped", build);
|
||||
return;
|
||||
}
|
||||
table[TERMINATE_MAX_EVT_SLOT] = reinterpret_cast<void *>(&terminate_until_acked);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_CONTROLLER_DISABLED
|
||||
// Bringing up the remote BT controller issues synchronous RPCs to the
|
||||
// co-processor with 5 second response timeouts, and the default task watchdog
|
||||
@@ -293,9 +223,6 @@ bool ESP32BLE::ble_setup_() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#ifdef USE_ESP32_BLE_TERMINATE_PATCH
|
||||
patch_terminate_until_acked();
|
||||
#endif
|
||||
|
||||
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
|
||||
#else
|
||||
|
||||
@@ -2,7 +2,6 @@ from dataclasses import dataclass
|
||||
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.const import CONF_ACCEPT, CONF_ON_NUMERIC_COMPARISON_REQUEST
|
||||
from esphome.components.nrf52.framework import (
|
||||
bluetooth_west_projects,
|
||||
include_west_project,
|
||||
@@ -55,6 +54,8 @@ def request_ble_l2cap_mtu(l2cap_mtu: int) -> None:
|
||||
zephyr_ble_server_ns = cg.esphome_ns.namespace("zephyr_ble_server")
|
||||
BLEServer = zephyr_ble_server_ns.class_("BLEServer", cg.Component)
|
||||
|
||||
CONF_ON_NUMERIC_COMPARISON_REQUEST = "on_numeric_comparison_request"
|
||||
CONF_ACCEPT = "accept"
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
|
||||
@@ -1932,23 +1932,28 @@ def test_start_udp_probe_succeeds(mock_socket: Mock) -> None:
|
||||
def test_udp_channel_pings_when_all_held() -> None:
|
||||
"""With every message in flight already held, it asks for responses instead,
|
||||
at most once per resend timeout."""
|
||||
device = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
device.bind(("127.0.0.1", 0))
|
||||
device.settimeout(1.0)
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.connect(device.getsockname())
|
||||
channel = espota2.UdpChannel(sock, b"\x01\x01\x01\x01")
|
||||
channel.sendall(b"x")
|
||||
channel._queue[0].held = True
|
||||
with patch.object(channel, "_receive"):
|
||||
sock = Mock()
|
||||
# A hand-driven clock, so a slow runner cannot pass the resend timeout between pumps
|
||||
now = [100.0]
|
||||
with (
|
||||
patch.object(espota2, "time", Mock(monotonic=lambda: now[0])),
|
||||
patch("select.select", return_value=([], [], [])),
|
||||
):
|
||||
channel = espota2.UdpChannel(sock, b"\x01\x01\x01\x01")
|
||||
channel.sendall(b"x")
|
||||
channel._queue[0].held = True
|
||||
channel._pump()
|
||||
channel._pump()
|
||||
assert device.recv(2048)[0] == espota2.UDP_MSG_PING
|
||||
device.settimeout(0.1)
|
||||
with pytest.raises(TimeoutError):
|
||||
device.recv(2048)
|
||||
channel.close()
|
||||
device.close()
|
||||
pinged_at, rto = now[0], channel._rto()
|
||||
now[0] = pinged_at + rto - 0.01
|
||||
channel._pump()
|
||||
pings_before = [c.args[0][0] for c in sock.send.call_args_list]
|
||||
now[0] = pinged_at + rto
|
||||
channel._pump()
|
||||
assert pings_before == [espota2.UDP_MSG_PING]
|
||||
assert [c.args[0][0] for c in sock.send.call_args_list] == [
|
||||
espota2.UDP_MSG_PING
|
||||
] * 2
|
||||
|
||||
|
||||
def test_udp_channel_ignores_foreign_datagrams() -> None:
|
||||
|
||||
Reference in new issue
Block a user