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

WiFi State output split away from Networking #635

Merged
merged 4 commits into from
Jan 26, 2023
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
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ jobs:
- examples/analog_input.cpp
- examples/hysteresis.cpp
- examples/lambda_transform.cpp
- examples/manual_networking.cpp
- examples/minimal_app.cpp
- examples/relay_control.cpp
- examples/rpm_counter.cpp
Expand Down
68 changes: 68 additions & 0 deletions examples/manual_networking.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#include <math.h>

#include "sensesp/net/http_server.h"
#include "sensesp/net/networking.h"
#include "sensesp/sensors/digital_input.h"
#include "sensesp/system/lambda_consumer.h"
#include "sensesp/transforms/linear.h"
#include "sensesp/transforms/typecast.h"
#include "sensesp_minimal_app_builder.h"
#include "sensesp/signalk/signalk_output.h"

using namespace sensesp;

const unsigned int read_delay = 500;

const uint8_t input_pin1 = 0;

// This is a sample program to demonstrate how to instantiate a
// SensESPMinimalApp application and setup networking manually.
//
// The program reacts to changes on GPIO pin 0 and prints the value to the
// serial console.

ReactESP app;

void setup() {
SetupSerialDebug(115200);

SensESPMinimalAppBuilder builder;
auto sensesp_app = builder.set_hostname("counter-test")->get_app();

// manually create Networking and HTTPServer objects to enable
// the HTTP configuration interface

WiFi.mode(WIFI_STA);
WiFi.begin("Hat Labs Sensors", "kanneluuri2406");

Serial.println("");

// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}

Serial.println("");

debugD("Connected to WiFi. IP address: %s", WiFi.localIP().toString().c_str());

WiFi.setHostname(SensESPBaseApp::get_hostname().c_str());

auto* http_server = new HTTPServer();

auto* digin = new DigitalInputChange(input_pin1, INPUT, CHANGE);

digin->connect_to(new LambdaConsumer<bool>([](bool input) {
Serial.printf("millis: %d\n", millis());
Serial.printf("Digin: %d\n", input);
}));

digin->connect_to(new SKOutputBool("electrical.switches.0.state", "/digin/state"));

sensesp_app->start();
}

// The loop function is called in an endless loop during program execution.
// It simply calls `app.tick()` which will then execute all reactions as needed.
void loop() { app.tick(); }
74 changes: 15 additions & 59 deletions src/sensesp/net/networking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ Networking::Networking(String config_path, String ssid, String password,
wifi_manager_password_{wifi_manager_password},
Startable(80),
Resettable(0) {
this->output = WiFiState::kWifiNoAP;

// Get the WiFi state producer singleton and make it update this object output
wifi_state_producer = WiFiStateProducer::get_singleton();
wifi_state_producer->connect_to(new LambdaConsumer<WiFiState>(
[this](WiFiState state) { this->emit(state); }));

preset_ssid = ssid;
preset_password = password;
Expand Down Expand Up @@ -81,35 +85,10 @@ void Networking::activate_wifi_manager() {
}
}

void Networking::setup_wifi_callbacks() {


WiFi.onEvent([this](WiFiEvent_t event,
WiFiEventInfo_t info) { this->wifi_station_connected(); },
WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP);
WiFi.onEvent([this](WiFiEvent_t event,
WiFiEventInfo_t info) { this->wifi_ap_enabled(); },
WiFiEvent_t::ARDUINO_EVENT_WIFI_AP_START);
WiFi.onEvent(
[this](WiFiEvent_t event, WiFiEventInfo_t info) {
this->wifi_disconnected();
},
WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED);
WiFi.onEvent(
[this](WiFiEvent_t event, WiFiEventInfo_t info) {
this->wifi_disconnected();
},
WiFiEvent_t::ARDUINO_EVENT_WIFI_AP_STOP);

}

/**
* @brief Start WiFi using preset SSID and password.
*/
void Networking::setup_saved_ssid() {
this->emit(WiFiState::kWifiDisconnected);
setup_wifi_callbacks();

String hostname = SensESPBaseApp::get_hostname();
WiFi.setHostname(hostname.c_str());

Expand All @@ -136,37 +115,6 @@ void Networking::setup_saved_ssid() {
}
}

/**
* This method gets called when WiFi is connected to the AP and has
* received an IP address.
*/
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());
debugI("Default route: %s", WiFi.gatewayIP().toString().c_str());
debugI("DNS server: %s", WiFi.dnsIP().toString().c_str());
this->emit(WiFiState::kWifiConnectedToAP);
}

void Networking::wifi_ap_enabled() {
debugI("WiFi Access Point enabled, SSID: %s", WiFi.softAPSSID().c_str());
debugI("IP address of Device: %s", WiFi.softAPIP().toString().c_str());

// Setting the AP mode happens immediately,
// so this callback is likely called already before all startables have been
// initiated. Delay the WiFi state update until the start of the event loop.
ReactESP::app->onDelay(0, [this]() {this->emit(WiFiState::kWifiAPModeActivated);});
}

