1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-26 23:22:21 +01:00

Merge branch 'remove-esp32-arduino-wifi-driver' into integration

This commit is contained in:
J. Nick Koston
2025-09-23 09:17:15 -05:00
9 changed files with 31 additions and 886 deletions

View File

@@ -2,7 +2,6 @@ import esphome.codegen as cg
from esphome.components.esp32 import add_idf_component from esphome.components.esp32 import add_idf_component
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_BUFFER_SIZE, CONF_ID, CONF_TYPE from esphome.const import CONF_BUFFER_SIZE, CONF_ID, CONF_TYPE
from esphome.core import CORE
from esphome.types import ConfigType from esphome.types import ConfigType
CODEOWNERS = ["@DT-art1"] CODEOWNERS = ["@DT-art1"]
@@ -51,9 +50,8 @@ async def to_code(config: ConfigType) -> None:
buffer = cg.new_Pvariable(config[CONF_ENCODER_BUFFER_ID]) buffer = cg.new_Pvariable(config[CONF_ENCODER_BUFFER_ID])
cg.add(buffer.set_buffer_size(config[CONF_BUFFER_SIZE])) cg.add(buffer.set_buffer_size(config[CONF_BUFFER_SIZE]))
if config[CONF_TYPE] == ESP32_CAMERA_ENCODER: if config[CONF_TYPE] == ESP32_CAMERA_ENCODER:
if CORE.using_esp_idf: add_idf_component(name="espressif/esp32-camera", ref="2.1.1")
add_idf_component(name="espressif/esp32-camera", ref="2.1.0") cg.add_define("USE_ESP32_CAMERA_JPEG_ENCODER")
cg.add_build_flag("-DUSE_ESP32_CAMERA_JPEG_ENCODER")
var = cg.new_Pvariable( var = cg.new_Pvariable(
config[CONF_ID], config[CONF_ID],
config[CONF_QUALITY], config[CONF_QUALITY],

View File

@@ -1,3 +1,5 @@
#include "esphome/core/defines.h"
#ifdef USE_ESP32_CAMERA_JPEG_ENCODER #ifdef USE_ESP32_CAMERA_JPEG_ENCODER
#include "esp32_camera_jpeg_encoder.h" #include "esp32_camera_jpeg_encoder.h"
@@ -15,7 +17,7 @@ camera::EncoderError ESP32CameraJPEGEncoder::encode_pixels(camera::CameraImageSp
this->bytes_written_ = 0; this->bytes_written_ = 0;
this->out_of_output_memory_ = false; this->out_of_output_memory_ = false;
bool success = fmt2jpg_cb(pixels->get_data_buffer(), pixels->get_data_length(), spec->width, spec->height, bool success = fmt2jpg_cb(pixels->get_data_buffer(), pixels->get_data_length(), spec->width, spec->height,
to_internal_(spec->format), this->quality_, callback_, this); to_internal_(spec->format), this->quality_, callback, this);
if (!success) if (!success)
return camera::ENCODER_ERROR_CONFIGURATION; return camera::ENCODER_ERROR_CONFIGURATION;
@@ -49,7 +51,7 @@ void ESP32CameraJPEGEncoder::dump_config() {
this->output_->get_max_size(), this->quality_, this->buffer_expand_size_); this->output_->get_max_size(), this->quality_, this->buffer_expand_size_);
} }
size_t ESP32CameraJPEGEncoder::callback_(void *arg, size_t index, const void *data, size_t len) { size_t ESP32CameraJPEGEncoder::callback(void *arg, size_t index, const void *data, size_t len) {
ESP32CameraJPEGEncoder *that = reinterpret_cast<ESP32CameraJPEGEncoder *>(arg); ESP32CameraJPEGEncoder *that = reinterpret_cast<ESP32CameraJPEGEncoder *>(arg);
uint8_t *buffer = that->output_->get_data(); uint8_t *buffer = that->output_->get_data();
size_t buffer_length = that->output_->get_max_size(); size_t buffer_length = that->output_->get_max_size();

View File

@@ -1,5 +1,7 @@
#pragma once #pragma once
#include "esphome/core/defines.h"
#ifdef USE_ESP32_CAMERA_JPEG_ENCODER #ifdef USE_ESP32_CAMERA_JPEG_ENCODER
#include <esp_camera.h> #include <esp_camera.h>
@@ -24,7 +26,7 @@ class ESP32CameraJPEGEncoder : public camera::Encoder {
void dump_config() override; void dump_config() override;
// ------------------------- // -------------------------
protected: protected:
static size_t callback_(void *arg, size_t index, const void *data, size_t len); static size_t callback(void *arg, size_t index, const void *data, size_t len);
pixformat_t to_internal_(camera::PixelFormat format); pixformat_t to_internal_(camera::PixelFormat format);
camera::EncoderBuffer *output_{}; camera::EncoderBuffer *output_{};

View File

@@ -125,8 +125,8 @@ EAP_AUTH_SCHEMA = cv.All(
cv.Optional(CONF_USERNAME): cv.string_strict, cv.Optional(CONF_USERNAME): cv.string_strict,
cv.Optional(CONF_PASSWORD): cv.string_strict, cv.Optional(CONF_PASSWORD): cv.string_strict,
cv.Optional(CONF_CERTIFICATE_AUTHORITY): wpa2_eap.validate_certificate, cv.Optional(CONF_CERTIFICATE_AUTHORITY): wpa2_eap.validate_certificate,
cv.SplitDefault(CONF_TTLS_PHASE_2, esp32_idf="mschapv2"): cv.All( cv.SplitDefault(CONF_TTLS_PHASE_2, esp32="mschapv2"): cv.All(
cv.enum(TTLS_PHASE_2), cv.only_with_esp_idf cv.enum(TTLS_PHASE_2), cv.only_on_esp32
), ),
cv.Inclusive( cv.Inclusive(
CONF_CERTIFICATE, "certificate_and_key" CONF_CERTIFICATE, "certificate_and_key"
@@ -280,11 +280,11 @@ CONFIG_SCHEMA = cv.All(
cv.SplitDefault(CONF_OUTPUT_POWER, esp8266=20.0): cv.All( cv.SplitDefault(CONF_OUTPUT_POWER, esp8266=20.0): cv.All(
cv.decibel, cv.float_range(min=8.5, max=20.5) cv.decibel, cv.float_range(min=8.5, max=20.5)
), ),
cv.SplitDefault(CONF_ENABLE_BTM, esp32_idf=False): cv.All( cv.SplitDefault(CONF_ENABLE_BTM, esp32=False): cv.All(
cv.boolean, cv.only_with_esp_idf cv.boolean, cv.only_on_esp32
), ),
cv.SplitDefault(CONF_ENABLE_RRM, esp32_idf=False): cv.All( cv.SplitDefault(CONF_ENABLE_RRM, esp32=False): cv.All(
cv.boolean, cv.only_with_esp_idf cv.boolean, cv.only_on_esp32
), ),
cv.Optional(CONF_PASSIVE_SCAN, default=False): cv.boolean, cv.Optional(CONF_PASSIVE_SCAN, default=False): cv.boolean,
cv.Optional("enable_mdns"): cv.invalid( cv.Optional("enable_mdns"): cv.invalid(
@@ -416,10 +416,10 @@ async def to_code(config):
if CORE.is_esp8266: if CORE.is_esp8266:
cg.add_library("ESP8266WiFi", None) cg.add_library("ESP8266WiFi", None)
elif (CORE.is_esp32 and CORE.using_arduino) or CORE.is_rp2040: elif CORE.is_rp2040:
cg.add_library("WiFi", None) cg.add_library("WiFi", None)
if CORE.is_esp32 and CORE.using_esp_idf: if CORE.is_esp32:
if config[CONF_ENABLE_BTM] or config[CONF_ENABLE_RRM]: if config[CONF_ENABLE_BTM] or config[CONF_ENABLE_RRM]:
add_idf_sdkconfig_option("CONFIG_WPA_11KV_SUPPORT", True) add_idf_sdkconfig_option("CONFIG_WPA_11KV_SUPPORT", True)
cg.add_define("USE_WIFI_11KV_SUPPORT") cg.add_define("USE_WIFI_11KV_SUPPORT")
@@ -506,8 +506,10 @@ async def wifi_set_sta_to_code(config, action_id, template_arg, args):
FILTER_SOURCE_FILES = filter_source_files_from_platform( FILTER_SOURCE_FILES = filter_source_files_from_platform(
{ {
"wifi_component_esp32_arduino.cpp": {PlatformFramework.ESP32_ARDUINO}, "wifi_component_esp_idf.cpp": {
"wifi_component_esp_idf.cpp": {PlatformFramework.ESP32_IDF}, PlatformFramework.ESP32_IDF,
PlatformFramework.ESP32_ARDUINO,
},
"wifi_component_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, "wifi_component_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO},
"wifi_component_libretiny.cpp": { "wifi_component_libretiny.cpp": {
PlatformFramework.BK72XX_ARDUINO, PlatformFramework.BK72XX_ARDUINO,

View File

@@ -3,7 +3,7 @@
#include <cinttypes> #include <cinttypes>
#include <map> #include <map>
#ifdef USE_ESP_IDF #ifdef USE_ESP32
#if (ESP_IDF_VERSION_MAJOR >= 5 && ESP_IDF_VERSION_MINOR >= 1) #if (ESP_IDF_VERSION_MAJOR >= 5 && ESP_IDF_VERSION_MINOR >= 1)
#include <esp_eap_client.h> #include <esp_eap_client.h>
#else #else
@@ -11,7 +11,7 @@
#endif #endif
#endif #endif
#if defined(USE_ESP32) || defined(USE_ESP_IDF) #if defined(USE_ESP32)
#include <esp_wifi.h> #include <esp_wifi.h>
#endif #endif
#ifdef USE_ESP8266 #ifdef USE_ESP8266
@@ -344,7 +344,7 @@ void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) {
ESP_LOGV(TAG, " Identity: " LOG_SECRET("'%s'"), eap_config.identity.c_str()); ESP_LOGV(TAG, " Identity: " LOG_SECRET("'%s'"), eap_config.identity.c_str());
ESP_LOGV(TAG, " Username: " LOG_SECRET("'%s'"), eap_config.username.c_str()); ESP_LOGV(TAG, " Username: " LOG_SECRET("'%s'"), eap_config.username.c_str());
ESP_LOGV(TAG, " Password: " LOG_SECRET("'%s'"), eap_config.password.c_str()); ESP_LOGV(TAG, " Password: " LOG_SECRET("'%s'"), eap_config.password.c_str());
#ifdef USE_ESP_IDF #ifdef USE_ESP32
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
std::map<esp_eap_ttls_phase2_types, std::string> phase2types = {{ESP_EAP_TTLS_PHASE2_PAP, "pap"}, std::map<esp_eap_ttls_phase2_types, std::string> phase2types = {{ESP_EAP_TTLS_PHASE2_PAP, "pap"},
{ESP_EAP_TTLS_PHASE2_CHAP, "chap"}, {ESP_EAP_TTLS_PHASE2_CHAP, "chap"},

View File

@@ -20,7 +20,7 @@
#include <WiFi.h> #include <WiFi.h>
#endif #endif
#if defined(USE_ESP_IDF) && defined(USE_WIFI_WPA2_EAP) #if defined(USE_ESP32) && defined(USE_WIFI_WPA2_EAP)
#if (ESP_IDF_VERSION_MAJOR >= 5) && (ESP_IDF_VERSION_MINOR >= 1) #if (ESP_IDF_VERSION_MAJOR >= 5) && (ESP_IDF_VERSION_MINOR >= 1)
#include <esp_eap_client.h> #include <esp_eap_client.h>
#else #else
@@ -113,7 +113,7 @@ struct EAPAuth {
const char *client_cert; const char *client_cert;
const char *client_key; const char *client_key;
// used for EAP-TTLS // used for EAP-TTLS
#ifdef USE_ESP_IDF #ifdef USE_ESP32
esp_eap_ttls_phase2_types ttls_phase_2; esp_eap_ttls_phase2_types ttls_phase_2;
#endif #endif
}; };
@@ -199,7 +199,7 @@ enum WiFiPowerSaveMode : uint8_t {
WIFI_POWER_SAVE_HIGH, WIFI_POWER_SAVE_HIGH,
}; };
#ifdef USE_ESP_IDF #ifdef USE_ESP32
struct IDFWiFiEvent; struct IDFWiFiEvent;
#endif #endif
@@ -368,7 +368,7 @@ class WiFiComponent : public Component {
void wifi_event_callback_(arduino_event_id_t event, arduino_event_info_t info); void wifi_event_callback_(arduino_event_id_t event, arduino_event_info_t info);
void wifi_scan_done_callback_(); void wifi_scan_done_callback_();
#endif #endif
#ifdef USE_ESP_IDF #ifdef USE_ESP32
void wifi_process_event_(IDFWiFiEvent *data); void wifi_process_event_(IDFWiFiEvent *data);
#endif #endif

View File

@@ -1,860 +0,0 @@
#include "wifi_component.h"
#ifdef USE_WIFI
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
#include <esp_netif.h>
#include <esp_wifi.h>
#include <algorithm>
#include <utility>
#ifdef USE_WIFI_WPA2_EAP
#include <esp_eap_client.h>
#endif
#ifdef USE_WIFI_AP
#include "dhcpserver/dhcpserver.h"
#endif // USE_WIFI_AP
#include "lwip/apps/sntp.h"
#include "lwip/dns.h"
#include "lwip/err.h"
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "esphome/core/util.h"
namespace esphome {
namespace wifi {
static const char *const TAG = "wifi_esp32";
static esp_netif_t *s_sta_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
#ifdef USE_WIFI_AP
static esp_netif_t *s_ap_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
#endif // USE_WIFI_AP
static bool s_sta_connecting = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
void WiFiComponent::wifi_pre_setup_() {
uint8_t mac[6];
if (has_custom_mac_address()) {
get_mac_address_raw(mac);
set_mac_address(mac);
}
auto f = std::bind(&WiFiComponent::wifi_event_callback_, this, std::placeholders::_1, std::placeholders::_2);
WiFi.onEvent(f);
WiFi.persistent(false);
// Make sure WiFi is in clean state before anything starts
this->wifi_mode_(false, false);
}
bool WiFiComponent::wifi_mode_(optional<bool> sta, optional<bool> ap) {
wifi_mode_t current_mode = WiFiClass::getMode();
bool current_sta = current_mode == WIFI_MODE_STA || current_mode == WIFI_MODE_APSTA;
bool current_ap = current_mode == WIFI_MODE_AP || current_mode == WIFI_MODE_APSTA;
bool set_sta = sta.value_or(current_sta);
bool set_ap = ap.value_or(current_ap);
wifi_mode_t set_mode;
if (set_sta && set_ap) {
set_mode = WIFI_MODE_APSTA;
} else if (set_sta && !set_ap) {
set_mode = WIFI_MODE_STA;
} else if (!set_sta && set_ap) {
set_mode = WIFI_MODE_AP;
} else {
set_mode = WIFI_MODE_NULL;
}
if (current_mode == set_mode)
return true;
if (set_sta && !current_sta) {
ESP_LOGV(TAG, "Enabling STA");
} else if (!set_sta && current_sta) {
ESP_LOGV(TAG, "Disabling STA");
}
if (set_ap && !current_ap) {
ESP_LOGV(TAG, "Enabling AP");
} else if (!set_ap && current_ap) {
ESP_LOGV(TAG, "Disabling AP");
}
bool ret = WiFiClass::mode(set_mode);
if (!ret) {
ESP_LOGW(TAG, "Setting mode failed");
return false;
}
// WiFiClass::mode above calls esp_netif_create_default_wifi_sta() and
// esp_netif_create_default_wifi_ap(), which creates the interfaces.
// s_sta_netif handle is set during ESPHOME_EVENT_ID_WIFI_STA_START event
#ifdef USE_WIFI_AP
if (set_ap)
s_ap_netif = esp_netif_get_handle_from_ifkey("WIFI_AP_DEF");
#endif
return ret;
}
bool WiFiComponent::wifi_sta_pre_setup_() {
if (!this->wifi_mode_(true, {}))
return false;
WiFi.setAutoReconnect(false);
delay(10);
return true;
}
bool WiFiComponent::wifi_apply_output_power_(float output_power) {
int8_t val = static_cast<int8_t>(output_power * 4);
return esp_wifi_set_max_tx_power(val) == ESP_OK;
}
bool WiFiComponent::wifi_apply_power_save_() {
wifi_ps_type_t power_save;
switch (this->power_save_) {
case WIFI_POWER_SAVE_LIGHT:
power_save = WIFI_PS_MIN_MODEM;
break;
case WIFI_POWER_SAVE_HIGH:
power_save = WIFI_PS_MAX_MODEM;
break;
case WIFI_POWER_SAVE_NONE:
default:
power_save = WIFI_PS_NONE;
break;
}
return esp_wifi_set_ps(power_save) == ESP_OK;
}
bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) {
// enable STA
if (!this->wifi_mode_(true, {}))
return false;
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv417wifi_sta_config_t
wifi_config_t conf;
memset(&conf, 0, sizeof(conf));
if (ap.get_ssid().size() > sizeof(conf.sta.ssid)) {
ESP_LOGE(TAG, "SSID too long");
return false;
}
if (ap.get_password().size() > sizeof(conf.sta.password)) {
ESP_LOGE(TAG, "Password too long");
return false;
}
memcpy(reinterpret_cast<char *>(conf.sta.ssid), ap.get_ssid().c_str(), ap.get_ssid().size());
memcpy(reinterpret_cast<char *>(conf.sta.password), ap.get_password().c_str(), ap.get_password().size());
// The weakest authmode to accept in the fast scan mode
if (ap.get_password().empty()) {
conf.sta.threshold.authmode = WIFI_AUTH_OPEN;
} else {
conf.sta.threshold.authmode = WIFI_AUTH_WPA_WPA2_PSK;
}
#ifdef USE_WIFI_WPA2_EAP
if (ap.get_eap().has_value()) {
conf.sta.threshold.authmode = WIFI_AUTH_WPA2_ENTERPRISE;
}
#endif
if (ap.get_bssid().has_value()) {
conf.sta.bssid_set = true;
memcpy(conf.sta.bssid, ap.get_bssid()->data(), 6);
} else {
conf.sta.bssid_set = false;
}
if (ap.get_channel().has_value()) {
conf.sta.channel = *ap.get_channel();
conf.sta.scan_method = WIFI_FAST_SCAN;
} else {
conf.sta.scan_method = WIFI_ALL_CHANNEL_SCAN;
}
// Listen interval for ESP32 station to receive beacon when WIFI_PS_MAX_MODEM is set.
// Units: AP beacon intervals. Defaults to 3 if set to 0.
conf.sta.listen_interval = 0;
// Protected Management Frame
// Device will prefer to connect in PMF mode if other device also advertises PMF capability.
conf.sta.pmf_cfg.capable = true;
conf.sta.pmf_cfg.required = false;
// note, we do our own filtering
// The minimum rssi to accept in the fast scan mode
conf.sta.threshold.rssi = -127;
conf.sta.threshold.authmode = WIFI_AUTH_OPEN;
wifi_config_t current_conf;
esp_err_t err;
err = esp_wifi_get_config(WIFI_IF_STA, &current_conf);
if (err != ERR_OK) {
ESP_LOGW(TAG, "esp_wifi_get_config failed: %s", esp_err_to_name(err));
// can continue
}
if (memcmp(&current_conf, &conf, sizeof(wifi_config_t)) != 0) { // NOLINT
err = esp_wifi_disconnect();
if (err != ESP_OK) {
ESP_LOGV(TAG, "esp_wifi_disconnect failed: %s", esp_err_to_name(err));
return false;
}
}
err = esp_wifi_set_config(WIFI_IF_STA, &conf);
if (err != ESP_OK) {
ESP_LOGV(TAG, "esp_wifi_set_config failed: %s", esp_err_to_name(err));
return false;
}
if (!this->wifi_sta_ip_config_(ap.get_manual_ip())) {
return false;
}
// setup enterprise authentication if required
#ifdef USE_WIFI_WPA2_EAP
if (ap.get_eap().has_value()) {
// note: all certificates and keys have to be null terminated. Lengths are appended by +1 to include \0.
EAPAuth eap = ap.get_eap().value();
err = esp_eap_client_set_identity((uint8_t *) eap.identity.c_str(), eap.identity.length());
if (err != ESP_OK) {
ESP_LOGV(TAG, "esp_eap_client_set_identity failed: %d", err);
}
int ca_cert_len = strlen(eap.ca_cert);
int client_cert_len = strlen(eap.client_cert);
int client_key_len = strlen(eap.client_key);
if (ca_cert_len) {
err = esp_eap_client_set_ca_cert((uint8_t *) eap.ca_cert, ca_cert_len + 1);
if (err != ESP_OK) {
ESP_LOGV(TAG, "esp_eap_client_set_ca_cert failed: %d", err);
}
}
// workout what type of EAP this is
// validation is not required as the config tool has already validated it
if (client_cert_len && client_key_len) {
// if we have certs, this must be EAP-TLS
err = esp_eap_client_set_certificate_and_key((uint8_t *) eap.client_cert, client_cert_len + 1,
(uint8_t *) eap.client_key, client_key_len + 1,
(uint8_t *) eap.password.c_str(), strlen(eap.password.c_str()));
if (err != ESP_OK) {
ESP_LOGV(TAG, "esp_eap_client_set_certificate_and_key failed: %d", err);
}
} else {
// in the absence of certs, assume this is username/password based
err = esp_eap_client_set_username((uint8_t *) eap.username.c_str(), eap.username.length());
if (err != ESP_OK) {
ESP_LOGV(TAG, "esp_eap_client_set_username failed: %d", err);
}
err = esp_eap_client_set_password((uint8_t *) eap.password.c_str(), eap.password.length());
if (err != ESP_OK) {
ESP_LOGV(TAG, "esp_eap_client_set_password failed: %d", err);
}
}
err = esp_wifi_sta_enterprise_enable();
if (err != ESP_OK) {
ESP_LOGV(TAG, "esp_wifi_sta_enterprise_enable failed: %d", err);
}
}
#endif // USE_WIFI_WPA2_EAP
this->wifi_apply_hostname_();
s_sta_connecting = true;
err = esp_wifi_connect();
if (err != ESP_OK) {
ESP_LOGW(TAG, "esp_wifi_connect failed: %s", esp_err_to_name(err));
return false;
}
return true;
}
bool WiFiComponent::wifi_sta_ip_config_(optional<ManualIP> manual_ip) {
// enable STA
if (!this->wifi_mode_(true, {}))
return false;
// Check if the STA interface is initialized before using it
if (s_sta_netif == nullptr) {
ESP_LOGW(TAG, "STA interface not initialized");
return false;
}
esp_netif_dhcp_status_t dhcp_status;
esp_err_t err = esp_netif_dhcpc_get_status(s_sta_netif, &dhcp_status);
if (err != ESP_OK) {
ESP_LOGV(TAG, "esp_netif_dhcpc_get_status failed: %s", esp_err_to_name(err));
return false;
}
if (!manual_ip.has_value()) {
// sntp_servermode_dhcp lwip/sntp.c (Required to lock TCPIP core functionality!)
// https://github.com/esphome/issues/issues/6591
// https://github.com/espressif/arduino-esp32/issues/10526
{
LwIPLock lock;
// lwIP starts the SNTP client if it gets an SNTP server from DHCP. We don't need the time, and more importantly,
// the built-in SNTP client has a memory leak in certain situations. Disable this feature.
// https://github.com/esphome/issues/issues/2299
sntp_servermode_dhcp(false);
}
// No manual IP is set; use DHCP client
if (dhcp_status != ESP_NETIF_DHCP_STARTED) {
err = esp_netif_dhcpc_start(s_sta_netif);
if (err != ESP_OK) {
ESP_LOGV(TAG, "Starting DHCP client failed: %d", err);
}
return err == ESP_OK;
}
return true;
}
esp_netif_ip_info_t info; // struct of ip4_addr_t with ip, netmask, gw
info.ip = manual_ip->static_ip;
info.gw = manual_ip->gateway;
info.netmask = manual_ip->subnet;
err = esp_netif_dhcpc_stop(s_sta_netif);
if (err != ESP_OK && err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED) {
ESP_LOGV(TAG, "Stopping DHCP client failed: %s", esp_err_to_name(err));
}
err = esp_netif_set_ip_info(s_sta_netif, &info);
if (err != ESP_OK) {
ESP_LOGV(TAG, "Setting manual IP info failed: %s", esp_err_to_name(err));
}
esp_netif_dns_info_t dns;
if (manual_ip->dns1.is_set()) {
dns.ip = manual_ip->dns1;
esp_netif_set_dns_info(s_sta_netif, ESP_NETIF_DNS_MAIN, &dns);
}
if (manual_ip->dns2.is_set()) {
dns.ip = manual_ip->dns2;
esp_netif_set_dns_info(s_sta_netif, ESP_NETIF_DNS_BACKUP, &dns);
}
return true;
}
network::IPAddresses WiFiComponent::wifi_sta_ip_addresses() {
if (!this->has_sta())
return {};
network::IPAddresses addresses;
esp_netif_ip_info_t ip;
esp_err_t err = esp_netif_get_ip_info(s_sta_netif, &ip);
if (err != ESP_OK) {
ESP_LOGV(TAG, "esp_netif_get_ip_info failed: %s", esp_err_to_name(err));
// TODO: do something smarter
// return false;
} else {
addresses[0] = network::IPAddress(&ip.ip);
}
#if USE_NETWORK_IPV6
struct esp_ip6_addr if_ip6s[CONFIG_LWIP_IPV6_NUM_ADDRESSES];
uint8_t count = 0;
count = esp_netif_get_all_ip6(s_sta_netif, if_ip6s);
assert(count <= CONFIG_LWIP_IPV6_NUM_ADDRESSES);
for (int i = 0; i < count; i++) {
addresses[i + 1] = network::IPAddress(&if_ip6s[i]);
}
#endif /* USE_NETWORK_IPV6 */
return addresses;
}
bool WiFiComponent::wifi_apply_hostname_() {
// setting is done in SYSTEM_EVENT_STA_START callback
return true;
}
const char *get_auth_mode_str(uint8_t mode) {
switch (mode) {
case WIFI_AUTH_OPEN:
return "OPEN";
case WIFI_AUTH_WEP:
return "WEP";
case WIFI_AUTH_WPA_PSK:
return "WPA PSK";
case WIFI_AUTH_WPA2_PSK:
return "WPA2 PSK";
case WIFI_AUTH_WPA_WPA2_PSK:
return "WPA/WPA2 PSK";
case WIFI_AUTH_WPA2_ENTERPRISE:
return "WPA2 Enterprise";
case WIFI_AUTH_WPA3_PSK:
return "WPA3 PSK";
case WIFI_AUTH_WPA2_WPA3_PSK:
return "WPA2/WPA3 PSK";
case WIFI_AUTH_WAPI_PSK:
return "WAPI PSK";
default:
return "UNKNOWN";
}
}
using esphome_ip4_addr_t = esp_ip4_addr_t;
std::string format_ip4_addr(const esphome_ip4_addr_t &ip) {
char buf[20];
sprintf(buf, "%u.%u.%u.%u", uint8_t(ip.addr >> 0), uint8_t(ip.addr >> 8), uint8_t(ip.addr >> 16),
uint8_t(ip.addr >> 24));
return buf;
}
const char *get_op_mode_str(uint8_t mode) {
switch (mode) {
case WIFI_OFF:
return "OFF";
case WIFI_STA:
return "STA";
case WIFI_AP:
return "AP";
case WIFI_AP_STA:
return "AP+STA";
default:
return "UNKNOWN";
}
}
const char *get_disconnect_reason_str(uint8_t reason) {
switch (reason) {
case WIFI_REASON_AUTH_EXPIRE:
return "Auth Expired";
case WIFI_REASON_AUTH_LEAVE:
return "Auth Leave";
case WIFI_REASON_ASSOC_EXPIRE:
return "Association Expired";
case WIFI_REASON_ASSOC_TOOMANY:
return "Too Many Associations";
case WIFI_REASON_NOT_AUTHED:
return "Not Authenticated";
case WIFI_REASON_NOT_ASSOCED:
return "Not Associated";
case WIFI_REASON_ASSOC_LEAVE:
return "Association Leave";
case WIFI_REASON_ASSOC_NOT_AUTHED:
return "Association not Authenticated";
case WIFI_REASON_DISASSOC_PWRCAP_BAD:
return "Disassociate Power Cap Bad";
case WIFI_REASON_DISASSOC_SUPCHAN_BAD:
return "Disassociate Supported Channel Bad";
case WIFI_REASON_IE_INVALID:
return "IE Invalid";
case WIFI_REASON_MIC_FAILURE:
return "Mic Failure";
case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT:
return "4-Way Handshake Timeout";
case WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT:
return "Group Key Update Timeout";
case WIFI_REASON_IE_IN_4WAY_DIFFERS:
return "IE In 4-Way Handshake Differs";
case WIFI_REASON_GROUP_CIPHER_INVALID:
return "Group Cipher Invalid";
case WIFI_REASON_PAIRWISE_CIPHER_INVALID:
return "Pairwise Cipher Invalid";
case WIFI_REASON_AKMP_INVALID:
return "AKMP Invalid";
case WIFI_REASON_UNSUPP_RSN_IE_VERSION:
return "Unsupported RSN IE version";
case WIFI_REASON_INVALID_RSN_IE_CAP:
return "Invalid RSN IE Cap";
case WIFI_REASON_802_1X_AUTH_FAILED:
return "802.1x Authentication Failed";
case WIFI_REASON_CIPHER_SUITE_REJECTED:
return "Cipher Suite Rejected";
case WIFI_REASON_BEACON_TIMEOUT:
return "Beacon Timeout";
case WIFI_REASON_NO_AP_FOUND:
return "AP Not Found";
case WIFI_REASON_AUTH_FAIL:
return "Authentication Failed";
case WIFI_REASON_ASSOC_FAIL:
return "Association Failed";
case WIFI_REASON_HANDSHAKE_TIMEOUT:
return "Handshake Failed";
case WIFI_REASON_CONNECTION_FAIL:
return "Connection Failed";
case WIFI_REASON_AP_TSF_RESET:
return "AP TSF reset";
case WIFI_REASON_ROAMING:
return "Station Roaming";
case WIFI_REASON_ASSOC_COMEBACK_TIME_TOO_LONG:
return "Association comeback time too long";
case WIFI_REASON_SA_QUERY_TIMEOUT:
return "SA query timeout";
case WIFI_REASON_NO_AP_FOUND_W_COMPATIBLE_SECURITY:
return "No AP found with compatible security";
case WIFI_REASON_NO_AP_FOUND_IN_AUTHMODE_THRESHOLD:
return "No AP found in auth mode threshold";
case WIFI_REASON_NO_AP_FOUND_IN_RSSI_THRESHOLD:
return "No AP found in RSSI threshold";
case WIFI_REASON_UNSPECIFIED:
default:
return "Unspecified";
}
}
void WiFiComponent::wifi_loop_() {}
#define ESPHOME_EVENT_ID_WIFI_READY ARDUINO_EVENT_WIFI_READY
#define ESPHOME_EVENT_ID_WIFI_SCAN_DONE ARDUINO_EVENT_WIFI_SCAN_DONE
#define ESPHOME_EVENT_ID_WIFI_STA_START ARDUINO_EVENT_WIFI_STA_START
#define ESPHOME_EVENT_ID_WIFI_STA_STOP ARDUINO_EVENT_WIFI_STA_STOP
#define ESPHOME_EVENT_ID_WIFI_STA_CONNECTED ARDUINO_EVENT_WIFI_STA_CONNECTED
#define ESPHOME_EVENT_ID_WIFI_STA_DISCONNECTED ARDUINO_EVENT_WIFI_STA_DISCONNECTED
#define ESPHOME_EVENT_ID_WIFI_STA_AUTHMODE_CHANGE ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE
#define ESPHOME_EVENT_ID_WIFI_STA_GOT_IP ARDUINO_EVENT_WIFI_STA_GOT_IP
#define ESPHOME_EVENT_ID_WIFI_STA_GOT_IP6 ARDUINO_EVENT_WIFI_STA_GOT_IP6
#define ESPHOME_EVENT_ID_WIFI_STA_LOST_IP ARDUINO_EVENT_WIFI_STA_LOST_IP
#define ESPHOME_EVENT_ID_WIFI_AP_START ARDUINO_EVENT_WIFI_AP_START
#define ESPHOME_EVENT_ID_WIFI_AP_STOP ARDUINO_EVENT_WIFI_AP_STOP
#define ESPHOME_EVENT_ID_WIFI_AP_STACONNECTED ARDUINO_EVENT_WIFI_AP_STACONNECTED
#define ESPHOME_EVENT_ID_WIFI_AP_STADISCONNECTED ARDUINO_EVENT_WIFI_AP_STADISCONNECTED
#define ESPHOME_EVENT_ID_WIFI_AP_STAIPASSIGNED ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED
#define ESPHOME_EVENT_ID_WIFI_AP_PROBEREQRECVED ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED
#define ESPHOME_EVENT_ID_WIFI_AP_GOT_IP6 ARDUINO_EVENT_WIFI_AP_GOT_IP6
using esphome_wifi_event_id_t = arduino_event_id_t;
using esphome_wifi_event_info_t = arduino_event_info_t;
void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_wifi_event_info_t info) {
switch (event) {
case ESPHOME_EVENT_ID_WIFI_READY: {
ESP_LOGV(TAG, "Ready");
break;
}
case ESPHOME_EVENT_ID_WIFI_SCAN_DONE: {
auto it = info.wifi_scan_done;
ESP_LOGV(TAG, "Scan done: status=%u number=%u scan_id=%u", it.status, it.number, it.scan_id);
this->wifi_scan_done_callback_();
break;
}
case ESPHOME_EVENT_ID_WIFI_STA_START: {
ESP_LOGV(TAG, "STA start");
// apply hostname
s_sta_netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
esp_err_t err = esp_netif_set_hostname(s_sta_netif, App.get_name().c_str());
if (err != ERR_OK) {
ESP_LOGW(TAG, "esp_netif_set_hostname failed: %s", esp_err_to_name(err));
}
break;
}
case ESPHOME_EVENT_ID_WIFI_STA_STOP: {
ESP_LOGV(TAG, "STA stop");
break;
}
case ESPHOME_EVENT_ID_WIFI_STA_CONNECTED: {
auto it = info.wifi_sta_connected;
char buf[33];
memcpy(buf, it.ssid, it.ssid_len);
buf[it.ssid_len] = '\0';
ESP_LOGV(TAG, "Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf,
format_mac_address_pretty(it.bssid).c_str(), it.channel, get_auth_mode_str(it.authmode));
#if USE_NETWORK_IPV6
this->set_timeout(100, [] { WiFi.enableIPv6(); });
#endif /* USE_NETWORK_IPV6 */
break;
}
case ESPHOME_EVENT_ID_WIFI_STA_DISCONNECTED: {
auto it = info.wifi_sta_disconnected;
char buf[33];
memcpy(buf, it.ssid, it.ssid_len);
buf[it.ssid_len] = '\0';
if (it.reason == WIFI_REASON_NO_AP_FOUND) {
ESP_LOGW(TAG, "Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf);
} else {
ESP_LOGW(TAG, "Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf,
format_mac_address_pretty(it.bssid).c_str(), get_disconnect_reason_str(it.reason));
}
uint8_t reason = it.reason;
if (reason == WIFI_REASON_AUTH_EXPIRE || reason == WIFI_REASON_BEACON_TIMEOUT ||
reason == WIFI_REASON_NO_AP_FOUND || reason == WIFI_REASON_ASSOC_FAIL ||
reason == WIFI_REASON_HANDSHAKE_TIMEOUT) {
err_t err = esp_wifi_disconnect();
if (err != ESP_OK) {
ESP_LOGV(TAG, "Disconnect failed: %s", esp_err_to_name(err));
}
this->error_from_callback_ = true;
}
s_sta_connecting = false;
break;
}
case ESPHOME_EVENT_ID_WIFI_STA_AUTHMODE_CHANGE: {
auto it = info.wifi_sta_authmode_change;
ESP_LOGV(TAG, "Authmode Change old=%s new=%s", get_auth_mode_str(it.old_mode), get_auth_mode_str(it.new_mode));
// Mitigate CVE-2020-12638
// https://lbsfilm.at/blog/wpa2-authenticationmode-downgrade-in-espressif-microprocessors
if (it.old_mode != WIFI_AUTH_OPEN && it.new_mode == WIFI_AUTH_OPEN) {
ESP_LOGW(TAG, "Potential Authmode downgrade detected, disconnecting");
// we can't call retry_connect() from this context, so disconnect immediately
// and notify main thread with error_from_callback_
err_t err = esp_wifi_disconnect();
if (err != ESP_OK) {
ESP_LOGW(TAG, "Disconnect failed: %s", esp_err_to_name(err));
}
this->error_from_callback_ = true;
}
break;
}
case ESPHOME_EVENT_ID_WIFI_STA_GOT_IP: {
auto it = info.got_ip.ip_info;
ESP_LOGV(TAG, "static_ip=%s gateway=%s", format_ip4_addr(it.ip).c_str(), format_ip4_addr(it.gw).c_str());
this->got_ipv4_address_ = true;
#if USE_NETWORK_IPV6
s_sta_connecting = this->num_ipv6_addresses_ < USE_NETWORK_MIN_IPV6_ADDR_COUNT;
#else
s_sta_connecting = false;
#endif /* USE_NETWORK_IPV6 */
break;
}
#if USE_NETWORK_IPV6
case ESPHOME_EVENT_ID_WIFI_STA_GOT_IP6: {
auto it = info.got_ip6.ip6_info;
ESP_LOGV(TAG, "IPv6 address=" IPV6STR, IPV62STR(it.ip));
this->num_ipv6_addresses_++;
s_sta_connecting = !(this->got_ipv4_address_ & (this->num_ipv6_addresses_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT));
break;
}
#endif /* USE_NETWORK_IPV6 */
case ESPHOME_EVENT_ID_WIFI_STA_LOST_IP: {
ESP_LOGV(TAG, "Lost IP");
this->got_ipv4_address_ = false;
break;
}
case ESPHOME_EVENT_ID_WIFI_AP_START: {
ESP_LOGV(TAG, "AP start");
break;
}
case ESPHOME_EVENT_ID_WIFI_AP_STOP: {
ESP_LOGV(TAG, "AP stop");
break;
}
case ESPHOME_EVENT_ID_WIFI_AP_STACONNECTED: {
auto it = info.wifi_sta_connected;
auto &mac = it.bssid;
ESP_LOGV(TAG, "AP client connected MAC=%s", format_mac_address_pretty(mac).c_str());
break;
}
case ESPHOME_EVENT_ID_WIFI_AP_STADISCONNECTED: {
auto it = info.wifi_sta_disconnected;
auto &mac = it.bssid;
ESP_LOGV(TAG, "AP client disconnected MAC=%s", format_mac_address_pretty(mac).c_str());
break;
}
case ESPHOME_EVENT_ID_WIFI_AP_STAIPASSIGNED: {
ESP_LOGV(TAG, "AP client assigned IP");
break;
}
case ESPHOME_EVENT_ID_WIFI_AP_PROBEREQRECVED: {
auto it = info.wifi_ap_probereqrecved;
ESP_LOGVV(TAG, "AP receive Probe Request MAC=%s RSSI=%d", format_mac_address_pretty(it.mac).c_str(), it.rssi);
break;
}
default:
break;
}
}
WiFiSTAConnectStatus WiFiComponent::wifi_sta_connect_status_() {
const auto status = WiFi.status();
if (status == WL_CONNECT_FAILED || status == WL_CONNECTION_LOST) {
return WiFiSTAConnectStatus::ERROR_CONNECT_FAILED;
}
if (status == WL_NO_SSID_AVAIL) {
return WiFiSTAConnectStatus::ERROR_NETWORK_NOT_FOUND;
}
if (s_sta_connecting) {
return WiFiSTAConnectStatus::CONNECTING;
}
if (status == WL_CONNECTED) {
return WiFiSTAConnectStatus::CONNECTED;
}
return WiFiSTAConnectStatus::IDLE;
}
bool WiFiComponent::wifi_scan_start_(bool passive) {
// enable STA
if (!this->wifi_mode_(true, {}))
return false;
// need to use WiFi because of WiFiScanClass allocations :(
int16_t err = WiFi.scanNetworks(true, true, passive, 200);
if (err != WIFI_SCAN_RUNNING) {
ESP_LOGV(TAG, "WiFi.scanNetworks failed: %d", err);
return false;
}
return true;
}
void WiFiComponent::wifi_scan_done_callback_() {
this->scan_result_.clear();
int16_t num = WiFi.scanComplete();
if (num < 0)
return;
this->scan_result_.reserve(static_cast<unsigned int>(num));
for (int i = 0; i < num; i++) {
String ssid = WiFi.SSID(i);
wifi_auth_mode_t authmode = WiFi.encryptionType(i);
int32_t rssi = WiFi.RSSI(i);
uint8_t *bssid = WiFi.BSSID(i);
int32_t channel = WiFi.channel(i);
WiFiScanResult scan({bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]}, std::string(ssid.c_str()),
channel, rssi, authmode != WIFI_AUTH_OPEN, ssid.length() == 0);
this->scan_result_.push_back(scan);
}
WiFi.scanDelete();
this->scan_done_ = true;
}
#ifdef USE_WIFI_AP
bool WiFiComponent::wifi_ap_ip_config_(optional<ManualIP> manual_ip) {
esp_err_t err;
// enable AP
if (!this->wifi_mode_({}, true))
return false;
// Check if the AP interface is initialized before using it
if (s_ap_netif == nullptr) {
ESP_LOGW(TAG, "AP interface not initialized");
return false;
}
esp_netif_ip_info_t info;
if (manual_ip.has_value()) {
info.ip = manual_ip->static_ip;
info.gw = manual_ip->gateway;
info.netmask = manual_ip->subnet;
} else {
info.ip = network::IPAddress(192, 168, 4, 1);
info.gw = network::IPAddress(192, 168, 4, 1);
info.netmask = network::IPAddress(255, 255, 255, 0);
}
err = esp_netif_dhcps_stop(s_ap_netif);
if (err != ESP_OK && err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED) {
ESP_LOGE(TAG, "esp_netif_dhcps_stop failed: %s", esp_err_to_name(err));
return false;
}
err = esp_netif_set_ip_info(s_ap_netif, &info);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_netif_set_ip_info failed: %d", err);
return false;
}
dhcps_lease_t lease;
lease.enable = true;
network::IPAddress start_address = network::IPAddress(&info.ip);
start_address += 99;
lease.start_ip = start_address;
ESP_LOGV(TAG, "DHCP server IP lease start: %s", start_address.str().c_str());
start_address += 10;
lease.end_ip = start_address;
ESP_LOGV(TAG, "DHCP server IP lease end: %s", start_address.str().c_str());
err = esp_netif_dhcps_option(s_ap_netif, ESP_NETIF_OP_SET, ESP_NETIF_REQUESTED_IP_ADDRESS, &lease, sizeof(lease));
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_netif_dhcps_option failed: %d", err);
return false;
}
err = esp_netif_dhcps_start(s_ap_netif);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_netif_dhcps_start failed: %d", err);
return false;
}
return true;
}
bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) {
// enable AP
if (!this->wifi_mode_({}, true))
return false;
wifi_config_t conf;
memset(&conf, 0, sizeof(conf));
if (ap.get_ssid().size() > sizeof(conf.ap.ssid)) {
ESP_LOGE(TAG, "AP SSID too long");
return false;
}
memcpy(reinterpret_cast<char *>(conf.ap.ssid), ap.get_ssid().c_str(), ap.get_ssid().size());
conf.ap.channel = ap.get_channel().value_or(1);
conf.ap.ssid_hidden = ap.get_ssid().size();
conf.ap.max_connection = 5;
conf.ap.beacon_interval = 100;
if (ap.get_password().empty()) {
conf.ap.authmode = WIFI_AUTH_OPEN;
*conf.ap.password = 0;
} else {
conf.ap.authmode = WIFI_AUTH_WPA2_PSK;
if (ap.get_password().size() > sizeof(conf.ap.password)) {
ESP_LOGE(TAG, "AP password too long");
return false;
}
memcpy(reinterpret_cast<char *>(conf.ap.password), ap.get_password().c_str(), ap.get_password().size());
}
// pairwise cipher of SoftAP, group cipher will be derived using this.
conf.ap.pairwise_cipher = WIFI_CIPHER_TYPE_CCMP;
esp_err_t err = esp_wifi_set_config(WIFI_IF_AP, &conf);
if (err != ESP_OK) {
ESP_LOGV(TAG, "esp_wifi_set_config failed: %d", err);
return false;
}
yield();
if (!this->wifi_ap_ip_config_(ap.get_manual_ip())) {
ESP_LOGV(TAG, "wifi_ap_ip_config_ failed");
return false;
}
return true;
}
network::IPAddress WiFiComponent::wifi_soft_ap_ip() {
esp_netif_ip_info_t ip;
esp_netif_get_ip_info(s_ap_netif, &ip);
return network::IPAddress(&ip.ip);
}
#endif // USE_WIFI_AP
bool WiFiComponent::wifi_disconnect_() { return esp_wifi_disconnect(); }
bssid_t WiFiComponent::wifi_bssid() {
bssid_t bssid{};
uint8_t *raw_bssid = WiFi.BSSID();
if (raw_bssid != nullptr) {
for (size_t i = 0; i < bssid.size(); i++)
bssid[i] = raw_bssid[i];
}
return bssid;
}
std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); }
int8_t WiFiComponent::wifi_rssi() { return WiFi.RSSI(); }
int32_t WiFiComponent::get_wifi_channel() { return WiFi.channel(); }
network::IPAddress WiFiComponent::wifi_subnet_mask_() { return network::IPAddress(WiFi.subnetMask()); }
network::IPAddress WiFiComponent::wifi_gateway_ip_() { return network::IPAddress(WiFi.gatewayIP()); }
network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { return network::IPAddress(WiFi.dnsIP(num)); }
} // namespace wifi
} // namespace esphome
#endif // USE_ESP32_FRAMEWORK_ARDUINO
#endif

View File

@@ -1,7 +1,7 @@
#include "wifi_component.h" #include "wifi_component.h"
#ifdef USE_WIFI #ifdef USE_WIFI
#ifdef USE_ESP_IDF #ifdef USE_ESP32
#include <esp_event.h> #include <esp_event.h>
#include <esp_netif.h> #include <esp_netif.h>
@@ -1050,5 +1050,5 @@ network::IPAddress WiFiComponent::wifi_dns_ip_(int num) {
} // namespace wifi } // namespace wifi
} // namespace esphome } // namespace esphome
#endif // USE_ESP_IDF #endif // USE_ESP32
#endif #endif

View File

@@ -158,6 +158,7 @@
#define USE_ESP32_BLE_SERVER #define USE_ESP32_BLE_SERVER
#define USE_ESP32_BLE_UUID #define USE_ESP32_BLE_UUID
#define USE_ESP32_BLE_ADVERTISING #define USE_ESP32_BLE_ADVERTISING
#define USE_ESP32_CAMERA_JPEG_ENCODER
#define USE_I2C #define USE_I2C
#define USE_IMPROV #define USE_IMPROV
#define USE_MICROPHONE #define USE_MICROPHONE