Added WIFI auto-tune for EMP prevention

This commit is contained in:
RZ_MINIX\rober 2025-07-25 20:09:39 -07:00
parent 682ffb4fee
commit cf8ddebcc6
3 changed files with 462 additions and 222 deletions

View File

@ -46,43 +46,57 @@ void App::init()
m_led = new Led(LED_PIN);
m_wifi = new Wifi();
m_commandProcessor = new CommandProcessor(*this);
m_ble_service = new BleService(*this);
m_ble_service->start();
bool needs_provision = true;
bool first_attempt = true;
bool connected = false;
#if 0
ESP_LOGI(TAG, "Scanning wifi networks...");
int num_networks = WiFi.scanNetworks(false, false, false, 0);
for(int n = 0; n < num_networks; n++)
{
ESP_LOGI(TAG, "%s, %d", WiFi.SSID(n).c_str(), WiFi.RSSI(n));
}
#endif
if(SETTINGS.wifi.num > 0)
// Main connection/provisioning cycle
while(!connected)
{
// try connecting
m_led->setColor(0, 255, 0);
needs_provision = true;
// Try WiFi connections (provisioned networks + fallback)
if(SETTINGS.wifi.num > 0 || !first_attempt)
{
if(!first_attempt)
{
ESP_LOGI(TAG, "Retrying WiFi connections after provisioning timeout...");
m_wifi->pause(false); // Resume WiFi task
}
else
{
ESP_LOGI(TAG, "Trying provisioned networks...");
m_wifi->start();
}
m_led->setColor(0, 255, 0);
Wifi::WIFI_STATUS wifi_status = m_wifi->waitForConnection();
ESP_LOGI(TAG, "WiFi final status: %d", (int)wifi_status);
if(wifi_status == Wifi::WIFI_STATUS::CONNECTED)
{
ESP_LOGI(TAG, "Connected to WiFi successfully");
// Sync time
ESP_LOGI(TAG, "Getting local time...");
struct tm timeinfo;
int tries = 5;
while (tries--)
{
configTime(0, 0, "pool.ntp.org"); // needed?
configTime(0, 0, "pool.ntp.org");
if(getLocalTime(&timeinfo))
{
ESP_LOGI(TAG, "pool.ntp.org ok");
@ -93,45 +107,15 @@ void App::init()
}
needs_provision = false;
connected = true;
}
else
{
ESP_LOGI(TAG, "Provisioned networks failed, trying fallback...");
// Don't pause yet - let it try the fallback
// The modified startConnecting() will attempt CBX_IoT fallback
// Wait a bit more for the fallback attempt
delay(5000);
wifi_status = m_wifi->status();
if(wifi_status == Wifi::WIFI_STATUS::CONNECTED)
{
ESP_LOGI(TAG, "Connected via fallback network");
ESP_LOGI(TAG, "Getting local time...");
struct tm timeinfo;
int tries = 5;
while (tries--)
{
configTime(0, 0, "pool.ntp.org"); // needed?
if(getLocalTime(&timeinfo))
{
ESP_LOGI(TAG, "pool.ntp.org ok");
break;
}
else
ESP_LOGE(TAG, "pool.ntp.org Failed");
}
needs_provision = false;
}
else
{
ESP_LOGI(TAG, "Fallback also failed, entering provisioning mode");
ESP_LOGI(TAG, "All WiFi connections failed, will enter provisioning mode");
m_wifi->pause(true);
}
}
}
else
else if(first_attempt)
{
// No provisioned networks - try fallback directly
ESP_LOGI(TAG, "No provisioned networks, trying fallback...");
@ -142,14 +126,14 @@ void App::init()
if(wifi_status == Wifi::WIFI_STATUS::CONNECTED)
{
ESP_LOGI(TAG, "Connected to fallback network");
// Sync time
ESP_LOGI(TAG, "Getting local time...");
struct tm timeinfo;
int tries = 5;
while (tries--)
{
configTime(0, 0, "pool.ntp.org"); // needed?
configTime(0, 0, "pool.ntp.org");
if(getLocalTime(&timeinfo))
{
ESP_LOGI(TAG, "pool.ntp.org ok");
@ -158,21 +142,60 @@ void App::init()
else
ESP_LOGE(TAG, "pool.ntp.org Failed");
}
needs_provision = false;
connected = true;
}
else
{
ESP_LOGI(TAG, "Fallback failed, entering provisioning mode");
ESP_LOGI(TAG, "Fallback connection failed");
m_wifi->pause(true);
}
}
if(needs_provision)
// Enter provisioning mode if needed
if(needs_provision && !connected)
{
ESP_LOGI(TAG, "Entering provisioning mode for 10 minutes...");
m_led->setPulse(0, 0, 255);
// Start provisioning mode in WiFi
m_wifi->startProvisioning();
// Start the provision server
ProvisionSoftAP provision(80);
provision.start();
// Wait for either successful provisioning or timeout
ESP_LOGI(TAG, "Waiting for provisioning (10 minute timeout)...");
while(m_wifi->status() == Wifi::WIFI_STATUS::PROVISIONING)
{
if(m_wifi->isProvisioningTimedOut())
{
ESP_LOGW(TAG, "Provisioning timeout reached (10 minutes)");
break;
}
// You could add logic here to check if new credentials were received
// and attempt immediate connection if so
delay(1000);
}
// Stop provisioning
ESP_LOGI(TAG, "Stopping provisioning mode...");
// provision.stop(); // Add this method to ProvisionSoftAP if available
m_wifi->stopProvisioning();
ESP_LOGI(TAG, "Provisioning period ended, will retry connections in 5 seconds...");
delay(5000);
}
first_attempt = false;
}
ESP_LOGI(TAG, "WiFi connection established, continuing with app initialization...");
otaCheck();
// m_led->setPulse(255, 0, 255);

View File

@ -9,6 +9,7 @@
#include "app_config.h"
#include <esp_wifi.h>
#include "esp_phy_init.h"
static const char *TAG = "Wifi";
#define IGNORE_SSID_MINS 20
@ -20,6 +21,9 @@ Wifi::Wifi()
{
esp_log_level_set("wifi", ESP_LOG_WARN);
esp_log_level_set("wifi_init", ESP_LOG_INFO);
// Add WiFi configuration here
esp_wifi_set_country_code("US", true); // Or your country code
//WiFi.setSleep(false); // Disable power saving if BT is enabled, do not do this!
WiFi.onEvent(std::bind(&Wifi::wifi_event, this, _1, _2));
#ifdef USE_TIMER
@ -52,7 +56,12 @@ void Wifi::wifi_event(arduino_event_id_t event, arduino_event_info_t info)
if(event == ARDUINO_EVENT_WIFI_STA_DISCONNECTED)
{
wifi_err_reason_t reason = (wifi_err_reason_t)info.wifi_sta_disconnected.reason;
ESP_LOGI(TAG, "reason: %s", WiFi.disconnectReasonName(reason));
ESP_LOGI(TAG, "reason: %s, rssi: %d", WiFi.disconnectReasonName(reason), WiFi.RSSI());
ESP_LOGI(TAG, "Local IP: %s", WiFi.localIP().toString().c_str());
}
if(event == ARDUINO_EVENT_WIFI_STA_CONNECTED)
{
ESP_LOGI(TAG, "Connected - RSSI: %d, Channel: %d", WiFi.RSSI(), WiFi.channel());
}
}
@ -60,30 +69,150 @@ void Wifi::task()
{
while(true)
{
// Check if we're in provisioning mode and handle timeout
if(m_wifi_status == WIFI_STATUS::PROVISIONING)
{
if(isProvisioningTimedOut())
{
ESP_LOGW(TAG, "Provisioning timeout reached, stopping AP mode");
stopProvisioning();
m_wifi_status = WIFI_STATUS::NOT_CONNECTED;
m_provision_timeout = true;
}
delay(1000); // Check every second during provisioning
continue;
}
// Check pause flag FIRST (but not during provisioning)
while(m_pause && m_wifi_status != WIFI_STATUS::PROVISIONING)
{
delay(100);
}
if(!WiFi.isConnected())
{
m_wifi_status = WIFI_STATUS::PENDING;
m_wifi_status = startConnecting();
delay(500);
while(m_pause)
{
delay(100);
}
}
delay(15000);
}
}
void Wifi::startProvisioning()
{
m_wifi_status = WIFI_STATUS::PROVISIONING;
m_provision_start_time = millis();
m_provision_timeout = false;
ESP_LOGI(TAG, "Starting provisioning mode for %lu minutes", PROVISION_TIMEOUT_MS / 60000);
}
void Wifi::stopProvisioning()
{
m_wifi_status = WIFI_STATUS::NOT_CONNECTED;
m_provision_start_time = 0;
WiFi.mode(WIFI_STA); // Stop AP mode
}
bool Wifi::isProvisioningTimedOut()
{
if(m_provision_start_time == 0) return false;
return (millis() - m_provision_start_time) > PROVISION_TIMEOUT_MS;
}
void Wifi::resetProvisioningTimer()
{
m_provision_start_time = millis();
m_provision_timeout = false;
}
void Wifi::pause(bool pause)
{
m_pause = pause;
}
bool Wifi::shouldRecalibrateRadio(int failed_ssid_index)
{
// Check cooldown period
unsigned long current_time = millis();
bool cooldown_ok = (m_last_recalibration_time == 0) ||
(current_time - m_last_recalibration_time >= RECALIBRATION_COOLDOWN_MS);
if(!cooldown_ok)
{
unsigned long time_remaining = RECALIBRATION_COOLDOWN_MS - (current_time - m_last_recalibration_time);
ESP_LOGD(TAG, "Recalibration skipped - cooldown period active (%lu ms remaining)", time_remaining);
return false;
}
// Check if we have credentials for this index
if(failed_ssid_index < 0 || failed_ssid_index >= SETTINGS.wifi.num)
{
ESP_LOGD(TAG, "Recalibration skipped - no valid credentials");
return false;
}
// Check if the SSID is visible (network is up)
const char* target_ssid = SETTINGS.wifi.entry[failed_ssid_index].ssid;
if(!isSSIDVisible(target_ssid))
{
ESP_LOGD(TAG, "Recalibration skipped - SSID '%s' not visible", target_ssid);
return false;
}
ESP_LOGW(TAG, "Radio recalibration needed - connection failed but SSID '%s' is visible", target_ssid);
ESP_LOGI(TAG, "Current time: %lu, Last recalibration: %lu", current_time, m_last_recalibration_time);
return true;
}
bool Wifi::isSSIDVisible(const char* ssid)
{
ESP_LOGI(TAG, "Scanning for SSID '%s' visibility...", ssid);
int num_networks = WiFi.scanNetworks(false, false, false, 500);
ESP_LOGI(TAG, "Found %d networks during visibility check", num_networks);
for(int n = 0; n < num_networks; n++)
{
if(strcmp(WiFi.SSID(n).c_str(), ssid) == 0)
{
ESP_LOGI(TAG, "SSID '%s' found with RSSI %d dBm", ssid, WiFi.RSSI(n));
return true;
}
}
ESP_LOGI(TAG, "SSID '%s' not found in scan results", ssid);
return false;
}
void Wifi::performRadioRecalibration()
{
ESP_LOGW(TAG, "Performing WiFi radio recalibration...");
// Disconnect and stop WiFi
WiFi.disconnect();
WiFi.mode(WIFI_OFF);
delay(1000);
// Erase calibration data to force fresh calibration
esp_phy_erase_cal_data_in_nvs();
// Record recalibration time
m_last_recalibration_time = millis();
ESP_LOGW(TAG, "Radio recalibration completed - WiFi will reinitialize on next connection attempt");
// Brief delay to let the radio settle
delay(2000);
}
/// @brief Connect to provisioned wifi
/// @param index index of wifi in settings
/// @return WIFI_STATUS::CONNECTED/NOT_CONENCTED connected
// Modified connectTo method with smart recalibration
Wifi::WIFI_STATUS Wifi::connectTo(int index)
{
IPAddress local_IP(0, 0, 0, 0);
@ -116,9 +245,36 @@ Wifi::WIFI_STATUS Wifi::connectTo(int index)
}
delay(1000);
WiFi.disconnect();
ESP_LOGW(TAG, "Failed to connect, status: %d", status);
// Check if we should recalibrate the radio
if(shouldRecalibrateRadio(index))
{
performRadioRecalibration();
// Try connecting one more time after recalibration
ESP_LOGI(TAG, "Retrying connection after recalibration...");
delay(3000); // Give radio time to reinitialize
WiFi.mode(WIFI_STA);
WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS);
WiFi.begin(SETTINGS.wifi.entry[index].ssid, SETTINGS.wifi.entry[index].pwd);
status = WiFi.waitForConnectResult(10000);
if(status == WL_CONNECTED)
{
ESP_LOGI(TAG, "Connected after recalibration!");
return WIFI_STATUS::CONNECTED;
}
else
{
ESP_LOGW(TAG, "Still failed after recalibration, status: %d", status);
WiFi.disconnect();
}
}
return WIFI_STATUS::NOT_CONNECTED;
}
@ -147,6 +303,7 @@ Wifi::WIFI_STATUS Wifi::waitForConnection()
/// @brief Connect to default fallback wifi network
/// @return WIFI_STATUS::CONNECTED/NOT_CONNECTED
// Also modify startConnecting to handle recalibration for fallback network
Wifi::WIFI_STATUS Wifi::connectToDefault()
{
const char* default_ssid = "CBX_IoT";
@ -184,6 +341,7 @@ Wifi::WIFI_STATUS Wifi::connectToDefault()
delay(1000);
WiFi.disconnect();
ESP_LOGW(TAG, "Failed to connect to default network, status: %d", status);
return WIFI_STATUS::NOT_CONNECTED;
}
@ -275,6 +433,51 @@ Wifi::WIFI_STATUS Wifi::startConnecting()
return WIFI_STATUS::CONNECTED;
}
// Check if we should recalibrate for the fallback network
ESP_LOGW(TAG, "Fallback connection failed, checking if recalibration needed...");
const char* default_ssid = "CBX_IoT";
if(isSSIDVisible(default_ssid))
{
unsigned long current_time = millis();
// Allow recalibration if:
// 1. Never recalibrated before (m_last_recalibration_time == 0)
// 2. Or enough time has passed since last recalibration
bool cooldown_ok = (m_last_recalibration_time == 0) ||
(current_time - m_last_recalibration_time >= RECALIBRATION_COOLDOWN_MS);
if(cooldown_ok)
{
ESP_LOGW(TAG, "Fallback network '%s' visible but connection failed - performing recalibration", default_ssid);
ESP_LOGI(TAG, "Current time: %lu, Last recalibration: %lu", current_time, m_last_recalibration_time);
performRadioRecalibration();
// Retry fallback connection after recalibration
ESP_LOGI(TAG, "Retrying fallback connection after recalibration...");
fallback_status = connectToDefault();
if(fallback_status == WIFI_STATUS::CONNECTED)
{
ESP_LOGI(TAG, "Successfully connected to fallback after recalibration!");
return WIFI_STATUS::CONNECTED;
}
else
{
ESP_LOGW(TAG, "Fallback still failed after recalibration");
}
}
else
{
unsigned long time_remaining = RECALIBRATION_COOLDOWN_MS - (current_time - m_last_recalibration_time);
ESP_LOGD(TAG, "Recalibration skipped - cooldown period active (%lu ms remaining)", time_remaining);
}
}
else
{
ESP_LOGI(TAG, "Fallback network not visible - network may be down");
}
ESP_LOGE(TAG, "Cannot connect to any network including default fallback");
return WIFI_STATUS::NOT_PROVISIONED;
}

View File

@ -18,7 +18,8 @@ public:
NOT_CONNECTED,
PENDING,
NOT_PROVISIONED,
CONNECTED
CONNECTED,
PROVISIONING
} WIFI_STATUS;
protected:
@ -40,9 +41,16 @@ protected:
public:
Wifi();
void startProvisioning();
void stopProvisioning();
bool isProvisioningTimedOut();
void resetProvisioningTimer();
protected:
void wifi_event(arduino_event_id_t event, arduino_event_info_t info);
unsigned long m_provision_start_time = 0;
static const unsigned long PROVISION_TIMEOUT_MS = 600000; // 10 minutes
bool m_provision_timeout = false;
public:
int start();
@ -53,6 +61,12 @@ public:
private:
WIFI_STATUS connectToDefault();
unsigned long m_last_recalibration_time = 0;
static const unsigned long RECALIBRATION_COOLDOWN_MS = 3600000; // 1 hour
bool shouldRecalibrateRadio(int failed_ssid_index);
void performRadioRecalibration();
bool isSSIDVisible(const char* ssid);
};
#endif /* __WIFI_H__ */