Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non blocking wifi #367

Merged
merged 8 commits into from
Feb 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions src/controllers/system_status_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ void SystemStatusController::set_input(WifiState new_value,
// this would be a simple array dereferencing
switch (new_value) {
case WifiState::kWifiNoAP:
this->emit(SystemStatus::kWifiNoAP);
this->update_state(SystemStatus::kWifiNoAP);
break;
case WifiState::kWifiDisconnected:
this->emit(SystemStatus::kWifiDisconnected);
this->update_state(SystemStatus::kWifiDisconnected);
break;
case WifiState::kWifiConnectedToAP:
this->emit(SystemStatus::kWSDisconnected);
this->update_state(SystemStatus::kWSDisconnected);
break;
case WifiState::kWifiManagerActivated:
this->emit(SystemStatus::kWifiManagerActivated);
this->update_state(SystemStatus::kWifiManagerActivated);
break;
}
}
Expand All @@ -24,16 +24,21 @@ void SystemStatusController::set_input(WSConnectionState new_value,
uint8_t input_channel) {
switch (new_value) {
case WSConnectionState::kWSDisconnected:
this->emit(SystemStatus::kWSDisconnected);
if (current_state_ != SystemStatus::kWifiDisconnected &&
current_state_ != SystemStatus::kWifiNoAP &&
current_state_ != SystemStatus::kWifiManagerActivated) {
// Wifi disconnection states override the higher level protocol state
this->update_state(SystemStatus::kWSDisconnected);
}
break;
case WSConnectionState::kWSConnecting:
this->emit(SystemStatus::kWSConnecting);
this->update_state(SystemStatus::kWSConnecting);
break;
case WSConnectionState::kWSAuthorizing:
this->emit(SystemStatus::kWSAuthorizing);
this->update_state(SystemStatus::kWSAuthorizing);
break;
case WSConnectionState::kWSConnected:
this->emit(SystemStatus::kWSConnected);
this->update_state(SystemStatus::kWSConnected);
break;
}
}
7 changes: 7 additions & 0 deletions src/controllers/system_status_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ class SystemStatusController : public ValueConsumer<WifiState>,
/// (WSClient object state updates)
virtual void set_input(WSConnectionState new_value,
uint8_t input_channel = 0) override;
protected:
void update_state(const SystemStatus new_state) {
current_state_ = new_state;
this->emit(new_state);
}
private:
SystemStatus current_state_ = SystemStatus::kWifiNoAP;
};

#endif
78 changes: 35 additions & 43 deletions src/net/networking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,58 +38,60 @@ Networking::Networking(String config_path, String ssid, String password,
wifi_manager = new AsyncWiFiManager(server, dns);
}

void Networking::check_connection() {
if (WiFi.status() != WL_CONNECTED) {
// if connection is lost, simply restart
debugD("Wifi disconnected: restarting...");

// Might be futile to notify about a disconnection if it results in
// a reboot anyway
this->emit(WifiState::kWifiDisconnected);

ESP.restart();
}
}

void Networking::setup() {
if (ap_ssid != "" && ap_password != "") {
setup_saved_ssid();
}
if (ap_ssid == "" && WiFi.status() != WL_CONNECTED) {
setup_wifi_manager();
}
app.onRepeat(1000, std::bind(&Networking::check_connection, this));
}

void Networking::setup_wifi_callbacks() {
#if defined(ESP8266)
got_ip_event_handler_ =
WiFi.onStationModeGotIP([this](const WiFiEventStationModeGotIP& event) {
this->wifi_station_connected();
});
wifi_disconnected_event_handler_ = WiFi.onStationModeDisconnected(
[this](const WiFiEventStationModeDisconnected& event) {
this->wifi_station_disconnected();
});
#elif defined(ESP32)
WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) {
this->wifi_station_connected();
}, WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) {
this->wifi_station_disconnected();
}, WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);
#endif
}

void Networking::setup_saved_ssid() {
WiFi.begin(ap_ssid.c_str(), ap_password.c_str());
this->emit(WifiState::kWifiDisconnected);

uint32_t timer_start = millis();
setup_wifi_callbacks();
WiFi.begin(ap_ssid.c_str(), ap_password.c_str());

debugI("Connecting to wifi %s.", ap_ssid.c_str());
int printCounter = 0;
while (WiFi.status() != WL_CONNECTED &&
(millis() - timer_start) < 3 * 60 * 1000) {
delay(500);
if (printCounter % 4) {
debugI("Wifi status=%d, time=%d ms", WiFi.status(), 500 * printCounter);
}
printCounter++;
}
}

if (WiFi.status() == WL_CONNECTED) {
void Networking::wifi_station_connected() {
debugI("Connected to wifi, SSID: %s (signal: %d)", WiFi.SSID().c_str(),
WiFi.RSSI());
debugI("IP address of Device: %s", WiFi.localIP().toString().c_str());
this->emit(WifiState::kWifiConnectedToAP);
WiFi.mode(WIFI_STA);
}
}

