SensESP 2.7.2
Universal Signal K sensor toolkit ESP32
Loading...
Searching...
No Matches
networking.cpp
Go to the documentation of this file.
1#include "networking.h"
2
3#include "sensesp.h"
5#include "sensesp_app.h"
6
7namespace sensesp {
8
9// Wifi config portal timeout (seconds). The smaller the value, the faster
10// the device will attempt to reconnect. If set too small, it might
11// become impossible to actually configure the Wifi settings in the captive
12// portal.
13#ifndef WIFI_CONFIG_PORTAL_TIMEOUT
14#define WIFI_CONFIG_PORTAL_TIMEOUT 180
15#endif
16
17// Network configuration logic:
18// 1. Use hard-coded hostname and WiFi credentials by default
19// 2. If the hostname or credentials have been changed in WiFiManager or
20// the web UI, use the updated values.
21// 3. If the hard-coded hostname is changed, use that instead of the saved one.
22// (But keep using the saved WiFi credentials!)
23
26 : Configurable{config_path, "Basic WiFi Setup", 100},
27 wifi_manager_password_{wifi_manager_password},
28 Startable(80),
29 Resettable(0) {
30
31 // Get the WiFi state producer singleton and make it update this object output
32 wifi_state_producer = WiFiStateProducer::get_singleton();
33 wifi_state_producer->connect_to(new LambdaConsumer<WiFiState>(
34 [this](WiFiState state) { this->emit(state); }));
35
36 preset_ssid = ssid;
37 preset_password = password;
38 preset_hostname = hostname;
39 default_hostname = hostname;
40
42
43 if (default_hostname != preset_hostname) {
44 // if the preset hostname has changed, use it instead of the loaded one
45 SensESPBaseApp::get()->get_hostname_observable()->set(preset_hostname);
46 default_hostname = preset_hostname;
47 }
48
49 if (ap_ssid == "" && preset_ssid != "") {
50 // there is no saved config and preset config is available
51 ap_ssid = preset_ssid;
52 }
53
54 if (ap_password == "" && preset_password != "") {
55 // there is no saved config and preset config is available
56 ap_password = preset_password;
57 }
58
59 server = new AsyncWebServer(80);
60 dns = new DNSServer();
61}
62
64 debugD("Enabling Networking object");
65
66 // If we have preset or saved WiFi config, always use it. Otherwise,
67 // start WiFiManager. WiFiManager always starts the configuration portal
68 // instead of trying to connect.
69
70 if (ap_ssid != "" && ap_password != "") {
71 debugI("Using SSID %s", ap_ssid.c_str());
73 } else if (ap_ssid == "" && WiFi.status() != WL_CONNECTED &&
74 wifi_manager_enabled_) {
75 debugI("Starting WiFiManager");
77 }
78 // otherwise, fall through and WiFi will remain disconnected
79}
80
82 debugD("Activating WiFiManager");
83 if (WiFi.status() != WL_CONNECTED) {
85 }
86}
87
93 WiFi.setHostname(hostname.c_str());
94
95 if (ap_mode_ == false) {
96 // set up WiFi in regular STA (client) mode
97 auto reconnect_cb = [this]() {
98 if (WiFi.status() != WL_CONNECTED) {
99 debugI("Connecting to wifi SSID %s.", ap_ssid.c_str());
100 WiFi.begin(ap_ssid.c_str(), ap_password.c_str());
101 }
102 };
103
104 // Perform an initial connection without a delay.
105 reconnect_cb();
106
107 // Launch a separate onRepeat reaction to (re-)establish WiFi connection.
108 // Connecting is attempted only every 20 s to allow the previous connection
109 // attempt to complete even if the network is slow.
110 ReactESP::app->onRepeat(20000, reconnect_cb);
111 } else {
112 // Set up WiFi in AP mode. In this case, we don't need a reconnect loop.
113 debugI("Setting up a WiFi access point...");
114 WiFi.softAP(ap_ssid.c_str(), ap_password.c_str());
115 }
116}
117
126 wifi_manager = new AsyncWiFiManager(server, dns);
127
129
130 // set config save notify callback
131 wifi_manager->setBreakAfterConfig(true);
132
133 wifi_manager->setConfigPortalTimeout(WIFI_CONFIG_PORTAL_TIMEOUT);
134
135#ifdef SERIAL_DEBUG_DISABLED
136 wifi_manager->setDebugOutput(false);
137#endif
138 AsyncWiFiManagerParameter custom_hostname("hostname", "Device hostname",
139 hostname.c_str(), 64);
140 wifi_manager->addParameter(&custom_hostname);
141
143 "ap_ssid", "Custom Access Point SSID", ap_ssid.c_str(), 33);
144 wifi_manager->addParameter(&custom_ap_ssid);
145
147 "ap_password", "Custom Access Point Password", ap_password.c_str(), 64);
148 wifi_manager->addParameter(&custom_ap_password);
149
150 wifi_manager->setTryConnectDuringConfigPortal(false);
151
152 // Create a unique SSID for configuring each SensESP Device
154 if (wifi_manager_ap_ssid_ != "") {
155 config_ssid = wifi_manager_ap_ssid_;
156 } else {
157 config_ssid = "Configure " + hostname;
158 }
159 const char* pconfig_ssid = config_ssid.c_str();
160
161 // this is the only WiFi state we actively still emit
163
164 WiFi.setHostname(SensESPBaseApp::get_hostname().c_str());
165
166 wifi_manager->startConfigPortal(pconfig_ssid, wifi_manager_password_);
167
170
171 bool connected = false;
172
174 // AP mode is desired
176 ap_password = configured_custom_ap_password;
177 ap_mode_ = true;
178
179 // always assume we can launch a soft AP
180 connected = true;
181 } else {
182 // WiFiManager attempts to connect to the new SSID, but that doesn't seem to
183 // work reliably. Instead, we'll just attempt to connect manually.
184
185 this->ap_ssid = wifi_manager->getConfiguredSTASSID();
186 this->ap_password = wifi_manager->getConfiguredSTAPassword();
187
188 // attempt to connect with the new SSID and password
189 if (this->ap_ssid != "" && this->ap_password != "") {
190 debugD("Attempting to connect to acquired SSID %s and password",
191 this->ap_ssid.c_str());
192 WiFi.begin(this->ap_ssid.c_str(), this->ap_password.c_str());
193 for (int i = 0; i < 20; i++) {
194 if (WiFi.status() == WL_CONNECTED) {
195 connected = true;
196 break;
197 }
198 delay(1000);
199 }
200 }
201 }
202
203 // Only save the new configuration if we were able to connect to the new SSID.
204
205 if (connected) {
207 debugI("Got new custom hostname: %s", new_hostname.c_str());
208 SensESPBaseApp::get()->get_hostname_observable()->set(new_hostname);
209 debugI("Got new SSID and password: %s", ap_ssid.c_str());
211 }
212 debugW("Restarting...");
213 ESP.restart();
214}
215
217 static const char kSchema[] = R"###({
218 "type": "object",
219 "properties": {
220 "hostname": { "title": "Device hostname", "type": "string" },
221 "ssid": { "title": "WiFi SSID", "type": "string" },
222 "password": { "title": "WiFi password", "type": "string", "format": "password" },
223 "ap_mode": { "type": "string", "format": "radio", "title": "WiFi mode", "enum": ["Client", "Access Point"] }
224 }
225 })###";
226
227 return String(kSchema);
228}
229
230// FIXME: hostname should be saved in SensESPApp
231
234 root["hostname"] = hostname;
235 root["default_hostname"] = default_hostname;
236 root["ssid"] = ap_ssid;
237 root["password"] = ap_password;
238 root["ap_mode"] = ap_mode_ ? "Access Point" : "Client";
239}
240
242 if (!config.containsKey("hostname")) {
243 return false;
244 }
245
246 SensESPBaseApp::get()->get_hostname_observable()->set(
247 config["hostname"].as<String>());
248
249 if (config.containsKey("default_hostname")) {
250 default_hostname = config["default_hostname"].as<String>();
251 }
252 ap_ssid = config["ssid"].as<String>();
253 ap_password = config["password"].as<String>();
254
255 if (config.containsKey("ap_mode")) {
256 if (config["ap_mode"].as<String>() == "Access Point" ||
257 config["ap_mode"].as<String>() == "Hotspot") {
258 ap_mode_ = true;
259 } else {
260 ap_mode_ = false;
261 }
262 }
263
264 return true;
265}
266
268 debugI("Resetting WiFi SSID settings");
269
270 ap_ssid = preset_ssid;
271 ap_password = preset_password;
272
274 WiFi.disconnect(true);
275 // On ESP32, disconnect does not erase previous credentials. Let's connect
276 // to a bogus network instead
277 WiFi.begin("0", "0");
278}
279
281
283 if (instance_ == nullptr) {
285 }
286 return instance_;
287}
288
289} // namespace sensesp
An object that is capable of having configuration data that can be set remotely using a RESTful API,...
virtual void save_configuration()
virtual void load_configuration()
Construct a new transform based on a single function.
virtual void reset() override
virtual void get_configuration(JsonObject &doc) override final
void setup_wifi_manager()
Start WiFi using WiFi Manager.
virtual String get_config_schema() override
void setup_saved_ssid()
Start WiFi using preset SSID and password.
virtual bool set_configuration(const JsonObject &config) override final
virtual void start() override
Networking(String config_path, String ssid, String password, String hostname, const char *wifi_manager_password)
Automatic calling of the reset() method when the device needs to be reset.
Definition resettable.h:20
static SensESPBaseApp * get()
Get the singleton instance of the SensESPBaseApp.
static String get_hostname()
Get the current hostname.
Automatic calling of the start() method at startup.
Definition startable.h:20
void connect_to(ValueConsumer< T > *consumer, uint8_t input_channel=0)
void emit(WiFiState new_value)
Provide information about the current WiFi state.
Definition networking.h:28
static WiFiStateProducer * instance_
Definition networking.h:96
static WiFiStateProducer * get_singleton()
Get the singleton instance of the WiFiStateProducer.
#define debugI(fmt,...)
Definition local_debug.h:48
#define debugD(fmt,...)
Definition local_debug.h:47
#define debugW(fmt,...)
Definition local_debug.h:49
#define WIFI_CONFIG_PORTAL_TIMEOUT