Compare commits

...

7 Commits

Author SHA1 Message Date
J. Nick Koston
db5a58d71e fix dhcp 2026-01-24 22:51:38 -10:00
J. Nick Koston
7a2d4cd801 fix log spam with scan 2026-01-24 22:46:52 -10:00
J. Nick Koston
7c26047e04 fix address sta, make empty password work 2026-01-24 22:39:53 -10:00
J. Nick Koston
539e2a3c90 fix address sta, make empty password work 2026-01-24 22:38:28 -10:00
J. Nick Koston
83ae089bad Add captive_portal RP2040 test 2026-01-24 22:19:54 -10:00
J. Nick Koston
39cdedfd89 test 2026-01-24 22:13:07 -10:00
J. Nick Koston
4a453d90ad [web_server][captive_portal] Add RP2040 platform support 2026-01-24 22:09:48 -10:00
11 changed files with 53 additions and 23 deletions

View File

@@ -1 +1 @@
a5eed002f632b349071098533ebf1ff16b68a6d99eac5ba5850e80dbfa95f86e
c0db7505713f2ebf5d18f274d35469cfbdaabe1e1def9fe2195594dc345f1a49

View File

@@ -292,7 +292,7 @@ CONFIG_SCHEMA = cv.All(
CONF_MAX_CONNECTIONS,
esp8266=4, # ~40KB free RAM, each connection uses ~500-1000 bytes
esp32=8, # 520KB RAM available
rp2040=4, # 264KB RAM but LWIP constraints
rp2040=8, # 264KB RAM, plenty of heap available
bk72xx=8, # Moderate RAM
rtl87xx=8, # Moderate RAM
host=8, # Abundant resources

View File

@@ -38,8 +38,10 @@ async def to_code(config):
# https://github.com/ESP32Async/ESPAsyncTCP
cg.add_library("ESP32Async/ESPAsyncTCP", "2.0.0")
elif CORE.is_rp2040:
# https://github.com/khoih-prog/AsyncTCP_RP2040W
cg.add_library("khoih-prog/AsyncTCP_RP2040W", "1.2.0")
# https://github.com/ayushsharma82/RPAsyncTCP
# RPAsyncTCP is a drop-in replacement for AsyncTCP_RP2040W with better
# ESPAsyncWebServer compatibility
cg.add_library("ayushsharma82/RPAsyncTCP", "1.3.2")
# Other platforms (host, etc) use socket-based implementation

View File

@@ -8,8 +8,8 @@
// Use ESPAsyncTCP library for ESP8266 (always Arduino)
#include <ESPAsyncTCP.h>
#elif defined(USE_RP2040)
// Use AsyncTCP_RP2040W library for RP2040
#include <AsyncTCP_RP2040W.h>
// Use RPAsyncTCP library for RP2040
#include <RPAsyncTCP.h>
#else
// Use socket-based implementation for other platforms
#include "async_tcp_socket.h"

View File

@@ -13,6 +13,7 @@ from esphome.const import (
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_LN882X,
PLATFORM_RP2040,
PLATFORM_RTL87XX,
PlatformFramework,
)
@@ -53,6 +54,7 @@ CONFIG_SCHEMA = cv.All(
PLATFORM_ESP8266,
PLATFORM_BK72XX,
PLATFORM_LN882X,
PLATFORM_RP2040,
PLATFORM_RTL87XX,
]
),
@@ -106,6 +108,8 @@ async def to_code(config):
cg.add_library("DNSServer", None)
if CORE.is_libretiny:
cg.add_library("DNSServer", None)
if CORE.is_rp2040:
cg.add_library("DNSServer", None)
# Only compile the ESP-IDF DNS server when using ESP-IDF framework

View File

@@ -31,6 +31,7 @@ from esphome.const import (
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_LN882X,
PLATFORM_RP2040,
PLATFORM_RTL87XX,
)
from esphome.core import CORE, CoroPriority, coroutine_with_priority
@@ -213,6 +214,7 @@ CONFIG_SCHEMA = cv.All(
PLATFORM_ESP8266,
PLATFORM_BK72XX,
PLATFORM_LN882X,
PLATFORM_RP2040,
PLATFORM_RTL87XX,
]
),

View File

@@ -47,5 +47,10 @@ async def to_code(config):
cg.add_library("ESP8266WiFi", None)
if CORE.is_libretiny:
CORE.add_platformio_option("lib_ignore", ["ESPAsyncTCP", "RPAsyncTCP"])
if CORE.is_rp2040:
# Ignore bundled AsyncTCP libraries - we use RPAsyncTCP from async_tcp component
CORE.add_platformio_option(
"lib_ignore", ["ESPAsyncTCP", "AsyncTCP", "AsyncTCP_RP2040W"]
)
# https://github.com/ESP32Async/ESPAsyncWebServer/blob/main/library.json
cg.add_library("ESP32Async/ESPAsyncWebServer", "3.9.5")

View File