/**
* This method gets called when WiFi is disconnected from the AP.
*/
void Networking::wifi_disconnected() {
debugI("Disconnected from wifi.");
this->emit(WiFiState::kWifiDisconnected);
}

/**
* @brief Start WiFi using WiFi Manager.
*
Expand All @@ -179,8 +127,6 @@ void Networking::setup_wifi_manager() {

String hostname = SensESPBaseApp::get_hostname();

setup_wifi_callbacks();

// set config save notify callback
wifi_manager->setBreakAfterConfig(true);

Expand Down Expand Up @@ -212,6 +158,7 @@ void Networking::setup_wifi_manager() {
}
const char* pconfig_ssid = config_ssid.c_str();

// this is the only WiFi state we actively still emit
this->emit(WiFiState::kWifiManagerActivated);

WiFi.setHostname(SensESPBaseApp::get_hostname().c_str());
Expand Down Expand Up @@ -330,4 +277,13 @@ void Networking::reset() {
WiFi.begin("0", "0");
}

WiFiStateProducer* WiFiStateProducer::instance_ = nullptr;

WiFiStateProducer* WiFiStateProducer::get_singleton() {
if (instance_ == nullptr) {
instance_ = new WiFiStateProducer();
}
return instance_;
}

} // namespace sensesp
83 changes: 82 additions & 1 deletion src/sensesp/net/networking.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,86 @@

namespace sensesp {

/**
* @brief Provide information about the current WiFi state.
*
* WiFiStateProducer reads the current network state using
* Arduino Core callbacks. It is a replacement for the Networking class
* ValueProducer output and effectively decouples the Networkig class
* from the rest of the system. This allows for replacing the Networking
* class with a different implementation.
*/
class WiFiStateProducer : public ValueProducer<WiFiState>, public Startable {
public:
/**
* Singletons should not be cloneable
*/
WiFiStateProducer(WiFiStateProducer& other) = delete;

/**
* Singletons should not be assignable
*/
void operator=(const WiFiStateProducer&) = delete;

/**
* @brief Get the singleton instance of the WiFiStateProducer
*/
static WiFiStateProducer* get_singleton();

virtual void start() override {
setup_wifi_callbacks();
// Emit the current state immediately
this->emit(this->output);
}

protected:
WiFiStateProducer() : Startable(81) { this->output = WiFiState::kWifiNoAP; }

void setup_wifi_callbacks() {
WiFi.onEvent(
[this](WiFiEvent_t event, WiFiEventInfo_t info) {
this->wifi_station_connected();
},
WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP);
WiFi.onEvent([this](WiFiEvent_t event,
WiFiEventInfo_t info) { this->wifi_ap_enabled(); },
WiFiEvent_t::ARDUINO_EVENT_WIFI_AP_START);
WiFi.onEvent([this](WiFiEvent_t event,
WiFiEventInfo_t info) { this->wifi_disconnected(); },
WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED);
WiFi.onEvent([this](WiFiEvent_t event,
WiFiEventInfo_t info) { this->wifi_disconnected(); },
WiFiEvent_t::ARDUINO_EVENT_WIFI_AP_STOP);
}

void 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());
debugI("Default route: %s", WiFi.gatewayIP().toString().c_str());
debugI("DNS server: %s", WiFi.dnsIP().toString().c_str());
this->emit(WiFiState::kWifiConnectedToAP);
}

void wifi_ap_enabled() {
debugI("WiFi Access Point enabled, SSID: %s", WiFi.softAPSSID().c_str());
debugI("IP address of Device: %s", WiFi.softAPIP().toString().c_str());

// Setting the AP mode happens immediately,
// so this callback is likely called already before all startables have been
// initiated. Delay the WiFi state update until the start of the event loop.
ReactESP::app->onDelay(
0, [this]() { this->emit(WiFiState::kWifiAPModeActivated); });
}

void wifi_disconnected() {
debugI("Disconnected from wifi.");
this->emit(WiFiState::kWifiDisconnected);
}

static WiFiStateProducer* instance_;
};

/**
* @brief Manages the ESP's connection to the Wifi network.
*/
Expand Down Expand Up @@ -43,7 +123,6 @@ class Networking : public Configurable,

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

// callbacks
Expand Down Expand Up @@ -81,6 +160,8 @@ class Networking : public Configurable,
// in the hardcoded value
String default_hostname = "";

WiFiStateProducer* wifi_state_producer;

const char* wifi_manager_password_;
};

Expand Down
2 changes: 1 addition & 1 deletion src/sensesp_app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ void SensESPApp::setup() {
sk_server_address_, sk_server_port_);

// connect the system status controller
this->networking_->connect_to(&system_status_controller_);
WiFiStateProducer::get_singleton()->connect_to(&system_status_controller_);
this->ws_client_->connect_to(&system_status_controller_);

// create the MDNS discovery object
Expand Down