Compare commits

..

64 Commits

Author SHA1 Message Date
J. Nick Koston
217e5a4833 cleanups 2025-11-11 14:05:55 -06:00
J. Nick Koston
fd3689dd64 cleanup 2025-11-11 14:00:25 -06:00
J. Nick Koston
c166e063f9 cleanups 2025-11-11 13:55:29 -06:00
J. Nick Koston
8b6400f24c Fix scan failing after restart 2025-11-11 13:12:10 -06:00
J. Nick Koston
bbff660499 Merge branch 'integration' into memory_api 2025-11-11 12:19:33 -06:00
J. Nick Koston
e7409ac5cd Merge remote-tracking branch 'upstream/dev' into integration 2025-11-11 12:19:24 -06:00
tomaszduda23
661920c51e [nrf52,ssd1306_i2c] fix build error (#11847) 2025-11-11 18:18:17 +00:00
tomaszduda23
a6b905e148 [nrf52,pcf8563] fix build error (#11846)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick+github@koston.org>
2025-11-11 17:50:07 +00:00
J. Nick Koston
366e95f8d8 Merge branch 'integration' into memory_api 2025-11-11 11:25:11 -06:00
J. Nick Koston
326edd5082 Merge branch 'timezone' into integration 2025-11-11 11:25:03 -06:00
J. Nick Koston
d74fc6347b Update esphome/components/homeassistant/time/homeassistant_time.cpp 2025-11-11 11:24:41 -06:00
J. Nick Koston
dbbc4f741d Merge branch 'integration' into memory_api 2025-11-11 11:23:02 -06:00
J. Nick Koston
2d63b69ac1 Merge branch 'timezone' into integration 2025-11-11 11:22:55 -06:00
J. Nick Koston
a14e2d4d08 Update esphome/components/time/real_time_clock.cpp 2025-11-11 11:22:33 -06:00
J. Nick Koston
300bd420f8 Merge branch 'integration' into memory_api 2025-11-11 11:19:12 -06:00
J. Nick Koston
2d2472c50b Merge branch 'timezone' into integration 2025-11-11 11:19:03 -06:00
J. Nick Koston
2e115baf56 Merge remote-tracking branch 'tomaszduda23/timezone' into timezone 2025-11-11 11:17:47 -06:00
J. Nick Koston
b58b706bd6 fix 2025-11-11 11:17:05 -06:00
Tomasz Duda
d389ed585e fix 2025-11-11 18:13:02 +01:00
Tomasz Duda
1b30346c1e fix 2025-11-11 18:08:10 +01:00
Tomasz Duda
6b45debcba Merge remote-tracking branch 'origin/dev' into timezone 2025-11-11 18:00:20 +01:00
J. Nick Koston
e42b29659b Merge branch 'integration' into memory_api 2025-11-11 09:44:58 -06:00
J. Nick Koston
aba9ffccdf Merge branch 'retry_hidden_no_stuck_last_networks_visible' into integration 2025-11-11 09:44:52 -06:00
J. Nick Koston
8e29ae416e Update esphome/components/wifi/wifi_component.h
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-11 09:44:34 -06:00
J. Nick Koston
75c220eeb6 more tweaks for corner cases 2025-11-11 09:42:09 -06:00
tomaszduda23
a6b7c1f18c [nrf52,gpio] add gpio levels for high voltage mode (#9858)
Co-authored-by: J. Nick Koston <nick+github@koston.org>
2025-11-11 15:17:25 +00:00
J. Nick Koston
d2e1fbd76b Merge branch 'integration' into memory_api 2025-11-11 09:17:19 -06:00
J. Nick Koston
3bb7639470 Merge branch 'retry_hidden_no_stuck_last_networks_visible' into integration 2025-11-11 09:17:11 -06:00
J. Nick Koston
72a6051f0d [wifi] Fix infinite loop in RETRY_HIDDEN when remaining networks are visible 2025-11-11 09:16:31 -06:00
J. Nick Koston
649e27bf62 Merge branch 'integration' into memory_api 2025-11-11 08:53:53 -06:00
J. Nick Koston
77436b85b2 Merge branch 'lost_prio_decrease_merge_conflict_fix' into integration 2025-11-11 08:53:47 -06:00
J. Nick Koston
5f0957c81a Merge branch 'wifi_int8_prio' into lost_prio_decrease_merge_conflict_fix 2025-11-11 08:53:27 -06:00
J. Nick Koston
bee174150b fixes 2025-11-11 08:52:12 -06:00
J. Nick Koston
262f28aec5 Merge remote-tracking branch 'origin/wifi_int8_prio' into wifi_int8_prio 2025-11-11 08:51:10 -06:00
J. Nick Koston
bf312ad9ec fixes 2025-11-11 08:50:54 -06:00
Tomasz Duda
55cf0adb18 [nrf52,pcf8563] fix build error 2025-11-11 15:38:19 +01:00
J. Nick Koston
941feeedbe Merge branch 'dev' into wifi_int8_prio 2025-11-11 08:33:57 -06:00
J. Nick Koston
4565b126e2 Merge branch 'integration' into memory_api 2025-11-11 08:32:27 -06:00
J. Nick Koston
64651b5a07 Merge branch 'lost_prio_decrease_merge_conflict_fix' into integration 2025-11-11 08:31:33 -06:00
J. Nick Koston
f3007a5245 Merge branch 'wifi_manual_ip' into integration 2025-11-11 08:31:27 -06:00
J. Nick Koston
0e62c8b3fb Merge branch 'wifi_int8_prio' into lost_prio_decrease_merge_conflict_fix 2025-11-11 08:28:02 -06:00
J. Nick Koston
4160157457 [wifi] Restore two-attempt BSSID filtering for mesh networks 2025-11-11 08:26:15 -06:00
J. Nick Koston
75d7578491 Merge wifi_int8_prio into wifi_manual_ip
Changes priority type from float to int8_t for memory savings.
Resolves conflict with USE_WIFI_MANUAL_IP conditional compilation.
2025-11-11 08:10:39 -06:00
J. Nick Koston
f28566545f Merge branch 'integration' into memory_api 2025-11-10 22:25:51 -06:00
J. Nick Koston
dc37321aa9 Merge branch 'wifi_manual_ip' into integration 2025-11-10 22:25:43 -06:00
J. Nick Koston
89abd9c817 fix conflict 2025-11-10 22:24:22 -06:00
J. Nick Koston
d4d44a5c08 manual_ip test 2025-11-10 22:23:29 -06:00
J. Nick Koston
b8e4efc1cd manual_ip test 2025-11-10 22:23:02 -06:00
J. Nick Koston
cf66c4cd3e Merge branch 'integration' into memory_api 2025-11-10 22:14:11 -06:00
J. Nick Koston
4b60012814 Merge branch 'wifi_manual_ip' into integration
# Conflicts:
#	esphome/components/wifi/wifi_component.h
2025-11-10 22:13:44 -06:00
J. Nick Koston
c38df0af85 [wifi] Conditionally compile manual_ip to save 24-120 bytes RAM 2025-11-10 22:09:01 -06:00
J. Nick Koston
bb51c6b6d5 Merge branch 'integration' into memory_api 2025-11-10 21:59:32 -06:00
J. Nick Koston
b8f972b6f6 Merge branch 'ethernet_manual_ip_cond' into integration 2025-11-10 21:59:25 -06:00
J. Nick Koston
d87063865c [ethernet] Conditionally compile manual_ip to save 24 bytes RAM 2025-11-10 21:57:52 -06:00
J. Nick Koston
682b6711f3 Merge branch 'integration' into memory_api 2025-11-10 20:44:42 -06:00
J. Nick Koston
066674df19 Merge branch 'fix_wifi_state_machine_hidden_phase_skipped' into integration 2025-11-10 20:44:34 -06:00
J. Nick Koston
48a33611a1 [wifi] Fix infinite retry loop when no hidden networks and captive portal active 2025-11-10 20:43:32 -06:00
J. Nick Koston
caf6045485 Merge branch 'integration' into memory_api 2025-11-10 20:24:34 -06:00
J. Nick Koston
bd7d103813 Merge branch 'wifi_int8_prio' into integration 2025-11-10 20:24:26 -06:00
J. Nick Koston
6631e2ffb2 tweaks 2025-11-10 20:22:24 -06:00
J. Nick Koston
b80b0eb864 save more 2025-11-10 20:17:03 -06:00
Clyde Stubbs
7a700ca077 [core] Update clamp functions to allow mixed but comparable types (#11828) 2025-11-11 02:15:44 +00:00
J. Nick Koston
130a8b853d missed one 2025-11-10 20:14:40 -06:00
J. Nick Koston
0f02c75f66 [wifi] Change priority type from float to int8_t 2025-11-10 20:05:02 -06:00
33 changed files with 422 additions and 84 deletions

View File

@@ -23,7 +23,7 @@ void DS1307Component::dump_config() {
if (this->is_failed()) {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str());
RealTimeClock::dump_config();
}
float DS1307Component::get_setup_priority() const { return setup_priority::DATA; }

View File

@@ -383,6 +383,7 @@ async def to_code(config):
cg.add(var.set_use_address(config[CONF_USE_ADDRESS]))
if CONF_MANUAL_IP in config:
cg.add_define("USE_ETHERNET_MANUAL_IP")
cg.add(var.set_manual_ip(manual_ip(config[CONF_MANUAL_IP])))
# Add compile-time define for PHY types with specific code

View File

@@ -550,11 +550,14 @@ void EthernetComponent::start_connect_() {
}
esp_netif_ip_info_t info;
#ifdef USE_ETHERNET_MANUAL_IP
if (this->manual_ip_.has_value()) {
info.ip = this->manual_ip_->static_ip;
info.gw = this->manual_ip_->gateway;
info.netmask = this->manual_ip_->subnet;
} else {
} else
#endif
{
info.ip.addr = 0;
info.gw.addr = 0;
info.netmask.addr = 0;
@@ -575,6 +578,7 @@ void EthernetComponent::start_connect_() {
err = esp_netif_set_ip_info(this->eth_netif_, &info);
ESPHL_ERROR_CHECK(err, "DHCPC set IP info error");
#ifdef USE_ETHERNET_MANUAL_IP
if (this->manual_ip_.has_value()) {
LwIPLock lock;
if (this->manual_ip_->dns1.is_set()) {
@@ -587,7 +591,9 @@ void EthernetComponent::start_connect_() {
d = this->manual_ip_->dns2;
dns_setserver(1, &d);
}
} else {
} else
#endif
{
err = esp_netif_dhcpc_start(this->eth_netif_);
if (err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STARTED) {
ESPHL_ERROR_CHECK(err, "DHCPC start error");
@@ -685,7 +691,9 @@ void EthernetComponent::set_clk_mode(emac_rmii_clock_mode_t clk_mode) { this->cl
void EthernetComponent::add_phy_register(PHYRegister register_value) { this->phy_registers_.push_back(register_value); }
#endif
void EthernetComponent::set_type(EthernetType type) { this->type_ = type; }
#ifdef USE_ETHERNET_MANUAL_IP
void EthernetComponent::set_manual_ip(const ManualIP &manual_ip) { this->manual_ip_ = manual_ip; }
#endif
// set_use_address() is guaranteed to be called during component setup by Python code generation,
// so use_address_ will always be valid when get_use_address() is called - no fallback needed.

View File

@@ -82,7 +82,9 @@ class EthernetComponent : public Component {
void add_phy_register(PHYRegister register_value);
#endif
void set_type(EthernetType type);
#ifdef USE_ETHERNET_MANUAL_IP
void set_manual_ip(const ManualIP &manual_ip);
#endif
void set_fixed_mac(const std::array<uint8_t, 6> &mac) { this->fixed_mac_ = mac; }
network::IPAddresses get_ip_addresses();
@@ -137,7 +139,9 @@ class EthernetComponent : public Component {
uint8_t mdc_pin_{23};
uint8_t mdio_pin_{18};
#endif
#ifdef USE_ETHERNET_MANUAL_IP
optional<ManualIP> manual_ip_{};
#endif
uint32_t connect_begin_;
// Group all uint8_t types together (enums and bools)

View File

@@ -7,10 +7,8 @@ namespace homeassistant {
static const char *const TAG = "homeassistant.time";
void HomeassistantTime::dump_config() {
ESP_LOGCONFIG(TAG,
"Home Assistant Time:\n"
" Timezone: '%s'",
this->timezone_.c_str());
ESP_LOGCONFIG(TAG, "Home Assistant Time");
RealTimeClock::dump_config();
}
float HomeassistantTime::get_setup_priority() const { return setup_priority::DATA; }

View File

@@ -25,6 +25,7 @@ from esphome.const import (
CONF_FRAMEWORK,
CONF_ID,
CONF_RESET_PIN,
CONF_VOLTAGE,
KEY_CORE,
KEY_FRAMEWORK_VERSION,
KEY_TARGET_FRAMEWORK,
@@ -102,6 +103,11 @@ nrf52_ns = cg.esphome_ns.namespace("nrf52")
DeviceFirmwareUpdate = nrf52_ns.class_("DeviceFirmwareUpdate", cg.Component)
CONF_DFU = "dfu"
CONF_REG0 = "reg0"
CONF_UICR_ERASE = "uicr_erase"
VOLTAGE_LEVELS = [1.8, 2.1, 2.4, 2.7, 3.0, 3.3]
DEFAULT_VOLTAGE_LEVEL = "default"
CONFIG_SCHEMA = cv.All(
_detect_bootloader,
@@ -116,6 +122,18 @@ CONFIG_SCHEMA = cv.All(
cv.Required(CONF_RESET_PIN): pins.gpio_output_pin_schema,
}
),
cv.Optional(CONF_REG0): cv.Schema(
{
cv.Required(CONF_VOLTAGE): cv.Any(
cv.All(
cv.voltage,
cv.one_of(*VOLTAGE_LEVELS, float=True),
),
cv.one_of(*[DEFAULT_VOLTAGE_LEVEL], lower=True),
),
cv.Optional(CONF_UICR_ERASE, default=False): cv.boolean,
}
),
}
),
)
@@ -183,6 +201,14 @@ async def to_code(config: ConfigType) -> None:
if dfu_config := config.get(CONF_DFU):
CORE.add_job(_dfu_to_code, dfu_config)
if reg0_config := config.get(CONF_REG0):
value = 7 # DEFAULT_VOLTAGE_LEVEL
if reg0_config[CONF_VOLTAGE] in VOLTAGE_LEVELS:
value = VOLTAGE_LEVELS.index(reg0_config[CONF_VOLTAGE])
cg.add_define("USE_NRF52_REG0_VOUT", value)
if reg0_config[CONF_UICR_ERASE]:
cg.add_define("USE_NRF52_UICR_ERASE")
@coroutine_with_priority(CoroPriority.DIAGNOSTICS)
async def _dfu_to_code(dfu_config):

View File

@@ -0,0 +1,110 @@
#include "esphome/core/defines.h"
#ifdef USE_NRF52_REG0_VOUT
#include <zephyr/init.h>
#include <hal/nrf_power.h>
#include <zephyr/sys/printk.h>
extern "C" {
void nvmc_config(uint32_t mode);
void nvmc_wait();
nrfx_err_t nrfx_nvmc_uicr_erase();
}
namespace esphome::nrf52 {
enum class StatusFlags : uint8_t {
OK = 0x00,
NEED_RESET = 0x01,
NEED_ERASE = 0x02,
};
constexpr StatusFlags &operator|=(StatusFlags &a, StatusFlags b) {
a = static_cast<StatusFlags>(static_cast<uint8_t>(a) | static_cast<uint8_t>(b));
return a;
}
constexpr bool operator&(StatusFlags a, StatusFlags b) {
return (static_cast<uint8_t>(a) & static_cast<uint8_t>(b)) != 0;
}
static bool regout0_ok() {
return (NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) == (USE_NRF52_REG0_VOUT << UICR_REGOUT0_VOUT_Pos);
}
static StatusFlags set_regout0() {
/* If the board is powered from USB (high voltage mode),
* GPIO output voltage is set to 1.8 volts by default.
*/
if (!regout0_ok()) {
nvmc_config(NVMC_CONFIG_WEN_Wen);
NRF_UICR->REGOUT0 =
(NRF_UICR->REGOUT0 & ~((uint32_t) UICR_REGOUT0_VOUT_Msk)) | (USE_NRF52_REG0_VOUT << UICR_REGOUT0_VOUT_Pos);
nvmc_wait();
nvmc_config(NVMC_CONFIG_WEN_Ren);
return regout0_ok() ? StatusFlags::NEED_RESET : StatusFlags::NEED_ERASE;
}
return StatusFlags::OK;
}
#ifndef USE_BOOTLOADER_MCUBOOT
// https://github.com/adafruit/Adafruit_nRF52_Bootloader/blob/6a9a6a3e6d0f86918e9286188426a279976645bd/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h#L61
constexpr uint32_t BOOTLOADER_REGION_START = 0x000F4000;
constexpr uint32_t BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS = 0x000FE000;
static bool bootloader_ok() {
return NRF_UICR->NRFFW[0] == BOOTLOADER_REGION_START && NRF_UICR->NRFFW[1] == BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS;
}
static StatusFlags fix_bootloader() {
if (!bootloader_ok()) {
nvmc_config(NVMC_CONFIG_WEN_Wen);
NRF_UICR->NRFFW[0] = BOOTLOADER_REGION_START;
NRF_UICR->NRFFW[1] = BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS;
nvmc_wait();
nvmc_config(NVMC_CONFIG_WEN_Ren);
return bootloader_ok() ? StatusFlags::NEED_RESET : StatusFlags::NEED_ERASE;
}
return StatusFlags::OK;
}
#endif
static StatusFlags set_uicr() {
StatusFlags status = StatusFlags::OK;
status |= set_regout0();
#ifndef USE_BOOTLOADER_MCUBOOT
status |= fix_bootloader();
#endif
return status;
}
static int board_esphome_init() {
StatusFlags status = set_uicr();
#ifdef USE_NRF52_UICR_ERASE
if (status & StatusFlags::NEED_ERASE) {
nrfx_err_t ret = nrfx_nvmc_uicr_erase();
if (ret != NRFX_SUCCESS) {
#ifdef CONFIG_PRINTK
printk("nrfx_nvmc_uicr_erase failed %d\n", ret);
#endif
} else {
status |= set_uicr();
}
}
#endif
if (status & StatusFlags::NEED_RESET) {
/* a reset is required for changes to take effect */
NVIC_SystemReset();
}
return 0;
}
} // namespace esphome::nrf52
static int board_esphome_init() { return esphome::nrf52::board_esphome_init(); }
SYS_INIT(board_esphome_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
#endif

View File

@@ -23,7 +23,7 @@ void PCF85063Component::dump_config() {
if (this->is_failed()) {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str());
RealTimeClock::dump_config();
}
float PCF85063Component::get_setup_priority() const { return setup_priority::DATA; }

View File

@@ -23,7 +23,7 @@ void PCF8563Component::dump_config() {
if (this->is_failed()) {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str());
RealTimeClock::dump_config();
}
float PCF8563Component::get_setup_priority() const { return setup_priority::DATA; }

View File

@@ -62,6 +62,7 @@ void RX8130Component::update() { this->read_time(); }
void RX8130Component::dump_config() {
ESP_LOGCONFIG(TAG, "RX8130:");
LOG_I2C_DEVICE(this);
RealTimeClock::dump_config();
}
void RX8130Component::read_time() {

View File

@@ -61,6 +61,7 @@ void SNTPComponent::dump_config() {
for (auto &server : this->servers_) {
ESP_LOGCONFIG(TAG, " Server %zu: '%s'", i++, server);
}
RealTimeClock::dump_config();
}
void SNTPComponent::update() {
#if !defined(USE_ESP32)

View File

@@ -23,6 +23,13 @@ namespace time {
static const char *const TAG = "time";
RealTimeClock::RealTimeClock() = default;
void RealTimeClock::dump_config() {
#ifdef USE_TIME_TIMEZONE
ESP_LOGCONFIG(TAG, "Timezone: '%s'", this->timezone_.c_str());
#endif
}
void RealTimeClock::synchronize_epoch_(uint32_t epoch) {
ESP_LOGVV(TAG, "Got epoch %" PRIu32, epoch);
// Update UTC epoch time.

View File

@@ -52,6 +52,8 @@ class RealTimeClock : public PollingComponent {
this->time_sync_callback_.add(std::move(callback));
};
void dump_config() override;
protected:
/// Report a unix epoch as current time.
void synchronize_epoch_(uint32_t epoch);

View File

@@ -425,6 +425,8 @@ async def to_code(config):
# Track if any network uses Enterprise authentication
has_eap = False
# Track if any network uses manual IP
has_manual_ip = False
# Initialize FixedVector with the count of networks
networks = config.get(CONF_NETWORKS, [])
@@ -438,11 +440,15 @@ async def to_code(config):
for network in networks:
if CONF_EAP in network:
has_eap = True
if network.get(CONF_MANUAL_IP) or config.get(CONF_MANUAL_IP):
has_manual_ip = True
cg.with_local_variable(network[CONF_ID], WiFiAP(), add_sta, network)
if CONF_AP in config:
conf = config[CONF_AP]
ip_config = conf.get(CONF_MANUAL_IP)
if ip_config:
has_manual_ip = True
cg.with_local_variable(
conf[CONF_ID],
WiFiAP(),
@@ -458,6 +464,10 @@ async def to_code(config):
if CORE.is_esp32:
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT", has_eap)
# Only define USE_WIFI_MANUAL_IP if any AP uses manual IP
if has_manual_ip:
cg.add_define("USE_WIFI_MANUAL_IP")
cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
cg.add(var.set_power_save_mode(config[CONF_POWER_SAVE_MODE]))
if CONF_MIN_AUTH_MODE in config:

View File

@@ -253,17 +253,19 @@ bool WiFiComponent::ssid_was_seen_in_scan_(const std::string &ssid) const {
return false;
}
int8_t WiFiComponent::find_next_hidden_sta_(int8_t start_index, bool include_explicit_hidden) {
int8_t WiFiComponent::find_next_hidden_sta_(int8_t start_index) {
// Find next SSID that wasn't in scan results (might be hidden)
bool include_explicit_hidden = !this->went_through_explicit_hidden_phase_();
// Start searching from start_index + 1
for (size_t i = start_index + 1; i < this->sta_.size(); i++) {
const auto &sta = this->sta_[i];
// Skip networks that were already tried in EXPLICIT_HIDDEN phase
// Those are: networks marked hidden:true that appear before the first non-hidden network
// If all networks are hidden (first_non_hidden_idx == -1), skip all of them
if (!include_explicit_hidden && sta.get_hidden()) {
int8_t first_non_hidden_idx = this->find_first_non_hidden_index_();
if (first_non_hidden_idx >= 0 && static_cast<int8_t>(i) < first_non_hidden_idx) {
if (first_non_hidden_idx < 0 || static_cast<int8_t>(i) < first_non_hidden_idx) {
ESP_LOGD(TAG, "Skipping " LOG_SECRET("'%s'") " (explicit hidden, already tried)", sta.get_ssid().c_str());
continue;
}
@@ -411,8 +413,10 @@ void WiFiComponent::start() {
void WiFiComponent::restart_adapter() {
ESP_LOGW(TAG, "Restarting adapter");
this->wifi_mode_(false, {});
delay(100); // NOLINT
// Enter cooldown state to allow WiFi hardware to stabilize after restart
// Don't set retry_phase_ or num_retried_ here - state machine handles transitions
this->state_ = WIFI_COMPONENT_STATE_COOLDOWN;
this->action_started_ = millis();
}
void WiFiComponent::loop() {
@@ -433,19 +437,11 @@ void WiFiComponent::loop() {
case WIFI_COMPONENT_STATE_COOLDOWN: {
this->status_set_warning(LOG_STR("waiting to reconnect"));
if (millis() - this->action_started_ > 5000) {
// After cooldown, connect based on current retry phase
this->reset_selected_ap_to_first_if_invalid_();
// Check if we need to trigger a scan first
if (this->needs_scan_results_() && !this->all_networks_hidden_()) {
// Need scan results or no matching networks found - scan/rescan
ESP_LOGD(TAG, "Scanning required for phase %s", LOG_STR_ARG(retry_phase_to_log_string(this->retry_phase_)));
this->start_scanning();
} else {
// Have everything we need to connect (or all networks are hidden, skip scanning)
WiFiAP params = this->build_params_for_current_phase_();
this->start_connecting(params, false);
}
// After cooldown we either restarted the adapter because of
// a failure, or something tried to connect over and over
// so we entered cooldown. In both cases we call
// check_connecting_finished to continue the state machine.
this->check_connecting_finished();
}
break;
}
@@ -569,6 +565,7 @@ void WiFiComponent::setup_ap_config_() {
" IP Address: %s",
this->ap_.get_ssid().c_str(), this->ap_.get_password().c_str(), ip_address.c_str());
#ifdef USE_WIFI_MANUAL_IP
auto manual_ip = this->ap_.get_manual_ip();
if (manual_ip.has_value()) {
ESP_LOGCONFIG(TAG,
@@ -578,6 +575,7 @@ void WiFiComponent::setup_ap_config_() {
manual_ip->static_ip.str().c_str(), manual_ip->gateway.str().c_str(),
manual_ip->subnet.str().c_str());
}
#endif
if (!this->has_sta()) {
this->state_ = WIFI_COMPONENT_STATE_AP;
@@ -667,7 +665,7 @@ void WiFiComponent::save_wifi_sta(const std::string &ssid, const std::string &pa
void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) {
// Log connection attempt at INFO level with priority
std::string bssid_formatted;
float priority = 0.0f;
int8_t priority = 0;
if (ap.get_bssid().has_value()) {
bssid_formatted = format_mac_address_pretty(ap.get_bssid().value().data());
@@ -716,11 +714,14 @@ void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) {
} else {
ESP_LOGV(TAG, " Channel not set");
}
#ifdef USE_WIFI_MANUAL_IP
if (ap.get_manual_ip().has_value()) {
ManualIP m = *ap.get_manual_ip();
ESP_LOGV(TAG, " Manual IP: Static IP=%s Gateway=%s Subnet=%s DNS1=%s DNS2=%s", m.static_ip.str().c_str(),
m.gateway.str().c_str(), m.subnet.str().c_str(), m.dns1.str().c_str(), m.dns2.str().c_str());
} else {
} else
#endif
{
ESP_LOGV(TAG, " Using DHCP IP");
}
ESP_LOGV(TAG, " Hidden: %s", YESNO(ap.get_hidden()));
@@ -1002,6 +1003,10 @@ void WiFiComponent::check_scanning_finished() {
// No scan results matched our configured networks - transition directly to hidden mode
// Don't call retry_connect() since we never attempted a connection (no BSSID to penalize)
this->transition_to_phase_(WiFiRetryPhase::RETRY_HIDDEN);
// If no hidden networks to try, skip connection attempt (will be handled on next loop)
if (this->selected_sta_index_ == -1) {
return;
}
// Now start connection attempt in hidden mode
} else if (this->transition_to_phase_(WiFiRetryPhase::SCAN_CONNECTING)) {
return; // scan started, wait for next loop iteration
@@ -1062,8 +1067,8 @@ void WiFiComponent::check_connecting_finished() {
this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTED;
this->num_retried_ = 0;
// Reset all priorities if they're all the same (can't differentiate)
this->reset_priorities_if_all_same_();
// Clear priority tracking if all priorities are at minimum
this->clear_priorities_if_all_min_();
#ifdef USE_WIFI_FAST_CONNECT
this->save_fast_connect_settings_();
@@ -1081,12 +1086,18 @@ void WiFiComponent::check_connecting_finished() {
uint32_t now = millis();
if (now - this->action_started_ > 30000) {
ESP_LOGW(TAG, "Connection timeout");
// Move from STA_CONNECTING_2 back to STA_CONNECTING state
// since we know the connection attempt has failed
this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTING;
this->retry_connect();
return;
}
if (this->error_from_callback_) {
ESP_LOGW(TAG, "Connecting to network failed");
ESP_LOGW(TAG, "Connecting to network failed (callback)");
// Move from STA_CONNECTING_2 back to STA_CONNECTING state
// since we know the connection attempt is finished
this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTING;
this->retry_connect();
return;
}
@@ -1095,6 +1106,9 @@ void WiFiComponent::check_connecting_finished() {
return;
}
// Move from STA_CONNECTING_2 back to STA_CONNECTING state
// since we know the connection attempt is finished
this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTING;
if (status == WiFiSTAConnectStatus::ERROR_NETWORK_NOT_FOUND) {
ESP_LOGW(TAG, "Network no longer found");
this->retry_connect();
@@ -1144,7 +1158,12 @@ WiFiRetryPhase WiFiComponent::determine_next_phase_() {
return WiFiRetryPhase::EXPLICIT_HIDDEN;
}
// No more consecutive explicitly hidden networks - proceed to scanning
// No more consecutive explicitly hidden networks
// If ALL networks are hidden, skip scanning and go directly to restart
if (this->find_first_non_hidden_index_() < 0) {
return WiFiRetryPhase::RESTARTING_ADAPTER;
}
// Otherwise proceed to scanning for non-hidden networks
return WiFiRetryPhase::SCAN_CONNECTING;
}
@@ -1162,14 +1181,12 @@ WiFiRetryPhase WiFiComponent::determine_next_phase_() {
// Its priority has been decreased, so on next scan it will be sorted lower
// and we'll try the next best BSSID.
// Check if there are any potentially hidden networks to try
if (this->find_next_hidden_sta_(-1, !this->went_through_explicit_hidden_phase_()) >= 0) {
if (this->find_next_hidden_sta_(-1) >= 0) {
return WiFiRetryPhase::RETRY_HIDDEN; // Found hidden networks to try
}
// No hidden networks - skip directly to restart/rescan
if (this->is_captive_portal_active_() || this->is_esp32_improv_active_()) {
return this->went_through_explicit_hidden_phase_() ? WiFiRetryPhase::EXPLICIT_HIDDEN
: WiFiRetryPhase::SCAN_CONNECTING;
}
// No hidden networks - always go through RESTARTING_ADAPTER phase
// This ensures num_retried_ gets reset and a fresh scan is triggered
// The actual adapter restart will be skipped if captive portal/improv is active
return WiFiRetryPhase::RESTARTING_ADAPTER;
case WiFiRetryPhase::RETRY_HIDDEN:
@@ -1181,20 +1198,18 @@ WiFiRetryPhase WiFiComponent::determine_next_phase_() {
// Exhausted retries on current SSID - check if there are more potentially hidden SSIDs to try
if (this->selected_sta_index_ < static_cast<int8_t>(this->sta_.size()) - 1) {
// More SSIDs available - stay in RETRY_HIDDEN, advance will happen in retry_connect()
return WiFiRetryPhase::RETRY_HIDDEN;
// Check if find_next_hidden_sta_() would actually find another hidden SSID
// as it might have been seen in the scan results and we want to skip those
// otherwise we will get stuck in RETRY_HIDDEN phase
if (this->find_next_hidden_sta_(this->selected_sta_index_) != -1) {
// More hidden SSIDs available - stay in RETRY_HIDDEN, advance will happen in retry_connect()
return WiFiRetryPhase::RETRY_HIDDEN;
}
}
}
// Exhausted all potentially hidden SSIDs - rescan to try next BSSID
// If captive portal/improv is active, skip adapter restart and go back to start
// Otherwise restart adapter to clear any stuck state
if (this->is_captive_portal_active_() || this->is_esp32_improv_active_()) {
// Go back to explicit hidden if we went through it initially, otherwise scan
return this->went_through_explicit_hidden_phase_() ? WiFiRetryPhase::EXPLICIT_HIDDEN
: WiFiRetryPhase::SCAN_CONNECTING;
}
// Restart adapter
// Exhausted all potentially hidden SSIDs - always go through RESTARTING_ADAPTER
// This ensures num_retried_ gets reset and a fresh scan is triggered
// The actual adapter restart will be skipped if captive portal/improv is active
return WiFiRetryPhase::RESTARTING_ADAPTER;
case WiFiRetryPhase::RESTARTING_ADAPTER:
@@ -1214,8 +1229,8 @@ WiFiRetryPhase WiFiComponent::determine_next_phase_() {
/// - Performing phase-specific initialization (e.g., advancing AP index, starting scans)
///
/// @param new_phase The phase we're transitioning TO
/// @return true if an async scan was started (caller should wait for completion)
/// false if no scan started (caller can proceed with connection attempt)
/// @return true if connection attempt should be skipped (scan started or no networks to try)
/// false if caller can proceed with connection attempt
bool WiFiComponent::transition_to_phase_(WiFiRetryPhase new_phase) {
WiFiRetryPhase old_phase = this->retry_phase_;
@@ -1273,7 +1288,7 @@ bool WiFiComponent::transition_to_phase_(WiFiRetryPhase new_phase) {
// If first network is marked hidden, we went through EXPLICIT_HIDDEN phase
// In that case, skip networks marked hidden:true (already tried)
// Otherwise, include them (they haven't been tried yet)
this->selected_sta_index_ = this->find_next_hidden_sta_(-1, !this->went_through_explicit_hidden_phase_());
this->selected_sta_index_ = this->find_next_hidden_sta_(-1);
if (this->selected_sta_index_ == -1) {
ESP_LOGD(TAG, "All SSIDs visible or already tried, skipping hidden mode");
@@ -1282,7 +1297,12 @@ bool WiFiComponent::transition_to_phase_(WiFiRetryPhase new_phase) {
break;
case WiFiRetryPhase::RESTARTING_ADAPTER:
this->restart_adapter();
// Skip actual adapter restart if captive portal/improv is active
// This allows state machine to reset num_retried_ and trigger fresh scan
// without disrupting the captive portal/improv connection
if (!this->is_captive_portal_active_() && !this->is_esp32_improv_active_()) {
this->restart_adapter();
}
// Return true to indicate we should wait (go to COOLDOWN) instead of immediately connecting
return true;
@@ -1293,25 +1313,32 @@ bool WiFiComponent::transition_to_phase_(WiFiRetryPhase new_phase) {
return false; // Did not start scan, can proceed with connection
}
/// Reset all BSSID priorities to 0 if they're all identical (can't differentiate)
/// Called when starting a fresh connection attempt or after successful connection
void WiFiComponent::reset_priorities_if_all_same_() {
/// Clear BSSID priority tracking if all priorities are at minimum (saves memory)
/// At minimum priority, all BSSIDs are equally bad, so priority tracking is useless
/// Called after successful connection or after failed connection attempts
void WiFiComponent::clear_priorities_if_all_min_() {
if (this->sta_priorities_.empty()) {
return;
}
int8_t first_priority = this->sta_priorities_[0].priority;
// Only clear if all priorities have been decremented to the minimum value
// At this point, all BSSIDs have been equally penalized and priority info is useless
if (first_priority != std::numeric_limits<int8_t>::min()) {
return;
}
for (const auto &pri : this->sta_priorities_) {
if (pri.priority != first_priority) {
return; // Not all same, nothing to do
}
}
// All priorities are identical, reset to 0
ESP_LOGD(TAG, "Resetting all BSSID priorities (all identical)");
for (auto &pri : this->sta_priorities_) {
pri.priority = 0;
}
// All priorities are at minimum - clear the vector to save memory and reset
ESP_LOGD(TAG, "Clearing BSSID priorities (all at minimum)");
this->sta_priorities_.clear();
this->sta_priorities_.shrink_to_fit();
}
/// Log failed connection attempt and decrease BSSID priority to avoid repeated failures
@@ -1327,6 +1354,11 @@ void WiFiComponent::reset_priorities_if_all_same_() {
/// - Other phases: Uses BSSID from config if explicitly specified by user or fast_connect
///
/// If no BSSID is available (SSID-only connection), priority adjustment is skipped.
///
/// IMPORTANT: Priority is only decreased on the LAST attempt for a BSSID in SCAN_CONNECTING phase.
/// This prevents false positives from transient WiFi stack state issues after scanning.
/// Single failures don't necessarily mean the AP is bad - two genuine failures provide
/// higher confidence before degrading priority and skipping the BSSID in future scans.
void WiFiComponent::log_and_adjust_priority_for_failed_connect_() {
// Determine which BSSID we tried to connect to
optional<bssid_t> failed_bssid;
@@ -1343,12 +1375,6 @@ void WiFiComponent::log_and_adjust_priority_for_failed_connect_() {
return; // No BSSID to penalize
}
// Decrease priority to avoid repeatedly trying the same failed BSSID
int8_t old_priority = this->get_sta_priority(failed_bssid.value());
int8_t new_priority =
(old_priority > std::numeric_limits<int8_t>::min()) ? (old_priority - 1) : std::numeric_limits<int8_t>::min();
this->set_sta_priority(failed_bssid.value(), new_priority);
// Get SSID for logging
std::string ssid;
if (this->retry_phase_ == WiFiRetryPhase::SCAN_CONNECTING && !this->scan_result_.empty()) {
@@ -1357,12 +1383,28 @@ void WiFiComponent::log_and_adjust_priority_for_failed_connect_() {
ssid = config->get_ssid();
}
// Only decrease priority on the last attempt for this phase
// This prevents false positives from transient WiFi stack issues
uint8_t max_retries = get_max_retries_for_phase(this->retry_phase_);
bool is_last_attempt = (this->num_retried_ + 1 >= max_retries);
// Decrease priority only on last attempt to avoid false positives from transient failures
int8_t old_priority = this->get_sta_priority(failed_bssid.value());
int8_t new_priority = old_priority;
if (is_last_attempt) {
// Decrease priority, but clamp to int8_t::min to prevent overflow
new_priority =
(old_priority > std::numeric_limits<int8_t>::min()) ? (old_priority - 1) : std::numeric_limits<int8_t>::min();
this->set_sta_priority(failed_bssid.value(), new_priority);
}
ESP_LOGD(TAG, "Failed " LOG_SECRET("'%s'") " " LOG_SECRET("(%s)") ", priority %d → %d", ssid.c_str(),
format_mac_address_pretty(failed_bssid.value().data()).c_str(), old_priority, new_priority);
// After adjusting priority, check if all priorities are now identical
// If so, reset them all to 0 to start fresh
this->reset_priorities_if_all_same_();
// After adjusting priority, check if all priorities are now at minimum
// If so, clear the vector to save memory and reset for fresh start
this->clear_priorities_if_all_min_();
}
/// Handle target advancement or retry counter increment when staying in the same phase
@@ -1407,8 +1449,7 @@ void WiFiComponent::advance_to_next_target_or_increment_retry_() {
// If first network is marked hidden, we went through EXPLICIT_HIDDEN phase
// In that case, skip networks marked hidden:true (already tried)
// Otherwise, include them (they haven't been tried yet)
int8_t next_index =
this->find_next_hidden_sta_(this->selected_sta_index_, !this->went_through_explicit_hidden_phase_());
int8_t next_index = this->find_next_hidden_sta_(this->selected_sta_index_);
if (next_index != -1) {
// Found another potentially hidden SSID
this->selected_sta_index_ = next_index;
@@ -1436,15 +1477,13 @@ void WiFiComponent::advance_to_next_target_or_increment_retry_() {
void WiFiComponent::retry_connect() {
this->log_and_adjust_priority_for_failed_connect_();
delay(10);
// Determine next retry phase based on current state
WiFiRetryPhase current_phase = this->retry_phase_;
WiFiRetryPhase next_phase = this->determine_next_phase_();
// Handle phase transitions (transition_to_phase_ handles same-phase no-op internally)
if (this->transition_to_phase_(next_phase)) {
return; // Wait for scan to complete
return; // Scan started or adapter restarted (which sets its own state)
}
if (next_phase == current_phase) {
@@ -1462,11 +1501,16 @@ void WiFiComponent::retry_connect() {
this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTING_2;
WiFiAP params = this->build_params_for_current_phase_();
this->start_connecting(params, true);
return;
}
// No valid target - fall through to set state to allow phase transition
return;
}
// If we can't progress forward its likely because scanning failed
// or the stack is in a bad state after restart so we cooldown first
// and once it finishes, cooldown will call check_connecting_finished()
// which will progress the state machine
ESP_LOGD(TAG, "Entering cooldown from state %d and phase %s", this->state_,
LOG_STR_ARG(retry_phase_to_log_string(this->retry_phase_)));
this->state_ = WIFI_COMPONENT_STATE_COOLDOWN;
this->action_started_ = millis();
}
@@ -1560,7 +1604,9 @@ void WiFiAP::set_password(const std::string &password) { this->password_ = passw
void WiFiAP::set_eap(optional<EAPAuth> eap_auth) { this->eap_ = std::move(eap_auth); }
#endif
void WiFiAP::set_channel(optional<uint8_t> channel) { this->channel_ = channel; }
#ifdef USE_WIFI_MANUAL_IP
void WiFiAP::set_manual_ip(optional<ManualIP> manual_ip) { this->manual_ip_ = manual_ip; }
#endif
void WiFiAP::set_hidden(bool hidden) { this->hidden_ = hidden; }
const std::string &WiFiAP::get_ssid() const { return this->ssid_; }
const optional<bssid_t> &WiFiAP::get_bssid() const { return this->bssid_; }
@@ -1569,15 +1615,17 @@ const std::string &WiFiAP::get_password() const { return this->password_; }
const optional<EAPAuth> &WiFiAP::get_eap() const { return this->eap_; }
#endif
const optional<uint8_t> &WiFiAP::get_channel() const { return this->channel_; }
#ifdef USE_WIFI_MANUAL_IP
const optional<ManualIP> &WiFiAP::get_manual_ip() const { return this->manual_ip_; }
#endif
bool WiFiAP::get_hidden() const { return this->hidden_; }
WiFiScanResult::WiFiScanResult(const bssid_t &bssid, std::string ssid, uint8_t channel, int8_t rssi, bool with_auth,
bool is_hidden)
: bssid_(bssid),
ssid_(std::move(ssid)),
channel_(channel),
rssi_(rssi),
ssid_(std::move(ssid)),
with_auth_(with_auth),
is_hidden_(is_hidden) {}
bool WiFiScanResult::matches(const WiFiAP &config) const {

View File

@@ -158,7 +158,9 @@ class WiFiAP {
#endif // USE_WIFI_WPA2_EAP
void set_channel(optional<uint8_t> channel);
void set_priority(int8_t priority) { priority_ = priority; }
#ifdef USE_WIFI_MANUAL_IP
void set_manual_ip(optional<ManualIP> manual_ip);
#endif
void set_hidden(bool hidden);
const std::string &get_ssid() const;
const optional<bssid_t> &get_bssid() const;
@@ -168,7 +170,9 @@ class WiFiAP {
#endif // USE_WIFI_WPA2_EAP
const optional<uint8_t> &get_channel() const;
int8_t get_priority() const { return priority_; }
#ifdef USE_WIFI_MANUAL_IP
const optional<ManualIP> &get_manual_ip() const;
#endif
bool get_hidden() const;
protected:
@@ -178,7 +182,9 @@ class WiFiAP {
#ifdef USE_WIFI_WPA2_EAP
optional<EAPAuth> eap_;
#endif // USE_WIFI_WPA2_EAP
#ifdef USE_WIFI_MANUAL_IP
optional<ManualIP> manual_ip_;
#endif
optional<uint8_t> channel_;
int8_t priority_{0};
bool hidden_{false};
@@ -386,12 +392,11 @@ class WiFiComponent : public Component {
/// Find next SSID that wasn't in scan results (might be hidden)
/// Returns index of next potentially hidden SSID, or -1 if none found
/// @param start_index Start searching from index after this (-1 to start from beginning)
/// @param include_explicit_hidden If true, include SSIDs marked hidden:true. If false, only find truly hidden SSIDs.
int8_t find_next_hidden_sta_(int8_t start_index, bool include_explicit_hidden = true);
int8_t find_next_hidden_sta_(int8_t start_index);
/// Log failed connection and decrease BSSID priority to avoid repeated attempts
void log_and_adjust_priority_for_failed_connect_();
/// Reset all BSSID priorities to 0 if they're all identical (can't differentiate)
void reset_priorities_if_all_same_();
/// Clear BSSID priority tracking if all priorities are at minimum (saves memory)
void clear_priorities_if_all_min_();
/// Advance to next target (AP/SSID) within current phase, or increment retry counter
/// Called when staying in the same phase after a failed connection attempt
void advance_to_next_target_or_increment_retry_();

View File

@@ -282,9 +282,15 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) {
return false;
}
#ifdef USE_WIFI_MANUAL_IP
if (!this->wifi_sta_ip_config_(ap.get_manual_ip())) {
return false;
}
#else
if (!this->wifi_sta_ip_config_({})) {
return false;
}
#endif
// setup enterprise authentication if required
#ifdef USE_WIFI_WPA2_EAP
@@ -832,10 +838,17 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) {
return false;
}
#ifdef USE_WIFI_MANUAL_IP
if (!this->wifi_ap_ip_config_(ap.get_manual_ip())) {
ESP_LOGV(TAG, "wifi_ap_ip_config_ failed");
return false;
}
#else
if (!this->wifi_ap_ip_config_({})) {
ESP_LOGV(TAG, "wifi_ap_ip_config_ failed");
return false;
}
#endif
return true;
}

View File

@@ -380,9 +380,15 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) {
return false;
}
#ifdef USE_WIFI_MANUAL_IP
if (!this->wifi_sta_ip_config_(ap.get_manual_ip())) {
return false;
}
#else
if (!this->wifi_sta_ip_config_({})) {
return false;
}
#endif
// setup enterprise authentication if required
#ifdef USE_WIFI_WPA2_EAP
@@ -994,10 +1000,17 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) {
return false;
}
#ifdef USE_WIFI_MANUAL_IP
if (!this->wifi_ap_ip_config_(ap.get_manual_ip())) {
ESP_LOGE(TAG, "wifi_ap_ip_config_ failed:");
return false;
}
#else
if (!this->wifi_ap_ip_config_({})) {
ESP_LOGE(TAG, "wifi_ap_ip_config_ failed:");
return false;
}
#endif
return true;
}

View File

@@ -112,9 +112,15 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) {
WiFi.disconnect();
}
#ifdef USE_WIFI_MANUAL_IP
if (!this->wifi_sta_ip_config_(ap.get_manual_ip())) {
return false;
}
#else
if (!this->wifi_sta_ip_config_({})) {
return false;
}
#endif
this->wifi_apply_hostname_();
@@ -445,10 +451,17 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) {
if (!this->wifi_mode_({}, true))
return false;
#ifdef USE_WIFI_MANUAL_IP
if (!this->wifi_ap_ip_config_(ap.get_manual_ip())) {
ESP_LOGV(TAG, "wifi_ap_ip_config_ failed");
return false;
}
#else
if (!this->wifi_ap_ip_config_({})) {
ESP_LOGV(TAG, "wifi_ap_ip_config_ failed");
return false;
}
#endif
yield();

View File

@@ -55,8 +55,13 @@ bool WiFiComponent::wifi_apply_power_save_() {
bool WiFiComponent::wifi_apply_output_power_(float output_power) { return true; }
bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) {
#ifdef USE_WIFI_MANUAL_IP
if (!this->wifi_sta_ip_config_(ap.get_manual_ip()))
return false;
#else
if (!this->wifi_sta_ip_config_({}))
return false;
#endif
auto ret = WiFi.begin(ap.get_ssid().c_str(), ap.get_password().c_str());
if (ret != WL_CONNECTED)
@@ -161,10 +166,17 @@ bool WiFiComponent::wifi_ap_ip_config_(optional<ManualIP> manual_ip) {
bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) {
if (!this->wifi_mode_({}, true))
return false;
#ifdef USE_WIFI_MANUAL_IP
if (!this->wifi_ap_ip_config_(ap.get_manual_ip())) {
ESP_LOGV(TAG, "wifi_ap_ip_config_ failed");
return false;
}
#else
if (!this->wifi_ap_ip_config_({})) {
ESP_LOGV(TAG, "wifi_ap_ip_config_ failed");
return false;
}
#endif
WiFi.beginAP(ap.get_ssid().c_str(), ap.get_password().c_str(), ap.get_channel().value_or(1));

View File

@@ -144,6 +144,7 @@
#define USE_TIME_TIMEZONE
#define USE_WIFI
#define USE_WIFI_AP
#define USE_WIFI_MANUAL_IP
#define USE_WIREGUARD
#endif
@@ -215,6 +216,7 @@
#define USE_ARDUINO_VERSION_CODE VERSION_CODE(3, 3, 2)
#define USE_ETHERNET
#define USE_ETHERNET_KSZ8081
#define USE_ETHERNET_MANUAL_IP
#endif
#ifdef USE_ESP_IDF
@@ -287,6 +289,8 @@
#ifdef USE_NRF52
#define USE_NRF52_DFU
#define USE_NRF52_REG0_VOUT 5
#define USE_NRF52_UICR_ERASE
#define USE_SOFTDEVICE_ID 7
#define USE_SOFTDEVICE_VERSION 1
#endif

View File

@@ -1244,12 +1244,18 @@ template<class T> using ExternalRAMAllocator = RAMAllocator<T>;
* Functions to constrain the range of arithmetic values.
*/
template<std::totally_ordered T> T clamp_at_least(T value, T min) {
template<typename T, typename U>
concept comparable_with = requires(T a, U b) {
{ a > b } -> std::convertible_to<bool>;
{ a < b } -> std::convertible_to<bool>;
};
template<std::totally_ordered T, comparable_with<T> U> T clamp_at_least(T value, U min) {
if (value < min)
return min;
return value;
}
template<std::totally_ordered T> T clamp_at_most(T value, T max) {
template<std::totally_ordered T, comparable_with<T> U> T clamp_at_most(T value, U max) {
if (value > max)
return max;
return value;

View File

@@ -6,3 +6,7 @@
#ifdef USE_ARDUINO
#include <Arduino.h>
#endif
#ifdef USE_ZEPHYR
#define M_PI 3.14159265358979323846
#endif

View File

@@ -0,0 +1,4 @@
packages:
i2c: !include ../../test_build_components/common/i2c/nrf52.yaml
<<: !include common.yaml

View File

@@ -15,3 +15,6 @@ nrf52:
inverted: true
mode:
output: true
reg0:
voltage: 2.1V
uicr_erase: true

View File

@@ -0,0 +1,4 @@
nrf52:
reg0:
voltage: 3.3V
uicr_erase: true

View File

@@ -5,3 +5,5 @@ nrf52:
inverted: true
mode:
output: true
reg0:
voltage: default

View File

@@ -0,0 +1,4 @@
packages:
i2c: !include ../../test_build_components/common/i2c/nrf52.yaml
<<: !include common.yaml

View File

@@ -0,0 +1,4 @@
packages:
i2c: !include ../../test_build_components/common/i2c/nrf52.yaml
<<: !include common.yaml

View File

@@ -0,0 +1,4 @@
packages:
i2c: !include ../../test_build_components/common/i2c/nrf52.yaml
<<: !include common.yaml

View File

@@ -0,0 +1,7 @@
substitutions:
reset_pin: P0.10
packages:
i2c: !include ../../test_build_components/common/i2c/nrf52.yaml
<<: !include common.yaml

View File

@@ -15,5 +15,10 @@ wifi:
networks:
- ssid: MySSID
password: password1
priority: 10
- ssid: MySSID2
password: password2
priority: 5
- ssid: MySSID3
password: password3
priority: 0

View File

@@ -3,6 +3,21 @@ psram:
wifi:
use_psram: true
min_auth_mode: WPA
manual_ip:
static_ip: 192.168.1.100
gateway: 192.168.1.1
subnet: 255.255.255.0
dns1: 1.1.1.1
dns2: 8.8.8.8
ap:
ssid: Fallback AP
password: fallback_password
manual_ip:
static_ip: 192.168.4.1
gateway: 192.168.4.1
subnet: 255.255.255.0
captive_portal:
packages:
- !include common.yaml