@@ -88,6 +88,8 @@ bool WiFiComponent::wifi_sta_pre_setup_() { return this->wifi_mode_(true, {}); }
bool WiFiComponent::wifi_sta_ip_config_(const optional<ManualIP> &manual_ip) {
if (!manual_ip.has_value()) {
// Explicitly enable DHCP by passing all zeros - this resets any previous static IP config
WiFi.config(IPAddress(0, 0, 0, 0), IPAddress(0, 0, 0, 0), IPAddress(0, 0, 0, 0), IPAddress(0, 0, 0, 0));
return true;
}
@@ -161,19 +163,18 @@ bool WiFiComponent::wifi_scan_start_(bool passive) {
#ifdef USE_WIFI_AP
bool WiFiComponent::wifi_ap_ip_config_(const optional<ManualIP> &manual_ip) {
esphome::network::IPAddress ip_address, gateway, subnet, dns;
IPAddress ip_address, gateway, subnet;
if (manual_ip.has_value()) {
ip_address = manual_ip->static_ip;
gateway = manual_ip->gateway;
subnet = manual_ip->subnet;
dns = manual_ip->static_ip;
ip_address = IPAddress(manual_ip->static_ip);
gateway = IPAddress(manual_ip->gateway);
subnet = IPAddress(manual_ip->subnet);
} else {
ip_address = network::IPAddress(192, 168, 4, 1);
gateway = network::IPAddress(192, 168, 4, 1);
subnet = network::IPAddress(255, 255, 255, 0);
dns = network::IPAddress(192, 168, 4, 1);
ip_address = IPAddress(192, 168, 4, 1);
gateway = IPAddress(192, 168, 4, 1);
subnet = IPAddress(255, 255, 255, 0);
}
WiFi.config(ip_address, dns, gateway, subnet);
// Use softAPConfig for AP mode - WiFi.config() would disable DHCP for STA mode
WiFi.softAPConfig(ip_address, gateway, subnet);
return true;
}
@@ -192,12 +193,16 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) {
}
#endif
WiFi.beginAP(ap.get_ssid().c_str(), ap.get_password().c_str(), ap.has_channel() ? ap.get_channel() : 1);
const char *password = ap.get_password().empty() ? nullptr : ap.get_password().c_str();
WiFi.beginAP(ap.get_ssid().c_str(), password, ap.has_channel() ? ap.get_channel() : 1);
return true;
}
network::IPAddress WiFiComponent::wifi_soft_ap_ip() { return {(const ip_addr_t *) WiFi.localIP()}; }
network::IPAddress WiFiComponent::wifi_soft_ap_ip() {
struct netif *ap_netif = &cyw43_state.netif[CYW43_ITF_AP];
return {&ap_netif->ip_addr};
}
#endif // USE_WIFI_AP
bool WiFiComponent::wifi_disconnect_() {
@@ -229,20 +234,29 @@ network::IPAddresses WiFiComponent::wifi_sta_ip_addresses() {
network::IPAddresses addresses;
uint8_t index = 0;
for (auto addr : addrList) {
addresses[index++] = addr.ipFromNetifNum();
// Only include addresses from the STA interface (CYW43_ITF_STA = 0)
if (addr.ifnumber() == CYW43_ITF_STA) {
addresses[index++] = addr.ipFromNetifNum();
}
}
return addresses;
}
network::IPAddress WiFiComponent::wifi_subnet_mask_() { return {(const ip_addr_t *) WiFi.subnetMask()}; }
network::IPAddress WiFiComponent::wifi_gateway_ip_() { return {(const ip_addr_t *) WiFi.gatewayIP()}; }
network::IPAddress WiFiComponent::wifi_subnet_mask_() {
struct netif *sta_netif = &cyw43_state.netif[CYW43_ITF_STA];
return {&sta_netif->netmask};
}
network::IPAddress WiFiComponent::wifi_gateway_ip_() {
struct netif *sta_netif = &cyw43_state.netif[CYW43_ITF_STA];
return {&sta_netif->gw};
}
network::IPAddress WiFiComponent::wifi_dns_ip_(int num) {
const ip_addr_t *dns_ip = dns_getserver(num);
return network::IPAddress(dns_ip);
}
void WiFiComponent::wifi_loop_() {
// Handle scan completion
if (this->state_ == WIFI_COMPONENT_STATE_STA_SCANNING && !cyw43_wifi_scan_active(&cyw43_state)) {
// Handle scan completion - only process once when scan finishes
if (this->state_ == WIFI_COMPONENT_STATE_STA_SCANNING && !this->scan_done_ && !cyw43_wifi_scan_active(&cyw43_state)) {
this->scan_done_ = true;
ESP_LOGV(TAG, "Scan done");
#ifdef USE_WIFI_SCAN_RESULTS_LISTENERS

View File

@@ -200,6 +200,7 @@ platform_packages =
framework = arduino
lib_deps =
${common:arduino.lib_deps}
ayushsharma82/RPAsyncTCP@1.3.2 ; async_tcp
bblanchon/ArduinoJson@7.4.2 ; json
ESP32Async/ESPAsyncWebServer@3.9.5 ; web_server_base
build_flags =

View File

@@ -0,0 +1 @@
<<: !include common.yaml

View File

@@ -0,0 +1 @@
<<: !include common_v2.yaml