void Networking::wifi_station_disconnected() {
debugI("Disconnected from wifi.");
this->emit(WifiState::kWifiDisconnected);
}

void Networking::setup_wifi_manager() {
should_save_config = false;

setup_wifi_callbacks();

// set config save notify callback
wifi_manager->setSaveConfigCallback(save_config_callback);

Expand Down Expand Up @@ -162,21 +164,13 @@ String Networking::get_config_schema() {
// Config UI. If preset_hostname is not "SensESP", then it was set in
// main.cpp, so it should be read-only.
bool hostname_preset = preset_hostname != "SensESP";
bool wifi_preset = preset_ssid != "";
return String(FPSTR(SCHEMA_PREFIX)) +
get_property_row("hostname", "ESP device hostname", hostname_preset) +
"," +
get_property_row("ap_ssid", "Wifi Access Point SSID", wifi_preset) +
"," +
get_property_row("ap_password", "Wifi Access Point Password",
wifi_preset) +
"}}";
}

void Networking::get_configuration(JsonObject& root) {
root["hostname"] = this->hostname->get();
root["ap_ssid"] = this->ap_ssid;
root["ap_password"] = this->ap_password;
}

bool Networking::set_configuration(const JsonObject& config) {
Expand All @@ -188,11 +182,6 @@ bool Networking::set_configuration(const JsonObject& config) {
this->hostname->set(config["hostname"].as<String>());
}

if (preset_ssid == "") {
debugW("Using saved SSID and password");
this->ap_ssid = config["ap_ssid"].as<String>();
this->ap_password = config["ap_password"].as<String>();
}
return true;
}

Expand All @@ -201,5 +190,8 @@ void Networking::reset_settings() {
ap_password = preset_password;

save_configuration();
wifi_manager->resetSettings();
WiFi.disconnect(true);
// On ESP32, disconnect does not erase previous credentials. Let's connect
// to a bogus network instead
WiFi.begin("0", "0");
}
20 changes: 18 additions & 2 deletions src/net/networking.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@

#include "Arduino.h"

#ifdef ESP8266
#include <ESP8266WiFi.h>
#endif

// Local WebServer used to serve the configuration portal
#include <ESPAsyncWebServer.h>
#include <ESPAsyncWiFiManager.h>
Expand All @@ -19,7 +23,7 @@ enum class WifiState {
};

/**
* @brief Manages the ESP's connection to the Wifi network.
* @brief Manages the ESP's connection to the Wifi network.
*/
class Networking : public Configurable, public ValueProducer<WifiState> {
public:
Expand All @@ -33,16 +37,28 @@ class Networking : public Configurable, public ValueProducer<WifiState> {
void reset_settings();

protected:
void check_connection();
void setup_saved_ssid();
void setup_wifi_callbacks();
void setup_wifi_manager();

// callbacks

void wifi_station_connected();
void wifi_station_disconnected();

private:
AsyncWebServer* server;
// FIXME: DNSServer and AsyncWiFiManager could be instantiated in
// respective methods to save some runtime memory
DNSServer* dns;
AsyncWiFiManager* wifi_manager;

#ifdef ESP8266
// event handlers must be saved to keep the handlers activated
WiFiEventHandler got_ip_event_handler_;
WiFiEventHandler wifi_disconnected_event_handler_;
#endif

ObservableValue<String>* hostname;
String ap_ssid = "";
String ap_password = "";
Expand Down
18 changes: 17 additions & 1 deletion src/sensesp_app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "signalk/signalk_output.h"
#include "system/spiffs_storage.h"
#include "system/system_status_led.h"
#include "transforms/debounce.h"
#include "transforms/difference.h"
#include "transforms/frequency.h"
#include "transforms/linear.h"
Expand Down Expand Up @@ -99,6 +100,21 @@ void SensESPApp::setup() {
this->networking_->connect_to(&system_status_controller_);
this->ws_client_->connect_to(&system_status_controller_);

// create the wifi disconnect watchdog

this->system_status_controller_
.connect_to(new DebounceTemplate<SystemStatus>(
3*60*1000, // 180 s = 180000 ms = 3 minutes
"/system/wifi_reboot_watchdog"))
->connect_to(new LambdaConsumer<SystemStatus>([](SystemStatus input) {
debugD("Got system status: %d", (int)input);
if (input == SystemStatus::kWifiDisconnected ||
input == SystemStatus::kWifiNoAP) {
debugW("Unable to connect to wifi for too long; restarting.");
app.onDelay(1000, []() { ESP.restart(); });
}
}));

// create a system status led and connect it

if (system_status_led_ == NULL) {
Expand Down Expand Up @@ -181,7 +197,7 @@ void SensESPApp::reset() {
debugW("Resetting the device configuration.");
networking_->reset_settings();
SPIFFS.format();
app.onDelay(1000, []() { ESP.restart(); });
app.onDelay(1000, []() { ESP.restart(); delay(1000); });
}

String SensESPApp::get_hostname() { return networking_->get_hostname()->get(); }
Expand Down