mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-27 19:57:38 +00:00
Merge pull request #5564 from TD-er/bugfix/wifi_setup_crash
[WiFi] Fix stack overflow crash when no credentials set
This commit is contained in:
@@ -244,6 +244,8 @@
|
||||
"
|
||||
``WiFi#Connected``
|
||||
Triggered when the ESP has connected to Wi-Fi.
|
||||
|
||||
N.B. Similar events are also given for other network interfaces like ``ap``, ``eth`` or ``ppp``.
|
||||
","
|
||||
|
||||
.. code-block:: none
|
||||
@@ -256,6 +258,8 @@
|
||||
"
|
||||
``WiFi#Disconnected``
|
||||
Triggered when the ESP has disconnected from Wi-Fi.
|
||||
|
||||
N.B. Similar events are also given for other network interfaces like ``ap``, ``eth`` or ``ppp``.
|
||||
","
|
||||
|
||||
.. code-block:: none
|
||||
@@ -271,6 +275,34 @@
|
||||
SendToHTTP,url.com,80,/report.php?hash=123abc456&t=[temp2#out]
|
||||
Endon
|
||||
|
||||
"
|
||||
"
|
||||
``WiFi#Enabled``
|
||||
Triggered when the network interface ``WiFi`` was enabled.
|
||||
|
||||
N.B. Similar events are also given for other network interfaces like ``ap``, ``eth`` or ``ppp``.
|
||||
","
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
on WiFi#Enabled do
|
||||
GPIO,2,1 // Turn on WiFi led
|
||||
endon
|
||||
|
||||
"
|
||||
"
|
||||
``WiFi#Disabled``
|
||||
Triggered when the network interface ``WiFi`` was disabled.
|
||||
|
||||
N.B. Similar events are also given for other network interfaces like ``ap``, ``eth`` or ``ppp``.
|
||||
","
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
On WiFi#Disabled Do
|
||||
GPIO,2,0 // Turn off WiFi led
|
||||
Endon
|
||||
|
||||
"
|
||||
"
|
||||
``WiFi#ChangedAccesspoint``
|
||||
@@ -298,7 +330,7 @@
|
||||
|
||||
"
|
||||
"
|
||||
``WiFi#APmodeEnabled``
|
||||
``ap#Enabled``
|
||||
Triggered when the ESP has set the AP mode (access point) active.
|
||||
This may happen when no valid WiFi settings are found or the ESP cannot connect to the set AP, but it can also be enabled via some command.
|
||||
N.B. Sending a publish command may not be very useful on this event, since this will mainly happen when there is no WiFi connection.
|
||||
@@ -306,20 +338,20 @@
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
on WiFi#APmodeEnabled do
|
||||
on ap#Enabled do
|
||||
... // Some command
|
||||
endon
|
||||
|
||||
"
|
||||
"
|
||||
``WiFi#APmodeDisabled``
|
||||
``ap#Disabled``
|
||||
Triggered when the ESP has disabled the AP mode (access point).
|
||||
This can happen some time (default 60 seconds) after a WiFi connection has been made. Or disabled using some command.
|
||||
","
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
on WiFi#APmodeDisabled do
|
||||
on ap#Disabled do
|
||||
Publish,%sysname%/status,AP disabled
|
||||
endon
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 30 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 31 KiB |
+32
-11
@@ -4,17 +4,38 @@ WiFi
|
||||
WiFi State Machine
|
||||
==================
|
||||
|
||||
ESPEasy WiFi modes
|
||||
------------------
|
||||
|
||||
|
||||
.. figure:: StateDiagramESPEasyWiFi_mode_e.svg
|
||||
:width: 70 %
|
||||
:align: center
|
||||
|
||||
State Diagram ESPEasy WiFi_mode_e
|
||||
|
||||
|
||||
|
||||
|
||||
WiFi STA states
|
||||
---------------
|
||||
|
||||
#. STA off -> ESPEASY_WIFI_DISCONNECTED
|
||||
#. STA connecting
|
||||
#. STA connected -> ESPEASY_WIFI_CONNECTED
|
||||
#. STA got IP -> ESPEASY_WIFI_GOT_IP
|
||||
#. STA connected && got IP -> ESPEASY_WIFI_SERVICES_INITIALIZED
|
||||
#. ``Disabled`` WiFi STA radio is off. When timeout is set to 0, no new attempt should be made (e.g. low power mode or Ethernet active) Will only change to STA_Initializing when WiFi is allowed/required to start
|
||||
#. ``TimeOut/Error`` Error state, some action failed
|
||||
#. ``STA Initializing`` State from where we decide to start scanning or connecting
|
||||
#. ``STA Scanning`` STA mode + scanning
|
||||
#. ``STA+AP Scanning`` STA+AP mode + scanning, (scanning per channel) needs some careful handling to prevent disconnecting the connected stations.
|
||||
#. ``STA Connecting`` Connecting to an AP
|
||||
#. ``STA Reconnecting`` Reconnecting to an AP. May need to handle some specific disconnect reasons differently from connecting for the first time.
|
||||
#. ``STA Connected`` Connected to an AP.
|
||||
#. ``STA Connected Stable`` Connected to an AP for at least 5 minutes
|
||||
|
||||
.. figure:: StateDiagramESPEasyWiFi_STA_State_e.svg
|
||||
:width: 70 %
|
||||
:align: center
|
||||
|
||||
State Diagram ESPEasy WiFi_STA_State_e
|
||||
|
||||
N.B. the states are flags, meaning both "connected" and "got IP" must be set
|
||||
to be considered ESPEASY_WIFI_SERVICES_INITIALIZED
|
||||
|
||||
The flag wifiConnectAttemptNeeded indicates whether a new connect attempt is needed.
|
||||
This is set to true when:
|
||||
@@ -28,9 +49,9 @@ This is set to true when:
|
||||
WiFi AP mode states
|
||||
-------------------
|
||||
|
||||
#. AP on -> reset AP disable timer
|
||||
#. AP client connect/disconnect -> reset AP disable timer
|
||||
#. AP off -> AP disable timer = 0;
|
||||
#. ``AP on`` reset AP disable timer
|
||||
#. ``AP client connect/disconnect`` reset AP disable timer
|
||||
#. ``AP off`` AP disable timer = 0;
|
||||
|
||||
AP mode will be disabled when both apply:
|
||||
|
||||
@@ -141,7 +162,7 @@ Case 1, your PC does not know the MAC address of the ESP:
|
||||
|
||||
* Your PC tries to find the MAC address belonging to an IP address using an ARP packet (question is like: "Who has 1.2.3.4?")
|
||||
* ARP packets gets broadcasted to the entire network
|
||||
* If the ESP misses such an ARP request, you cannot route the IP packet to your ESP. -> fail
|
||||
* If the ESP misses such an ARP request, you cannot route the IP packet to your ESP. -> fail
|
||||
|
||||
Case 2, your PR does know the MAC address of the ESP, but a switch or access point does not:
|
||||
|
||||
|
||||
@@ -625,7 +625,7 @@ int HTTPClientLight::sendRequest(const char * type, uint8_t * payload, size_t si
|
||||
|
||||
// connect to server
|
||||
if(!connect()) {
|
||||
if (_secure) {
|
||||
if (_secure && _transportTraits) {
|
||||
int32_t ret = _transportTraits->getLastError(*_client);
|
||||
if (ret) {
|
||||
return returnError(-1000 - ret); // BearSSL error 46 transformed to -1046
|
||||
|
||||
@@ -244,6 +244,8 @@ build_flags = -DESP32_STAGE
|
||||
-DNO_GLOBAL_WIFIPROV
|
||||
; -Wsuggest-override
|
||||
-mstrict-align
|
||||
-DUSE_WEBCLIENT_HTTPS
|
||||
-DFEATURE_TLS=1
|
||||
|
||||
lib_ignore = BLE
|
||||
Zigbee
|
||||
|
||||
@@ -246,12 +246,16 @@ void NWPluginData_static_runtime::processEvents()
|
||||
|
||||
if (connected_changed || establishConnect_changed)
|
||||
{
|
||||
if (_connectedStats.isOn()) {
|
||||
log_connected();
|
||||
if (_establishConnectStats.isSet()) {
|
||||
if (_connectedStats.isOn()) {
|
||||
log_connected();
|
||||
|
||||
// _establishConnectStats.resetCount();
|
||||
} else if (_connectedStats.isOff() && !_establishConnectStats.isOn()) {
|
||||
log_disconnected();
|
||||
// _establishConnectStats.resetCount();
|
||||
} else if (_establishConnectStats.isSet() &&
|
||||
_connectedStats.isOff() &&
|
||||
!_establishConnectStats.isOn()) {
|
||||
log_disconnected();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,19 +284,9 @@ void NWPluginData_static_runtime::processEvents()
|
||||
if (Settings.UseRules && _eventInterfaceName.length())
|
||||
{
|
||||
if (_operationalStats.isOn()) {
|
||||
if (_isAP) {
|
||||
eventQueue.addDeDup(F("WiFi#APmodeConnected"));
|
||||
}
|
||||
else {
|
||||
eventQueue.addDeDup(concat(_eventInterfaceName, F("#Connected")));
|
||||
}
|
||||
eventQueue.addDeDup(concat(_eventInterfaceName, F("#Connected")));
|
||||
} else if (_operationalStats.isOff()) {
|
||||
if (_isAP) {
|
||||
eventQueue.addDeDup(F("WiFi#APmodeDisconnected"));
|
||||
}
|
||||
else {
|
||||
eventQueue.addDeDup(concat(_eventInterfaceName, F("#Disconnected")));
|
||||
}
|
||||
eventQueue.addDeDup(concat(_eventInterfaceName, F("#Disconnected")));
|
||||
}
|
||||
}
|
||||
statusLED(true);
|
||||
@@ -300,19 +294,9 @@ void NWPluginData_static_runtime::processEvents()
|
||||
|
||||
if (_startStopStats.changedSinceLastCheck_and_clear() && Settings.UseRules) {
|
||||
if (_startStopStats.isOn()) {
|
||||
if (_isAP) {
|
||||
eventQueue.addDeDup(F("WiFi#APmodeEnabled"));
|
||||
}
|
||||
else {
|
||||
eventQueue.addDeDup(concat(_eventInterfaceName, F("#Enabled")));
|
||||
}
|
||||
eventQueue.addDeDup(concat(_eventInterfaceName, F("#Enabled")));
|
||||
} else if (_startStopStats.isOff()) {
|
||||
if (_isAP) {
|
||||
eventQueue.addDeDup(F("WiFi#APmodeDisabled"));
|
||||
}
|
||||
else {
|
||||
eventQueue.addDeDup(concat(_eventInterfaceName, F("#Disabled")));
|
||||
}
|
||||
eventQueue.addDeDup(concat(_eventInterfaceName, F("#Disabled")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,13 +105,26 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
WiFi_AP_Candidate_List_t candidates;
|
||||
|
||||
// Known credentials supplying the following:
|
||||
// - Credentials index
|
||||
// - Priority
|
||||
// - SSID
|
||||
// - Key
|
||||
WiFi_AP_Candidate_List_t known;
|
||||
|
||||
WiFi_AP_Candidate_List_t scanned;
|
||||
// Unprocessed found from latest scan
|
||||
WiFi_AP_Candidate_List_t scanned_new;
|
||||
|
||||
// Processed found via one or more scans
|
||||
// When processing scanned_new, matching elements from a previous scan (in 'scanned')
|
||||
// will be updated with new information, like channel, RSSI, supported protocols, etc.
|
||||
WiFi_AP_Candidate_List_t scanned;
|
||||
|
||||
// Sorted list of possible APs to connect to. Matching known and scanned.
|
||||
// First element in the candidates list is the best candidate to try first.
|
||||
// Failed connect attempts will be removed from the candidates list.
|
||||
WiFi_AP_Candidate_List_t candidates;
|
||||
|
||||
WiFi_AP_Candidate_const_iterator known_it;
|
||||
|
||||
WiFi_AP_Candidate currentCandidate;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
StateDiagram [frame=true framecolor=steelblue label="State Diagram ESPEasy WiFi_STA_State_e"] {
|
||||
state Disabled [fillcolor=lightblue]
|
||||
state TimeOut as "TimeOut/\nError" [fillcolor=orangered]
|
||||
state STA_Initializing as "STA\nInitializing"
|
||||
state STA_Scanning as "STA\nScanning"
|
||||
state STA_AP_Scanning as "STA+AP\nScanning"
|
||||
state STA_Connecting as "STA\nConnecting"
|
||||
state STA_Reconnecting as "STA\nReconnecting"
|
||||
state STA_Connected as "STA\nConnected" [fillcolor=palegreen]
|
||||
|
||||
|
||||
choice AlreadyConnected
|
||||
choice HasScanned_NoCredentials
|
||||
choice AP_on_no_nw_found
|
||||
choice KnownAPfound
|
||||
|
||||
choice ExitSTA
|
||||
|
||||
choice ScanDone
|
||||
|
||||
initialState->Disabled
|
||||
|
||||
|
||||
Disabled -> STA_Initializing "Init\nSTA"
|
||||
STA_Initializing -> AlreadyConnected "Already\nConnected?"
|
||||
AlreadyConnected -> STA_Connected "Connected" [color=green]
|
||||
AlreadyConnected -> HasScanned_NoCredentials "Has Credentials?"
|
||||
Disabled <- HasScanned_NoCredentials "Scanned &\nNo Credentials"
|
||||
HasScanned_NoCredentials -> KnownAPfound "Known\nNetwork\nfound?"
|
||||
KnownAPfound -> AP_on_no_nw_found "Scan\nNeeded"
|
||||
KnownAPfound -> STA_Connecting "No Scan\nNeeded"
|
||||
AP_on_no_nw_found -> STA_AP_Scanning "AP"
|
||||
AP_on_no_nw_found -> STA_Scanning "No AP"
|
||||
|
||||
TimeOut <- ScanDone "Scan\nFail" [color=red]
|
||||
Disabled <- ScanDone "Scan\nSuccess" [color=green]
|
||||
|
||||
STA_Reconnecting <- STA_Connected "Disconnect" [color=red]
|
||||
STA_Reconnecting -> STA_Connected "Connected" [color=green]
|
||||
STA_Connecting -> STA_Connected "Connected" [color=green]
|
||||
|
||||
STA_Connected -> STA_Connected "After 5 min:\nMark Stable"
|
||||
|
||||
|
||||
ScanDone <- STA_Scanning "Scan\nDone"
|
||||
ScanDone <- STA_AP_Scanning "Scan\nDone"
|
||||
|
||||
STA_AP_Scanning <- STA_AP_Scanning "Next\nChannel"
|
||||
|
||||
TimeOut <- STA_Connecting "Connect\nFail" [color=red]
|
||||
|
||||
TimeOut <- STA_Initializing "Init\nSTA\nFail" [color=red]
|
||||
|
||||
Disabled <- Disabled "Wait for\nStartup Delay/\nSetup"
|
||||
Disabled <- TimeOut "Restart\nWiFi"
|
||||
|
||||
Disabled <- ExitSTA "Exit\nSTA"
|
||||
|
||||
TimeOut <- STA_Reconnecting "Reconnect\nFail" [color=red]
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
StateDiagram [frame=true framecolor=steelblue label="State Diagram ESPEasyWiFi_mode_e"] {
|
||||
choice NeedSetup
|
||||
choice HasCredentials
|
||||
choice HasCredentialsSetupRequired
|
||||
choice APalwaysOn
|
||||
choice APalwaysOnNoCred
|
||||
|
||||
state STA_only as "STA\nOnly" [fillcolor=cyan]
|
||||
state STA_AP as "STA\n+\nAP" [fillcolor=olivedrab1]
|
||||
state AP_only as "AP\nOnly" [fillcolor=orange1]
|
||||
|
||||
state Off [fillcolor=lightblue]
|
||||
state Setup with behaviors "WiFi scan\nStart webUI\nEnter Credentials\nSave" [fillcolor=yellow] {
|
||||
state Scan as "STA Only\nScan"
|
||||
state StartAP as "AP Only\nWebUI"
|
||||
state ClientConnect as "User Connects\nCaptive Portal to /setup"
|
||||
state EnterCredentials as "Enter WiFi Credentials"
|
||||
state ClientDisconnect as "User\nDisconnects"
|
||||
state CredentialsUpdated as "Credentials Updated"
|
||||
|
||||
initialState -> Scan
|
||||
Scan -> StartAP
|
||||
StartAP -> ClientConnect
|
||||
ClientDisconnect <- ClientConnect
|
||||
Scan <- ClientDisconnect
|
||||
Scan <- StartAP "Time Out"
|
||||
ClientConnect -> EnterCredentials
|
||||
EnterCredentials -> CredentialsUpdated
|
||||
CredentialsUpdated -> finalState
|
||||
}
|
||||
|
||||
|
||||
initialState->Off
|
||||
NeedSetup <- Off "Check\nNeed Setup?"
|
||||
NeedSetup -> HasCredentials "Setup not\nAllowed/\nRequired"
|
||||
HasCredentials -> APalwaysOn "Has\nCredentials"
|
||||
APalwaysOn -> STA_AP "AP Always On\nor\nSetup 1st connect"
|
||||
APalwaysOn -> STA_only "No AP"
|
||||
HasCredentials -> APalwaysOnNoCred "No\nCredentials"
|
||||
Off <- APalwaysOnNoCred "No AP"
|
||||
APalwaysOnNoCred -> AP_only "AP"
|
||||
NeedSetup -> HasCredentialsSetupRequired "Setup\nAllowed"
|
||||
HasCredentialsSetupRequired -> APalwaysOn "Has Credentials,\nNo Setup"
|
||||
|
||||
Setup <- HasCredentialsSetupRequired "No Credentials,\nEnter Setup"
|
||||
Setup -> NeedSetup "Setup\nDone"
|
||||
|
||||
STA_only <- STA_only "Retry/\nReconnect"
|
||||
STA_AP -> AP_only "No Known\nNetwork found"
|
||||
|
||||
STA_AP <- STA_AP "Retry/\nReconnect"
|
||||
|
||||
Setup <- STA_AP "Setup\nFailed 1st\nConnect"
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "../wifi/ESPEasyWiFi_state_machine.h"
|
||||
#include "ESPEasy/net/wifi/WiFi_State.h"
|
||||
#include "ESPEasy/net/wifi/WiFi_STA_State.h"
|
||||
|
||||
#if FEATURE_WIFI
|
||||
|
||||
@@ -24,9 +24,14 @@ namespace wifi {
|
||||
# define WIFI_STATE_MACHINE_STA_SCANNING_TIMEOUT 10000
|
||||
# define WIFI_STATE_MACHINE_AP_ONLY_TIMEOUT 60000
|
||||
|
||||
# define WIFI_STATE_MACHINE_STA_CONNECTED_STABLE 300000 // 5 minutes
|
||||
|
||||
void ESPEasyWiFi_t::setup() {
|
||||
if (!Settings.getNetworkEnabled(NETWORK_INDEX_WIFI_STA)) { return; }
|
||||
|
||||
WiFi_AP_Candidates.clearCache();
|
||||
WiFi_AP_Candidates.load_knownCredentials();
|
||||
|
||||
// TODO TD-er: Must maybe also call 'disable()' first?
|
||||
|
||||
// TODO TD-er: Load settings
|
||||
@@ -43,276 +48,203 @@ void ESPEasyWiFi_t::setup() {
|
||||
|
||||
void ESPEasyWiFi_t::enable() {}
|
||||
|
||||
void ESPEasyWiFi_t::disable() { setState(WiFiState_e::Disabled, 100); }
|
||||
void ESPEasyWiFi_t::disable() { setState(WiFi_STA_State_e::Disabled, 0); }
|
||||
|
||||
void ESPEasyWiFi_t::begin() {
|
||||
if (connected()) setState(WiFiState_e::STA_Connected);
|
||||
if (WiFi_AP_Candidates.hasCandidates()) {
|
||||
setState(WiFiState_e::IdleWaiting, 100);
|
||||
if (connected()) {
|
||||
setState(WiFi_STA_State_e::STA_Connected);
|
||||
} else {
|
||||
if (WiFi_AP_Candidates.hasScanned()) {
|
||||
// Has no candidates, but scan was performed.
|
||||
if (shouldStartAP_fallback()) {
|
||||
setState(WiFiState_e::AP_only, WIFI_STATE_MACHINE_AP_ONLY_TIMEOUT);
|
||||
}
|
||||
} else {
|
||||
if (WifiIsAP(WiFi.getMode())) {
|
||||
// TODO TD-er: Must check if any client is connected.
|
||||
// If not, then we can disable AP mode and switch to WiFiState_e::STA_Scanning
|
||||
setState(WiFiState_e::STA_AP_Scanning, WIFI_STATE_MACHINE_STA_AP_SCANNING_TIMEOUT);
|
||||
} else {
|
||||
// setState(WiFiState_e::STA_AP_Scanning, WIFI_STATE_MACHINE_STA_AP_SCANNING_TIMEOUT);
|
||||
setState(WiFiState_e::STA_Scanning, WIFI_STATE_MACHINE_STA_SCANNING_TIMEOUT);
|
||||
}
|
||||
}
|
||||
setState(WiFi_STA_State_e::STA_Initializing, 100);
|
||||
}
|
||||
}
|
||||
|
||||
void ESPEasyWiFi_t::loop()
|
||||
{
|
||||
// TODO TD-er: Must inspect WiFiEventData to see if we need to update some state here.
|
||||
auto wifi_STA_data = getWiFi_STA_NWPluginData_static_runtime();
|
||||
/*
|
||||
if ((_state != WiFi_STA_State_e::Disabled) &&
|
||||
(getWiFi_STA_NWPluginData_static_runtime() == nullptr)) {
|
||||
setState(WiFi_STA_State_e::Disabled, 100);
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
if (!wifi_STA_data) { return; }
|
||||
const bool stateTimeoutReached = _state_timeout.isSet() && _state_timeout.timeReached();
|
||||
|
||||
if (_state != WiFiState_e::IdleWaiting) {
|
||||
if (_callbackError ||
|
||||
(_state_timeout.isSet() && _state_timeout.timeReached()))
|
||||
if (_state != WiFi_STA_State_e::TimeOut) {
|
||||
if (_callbackError || stateTimeoutReached)
|
||||
{
|
||||
// TODO TD-er: Must check what error was given???
|
||||
_callbackError = false;
|
||||
|
||||
if (getMode() == ESPEasyWiFi_mode_e::Setup /* && _state == WiFiState_e::AP_Fallback */) {
|
||||
// TODO TD-er: Must perhaps check what action was pending and act on it?
|
||||
if (stateTimeoutReached) {
|
||||
if (_state == WiFi_STA_State_e::Disabled) {
|
||||
setState(WiFi_STA_State_e::STA_Initializing, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
setState(_state == WiFiState_e::STA_Connected_Setup ? WiFiState_e::STA_Connected : WiFiState_e::IdleWaiting);
|
||||
} else {
|
||||
setState(WiFiState_e::WiFiOFF);
|
||||
if (_state == WiFi_STA_State_e::STA_Connected) {
|
||||
setState(WiFi_STA_State_e::STA_Connected_Stable);
|
||||
return;
|
||||
}
|
||||
}
|
||||
setState(WiFi_STA_State_e::TimeOut, 100);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (_state)
|
||||
{
|
||||
case WiFiState_e::Disabled:
|
||||
case WiFi_STA_State_e::Disabled:
|
||||
// Do nothing here, as the device is disabled.
|
||||
break;
|
||||
case WiFiState_e::WiFiOFF:
|
||||
begin();
|
||||
|
||||
// setState(WiFiState_e::IdleWaiting, 100);
|
||||
case WiFi_STA_State_e::TimeOut:
|
||||
// We always go from TimeOut to Disabled.
|
||||
// Called this from the loop() to prevent recursive calls to setState
|
||||
setState(WiFi_STA_State_e::Disabled, 100);
|
||||
break;
|
||||
case WiFiState_e::AP_only:
|
||||
case WiFiState_e::AP_Fallback:
|
||||
|
||||
// TODO TD-er: Should also check for 'setup' mode, so we switch to connecting state instead of IdleWaiting
|
||||
// For sure not just when there are candidates.
|
||||
if (!ESPEasy::net::wifi::wifiAPmodeActivelyUsed()) {
|
||||
if (WiFi_AP_Candidates.hasCandidates() ||
|
||||
_state_timeout.timeReached()) {
|
||||
setState(WiFiState_e::IdleWaiting, 100);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WiFiState_e::IdleWaiting:
|
||||
case WiFi_STA_State_e::STA_Initializing:
|
||||
WiFi_AP_Candidates.purge_expired();
|
||||
|
||||
if (connected()) {
|
||||
if (getMode() == ESPEasyWiFi_mode_e::Setup) {
|
||||
setState(WiFiState_e::STA_Connected_Setup, 60000);
|
||||
}
|
||||
else {
|
||||
setState(WiFiState_e::STA_Connected, 100);
|
||||
}
|
||||
setState(WiFi_STA_State_e::STA_Connected);
|
||||
break;
|
||||
}
|
||||
|
||||
if (_state_timeout.timeReached() || (getSTA_connected_state() == STA_connected_state::Idle)) {
|
||||
if (WiFi_AP_Candidates.hasScanned() && !WiFi_AP_Candidates.hasCandidateCredentials()) {
|
||||
// We don't have any credentials and we do have some scanned, so we are prepared for setup.
|
||||
// However we don't need to quickly switch between Disabled and STA_Initializing, thus set to 10 sec.
|
||||
setState(WiFi_STA_State_e::Disabled, 10000);
|
||||
break;
|
||||
}
|
||||
|
||||
if (getWiFi_STA_NWPluginData_static_runtime() == nullptr) {
|
||||
Scheduler.setNetworkInitTimer(0, NETWORK_INDEX_WIFI_STA);
|
||||
}
|
||||
|
||||
if (getWiFi_STA_NWPluginData_static_runtime() != nullptr) {
|
||||
// This is where we decide what to do next:
|
||||
// - Reconnect
|
||||
// - Scan
|
||||
//
|
||||
|
||||
|
||||
// Do we have candidate to connect to ?
|
||||
if (WiFi_AP_Candidates.hasCandidates()) {
|
||||
setState(WiFiState_e::STA_Connecting, WIFI_STATE_MACHINE_STA_CONNECTING_TIMEOUT);
|
||||
} else if ((WiFi_AP_Candidates.scanComplete() == 0)
|
||||
|| (WiFi_AP_Candidates.scanComplete() == -3)) {
|
||||
if (WifiIsAP(WiFi.getMode())) {
|
||||
// TODO TD-er: Must check if any client is connected.
|
||||
// If not, then we can disable AP mode and switch to WiFiState_e::STA_Scanning
|
||||
setState(WiFiState_e::STA_AP_Scanning, WIFI_STATE_MACHINE_STA_AP_SCANNING_TIMEOUT);
|
||||
} else {
|
||||
// setState(WiFiState_e::STA_AP_Scanning, WIFI_STATE_MACHINE_STA_AP_SCANNING_TIMEOUT);
|
||||
setState(WiFiState_e::STA_Scanning, WIFI_STATE_MACHINE_STA_SCANNING_TIMEOUT);
|
||||
}
|
||||
|
||||
// Move up?
|
||||
} else if (!WiFi_AP_Candidates.hasCandidateCredentials() ||
|
||||
!Settings.DoNotStartAPfallback_ConnectFail()) {
|
||||
if (!WiFi_AP_Candidates.hasCandidateCredentials()
|
||||
|
||||
// && !WiFiEventData.warnedNoValidWiFiSettings
|
||||
)
|
||||
{
|
||||
// Check for whether No Valid WiFi settings was already logged
|
||||
static uint32_t lastTimeLoggedNoWiFiCredentials = 0;
|
||||
if (timePassedSince(lastTimeLoggedNoWiFiCredentials) > 5000) {
|
||||
addLog(LOG_LEVEL_ERROR, F("WIFI : No valid wifi settings"));
|
||||
lastTimeLoggedNoWiFiCredentials = millis();
|
||||
}
|
||||
|
||||
// WiFiEventData.warnedNoValidWiFiSettings = true;
|
||||
}
|
||||
wifi_STA_data->mark_connect_failed();
|
||||
|
||||
wifi_STA_data->_establishConnectStats.clear();
|
||||
|
||||
// WiFiEventData.wifiConnectAttemptNeeded = false;
|
||||
// end move up??
|
||||
setState(WiFi_STA_State_e::STA_Connecting, WIFI_STATE_MACHINE_STA_CONNECTING_TIMEOUT);
|
||||
break;
|
||||
}
|
||||
|
||||
// No known candidates, so need to scan first.
|
||||
if (WifiIsAP(WiFi.getMode())) {
|
||||
// TODO TD-er: Must check if any client is connected.
|
||||
// If not, then we can disable AP mode and switch to WiFi_STA_State_e::STA_Scanning
|
||||
setState(WiFi_STA_State_e::STA_AP_Scanning, WIFI_STATE_MACHINE_STA_AP_SCANNING_TIMEOUT);
|
||||
break;
|
||||
}
|
||||
setState(WiFi_STA_State_e::STA_Scanning, WIFI_STATE_MACHINE_STA_SCANNING_TIMEOUT);
|
||||
}
|
||||
break;
|
||||
case WiFiState_e::STA_Scanning:
|
||||
|
||||
case WiFi_STA_State_e::STA_Scanning:
|
||||
case WiFi_STA_State_e::STA_AP_Scanning:
|
||||
{
|
||||
// -1 if scan not finished
|
||||
auto scanCompleteStatus = WiFi_AP_Candidates.scanComplete();
|
||||
|
||||
if (scanCompleteStatus >= 0) {
|
||||
WiFi_AP_Candidates.load_knownCredentials();
|
||||
WiFi_AP_Candidates.process_WiFiscan();
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
addLog(LOG_LEVEL_INFO, strformat(
|
||||
F("WiFi : Scan done, found %d APs"),
|
||||
WiFi_AP_Candidates.scanComplete()));
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
} else if (scanCompleteStatus == -2) { // WIFI_SCAN_FAILED
|
||||
addLog(LOG_LEVEL_ERROR, F("WiFi : Scan failed"));
|
||||
|
||||
// WiFi.scanDelete();
|
||||
setState(WiFiState_e::WiFiOFF, 1000);
|
||||
}
|
||||
|
||||
if (_state_timeout.timeReached() || (scanCompleteStatus >= 0)) {
|
||||
// WiFi.scanDelete();
|
||||
|
||||
if (_state_timeout.timeReached()) {
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
addLog(LOG_LEVEL_ERROR, F("WiFi : Scan Running Timeout"));
|
||||
# endif
|
||||
}
|
||||
|
||||
if (WiFi_AP_Candidates.hasCandidates()) {
|
||||
setState(WiFiState_e::WiFiOFF, 100);
|
||||
} else {
|
||||
// FIXME TD-er: This might not be a responsibility of this state machine....
|
||||
if (shouldStartAP_fallback()) {
|
||||
setState(WiFiState_e::AP_Fallback, Settings.APfallback_minimal_on_time_sec() * 1000);
|
||||
|
||||
// TODO TD-er: Must keep track of whether the user has forced AP to be autostarted.
|
||||
} else {
|
||||
setState(WiFiState_e::WiFiOFF, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WiFiState_e::STA_AP_Scanning:
|
||||
{
|
||||
// -1 if scan not finished
|
||||
auto scanCompleteStatus = WiFi_AP_Candidates.scanComplete();
|
||||
|
||||
if (scanCompleteStatus >= 0) {
|
||||
WiFi_AP_Candidates.load_knownCredentials();
|
||||
WiFi_AP_Candidates.process_WiFiscan();
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
addLog(LOG_LEVEL_INFO, strformat(
|
||||
F("WiFi : Scan channel %d done, found %d APs"),
|
||||
_scan_channel,
|
||||
WiFi_AP_Candidates.scanComplete()));
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
} else if (scanCompleteStatus == -2) { // WIFI_SCAN_FAILED
|
||||
addLog(LOG_LEVEL_ERROR, F("WiFi : Scan failed"));
|
||||
|
||||
// WiFi.scanDelete();
|
||||
setState(WiFiState_e::WiFiOFF, 1000);
|
||||
}
|
||||
|
||||
if (_state_timeout.timeReached() || (scanCompleteStatus >= 0)) {
|
||||
// WiFi.scanDelete();
|
||||
|
||||
if (_state_timeout.timeReached()) {
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
addLog(LOG_LEVEL_ERROR, F("WiFi : Scan Running Timeout"));
|
||||
# endif
|
||||
}
|
||||
++_scan_channel;
|
||||
|
||||
if (_scan_channel > 14) {
|
||||
_scan_channel = 0;
|
||||
|
||||
if (!WiFi_AP_Candidates.hasCandidateCredentials() &&
|
||||
!Settings.DoNotStartAPfallback_ConnectFail()) {
|
||||
setState(WiFiState_e::AP_only, WIFI_STATE_MACHINE_AP_ONLY_TIMEOUT);
|
||||
} else {
|
||||
setState(WiFiState_e::WiFiOFF, 100);
|
||||
}
|
||||
}
|
||||
else {
|
||||
setState(WiFiState_e::STA_AP_Scanning, 500);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// Check if scanning is finished
|
||||
// When scanning per channel, call for scanning next channel
|
||||
}
|
||||
case WiFiState_e::STA_Connecting:
|
||||
case WiFiState_e::STA_Reconnecting:
|
||||
if (scanCompleteStatus >= 0) {
|
||||
WiFi_AP_Candidates.load_knownCredentials();
|
||||
WiFi_AP_Candidates.process_WiFiscan();
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
|
||||
// Check if (re)connecting has finished
|
||||
{
|
||||
const STA_connected_state sta_connected_state = getSTA_connected_state();
|
||||
|
||||
if (sta_connected_state == STA_connected_state::Connected) {
|
||||
if (getMode() == ESPEasyWiFi_mode_e::Setup) {
|
||||
setState(WiFiState_e::STA_Connected_Setup, 60000);
|
||||
}
|
||||
else {
|
||||
setState(WiFiState_e::STA_Connected, 100);
|
||||
}
|
||||
} else if (_state_timeout.timeReached()) {
|
||||
if (_state == WiFiState_e::STA_Connecting) {
|
||||
setState(WiFiState_e::STA_Reconnecting, WIFI_STATE_MACHINE_STA_CONNECTING_TIMEOUT);
|
||||
if (_state == WiFi_STA_State_e::STA_Scanning) {
|
||||
addLog(LOG_LEVEL_INFO, strformat(
|
||||
F("WiFi : Scan done, found %d APs"),
|
||||
scanCompleteStatus));
|
||||
} else {
|
||||
wifi_STA_data->mark_connect_failed();
|
||||
setState(WiFiState_e::WiFiOFF);
|
||||
addLog(LOG_LEVEL_INFO, strformat(
|
||||
F("WiFi : Scan channel %d done, found %d APs"),
|
||||
_scan_channel,
|
||||
scanCompleteStatus));
|
||||
}
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
|
||||
if (_state == WiFi_STA_State_e::STA_AP_Scanning) {
|
||||
++_scan_channel;
|
||||
|
||||
// TODO TD-er: What to do with 5 GHz WiFi?
|
||||
if (_scan_channel > 14) {
|
||||
_scan_channel = 0;
|
||||
|
||||
// "Scan Success"
|
||||
setState(WiFi_STA_State_e::Disabled, 100);
|
||||
}
|
||||
else {
|
||||
setState(WiFi_STA_State_e::STA_AP_Scanning, 500);
|
||||
}
|
||||
} else {
|
||||
// "Scan Success"
|
||||
setState(WiFi_STA_State_e::Disabled, 100);
|
||||
}
|
||||
} else if (scanCompleteStatus == -2) { // WIFI_SCAN_FAILED
|
||||
addLog(LOG_LEVEL_ERROR, F("WiFi : Scan failed"));
|
||||
|
||||
// WiFi.scanDelete();
|
||||
setState(WiFi_STA_State_e::TimeOut, 1000);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WiFi_STA_State_e::STA_Connecting:
|
||||
case WiFi_STA_State_e::STA_Reconnecting:
|
||||
{
|
||||
// Check if (re)connecting has finished
|
||||
auto wifi_STA_data = getWiFi_STA_NWPluginData_static_runtime();
|
||||
|
||||
if (wifi_STA_data) {
|
||||
wifi_STA_data->processEvents();
|
||||
}
|
||||
|
||||
|
||||
if (getSTA_connected_state() == STA_connected_state::Connected) {
|
||||
setState(WiFi_STA_State_e::STA_Connected);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case WiFiState_e::STA_Connected:
|
||||
|
||||
case WiFi_STA_State_e::STA_Connected:
|
||||
case WiFi_STA_State_e::STA_Connected_Stable:
|
||||
|
||||
// Check if still connected
|
||||
if (getSTA_connected_state() != STA_connected_state::Connected) {
|
||||
// setState(WiFiState_e::WiFiOFF);
|
||||
if (WiFi_AP_Candidates.hasCandidates()) {
|
||||
setState(WiFiState_e::STA_Connecting, WIFI_STATE_MACHINE_STA_CONNECTING_TIMEOUT);
|
||||
} else {
|
||||
setState(WiFiState_e::STA_Scanning, WIFI_STATE_MACHINE_STA_SCANNING_TIMEOUT);
|
||||
// "Disconnect"
|
||||
|
||||
auto wifi_STA_data = getWiFi_STA_NWPluginData_static_runtime();
|
||||
|
||||
if (wifi_STA_data) {
|
||||
wifi_STA_data->mark_disconnected();
|
||||
}
|
||||
|
||||
/*
|
||||
if (Settings.UseRules)
|
||||
{
|
||||
eventQueue.addDeDup(F("WiFi#Disconnected"));
|
||||
}
|
||||
statusLED(false);
|
||||
*/
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
WiFi.disconnect(true);
|
||||
}
|
||||
|
||||
if (wifi_STA_data) {
|
||||
wifi_STA_data->processEvents();
|
||||
}
|
||||
|
||||
if (WiFi_AP_Candidates.hasCandidates()) {
|
||||
setState(WiFi_STA_State_e::STA_Reconnecting, WIFI_STATE_MACHINE_STA_CONNECTING_TIMEOUT);
|
||||
} else {
|
||||
setState(WiFi_STA_State_e::TimeOut, 100);
|
||||
}
|
||||
} else {
|
||||
// Else mark last timestamp seen as connected
|
||||
_last_seen_connected.setNow();
|
||||
|
||||
# if FEATURE_SET_WIFI_TX_PWR
|
||||
SetWiFiTXpower();
|
||||
# endif
|
||||
@@ -334,43 +266,15 @@ bool ESPEasyWiFi_t::connected() const
|
||||
|
||||
void ESPEasyWiFi_t::disconnect() { doWiFiDisconnect(); }
|
||||
|
||||
void ESPEasyWiFi_t::setState(WiFiState_e newState, uint32_t timeout) {
|
||||
void ESPEasyWiFi_t::setState(WiFi_STA_State_e newState, uint32_t timeout) {
|
||||
if (newState == _state) { return; }
|
||||
//# ifndef BUILD_NO_DEBUG
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
addLog(
|
||||
LOG_LEVEL_INFO,
|
||||
concat(F("WiFi : Set state from: "), toString(_state)) +
|
||||
concat(F(" to: "), toString(newState)) +
|
||||
concat(F(" timeout: "), timeout));
|
||||
}
|
||||
//# endif // ifndef BUILD_NO_DEBUG
|
||||
const WiFi_STA_State_e oldState = _state;
|
||||
|
||||
if ((_state == WiFiState_e::AP_only) ||
|
||||
(_state == WiFiState_e::AP_Fallback)) {
|
||||
Scheduler.setNetworkExitTimer(0, NETWORK_INDEX_WIFI_AP);
|
||||
// setAP(false);
|
||||
}
|
||||
// Need to set the newState first as some of the functions below will call
|
||||
// setState, causing a loop, or calling to change state multiple times.
|
||||
|
||||
if (_state == WiFiState_e::STA_Connected)
|
||||
{
|
||||
auto wifi_STA_data = getWiFi_STA_NWPluginData_static_runtime();
|
||||
|
||||
if (wifi_STA_data) {
|
||||
wifi_STA_data->mark_disconnected();
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
WiFi.disconnect(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((_state == WiFiState_e::STA_AP_Scanning) ||
|
||||
(_state == WiFiState_e::STA_Scanning))
|
||||
{
|
||||
WiFi_AP_Candidates.process_WiFiscan();
|
||||
}
|
||||
auto wifi_STA_data = getWiFi_STA_NWPluginData_static_runtime();
|
||||
|
||||
if (timeout == 0)
|
||||
{
|
||||
@@ -383,37 +287,94 @@ void ESPEasyWiFi_t::setState(WiFiState_e newState, uint32_t timeout) {
|
||||
_last_state_change.setNow();
|
||||
_state = newState;
|
||||
|
||||
// # ifndef BUILD_NO_DEBUG
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
addLog(
|
||||
LOG_LEVEL_INFO,
|
||||
concat(F("WiFi : Set state from: "), toString(oldState)) +
|
||||
concat(F(" to: "), toString(newState)) +
|
||||
concat(F(" timeout: "), timeout));
|
||||
}
|
||||
|
||||
// # endif // ifndef BUILD_NO_DEBUG
|
||||
|
||||
if ((oldState == WiFi_STA_State_e::STA_AP_Scanning) ||
|
||||
(oldState == WiFi_STA_State_e::STA_Scanning))
|
||||
{
|
||||
WiFi_AP_Candidates.load_knownCredentials();
|
||||
WiFi_AP_Candidates.process_WiFiscan();
|
||||
}
|
||||
|
||||
// Make sure not to call setState from this function
|
||||
// This may cause quite a lot of recursive calls.
|
||||
|
||||
switch (newState)
|
||||
{
|
||||
case WiFiState_e::Disabled:
|
||||
// Do nothing here, as the device is disabled.
|
||||
case WiFi_STA_State_e::Disabled:
|
||||
// TODO TD-er: Maybe call scheduler?
|
||||
// if (WifiIsSTA(WiFi.getMode()))
|
||||
// Scheduler.setNetworkExitTimer(0, NETWORK_INDEX_WIFI_STA);
|
||||
WifiDisconnect();
|
||||
setSTA(false);
|
||||
break;
|
||||
case WiFiState_e::WiFiOFF:
|
||||
// TODO TD-er: Must cancel all and turn off WiFi.
|
||||
setSTA_AP(false, false);
|
||||
|
||||
case WiFi_STA_State_e::TimeOut:
|
||||
{
|
||||
|
||||
// From a failed state we always turn off at least WiFi STA.
|
||||
// Only when we end up here from STA_Initializing, we also need to turn off AP.
|
||||
if (oldState == WiFi_STA_State_e::STA_Initializing) {
|
||||
// "Init STA Fail"
|
||||
|
||||
// TODO TD-er: Maybe call scheduler?
|
||||
// if (WifiIsAP(WiFi.getMode()))
|
||||
// Scheduler.setNetworkExitTimer(0, NETWORK_INDEX_WIFI_AP);
|
||||
setAP(false);
|
||||
} else if (oldState == WiFi_STA_State_e::STA_Connecting) {
|
||||
// "Connect Fail"
|
||||
if (wifi_STA_data) {
|
||||
wifi_STA_data->mark_connect_failed();
|
||||
}
|
||||
|
||||
// setState(WiFi_STA_State_e::STA_Reconnecting, WIFI_STATE_MACHINE_STA_CONNECTING_TIMEOUT);
|
||||
} else if (oldState == WiFi_STA_State_e::STA_Reconnecting) {
|
||||
// "Reconnect Fail"
|
||||
if (wifi_STA_data) {
|
||||
wifi_STA_data->mark_connect_failed();
|
||||
}
|
||||
} else if (oldState == WiFi_STA_State_e::STA_Initializing) {
|
||||
// "Init STA Fail"
|
||||
} else if ((oldState == WiFi_STA_State_e::STA_Scanning) ||
|
||||
(oldState == WiFi_STA_State_e::STA_AP_Scanning)) {
|
||||
// "Scan Fail"
|
||||
}
|
||||
|
||||
// TODO TD-er: Handle "Scan Fail" and "Init STA Fail"
|
||||
// For "Scan Fail" set state to Disabled, with some timeout?
|
||||
// Do we also need to turn off WiFi STA after successful scan?
|
||||
break;
|
||||
case WiFiState_e::AP_only:
|
||||
case WiFiState_e::AP_Fallback:
|
||||
Scheduler.setNetworkInitTimer(0, NETWORK_INDEX_WIFI_AP);
|
||||
}
|
||||
case WiFi_STA_State_e::STA_Initializing:
|
||||
/*
|
||||
if (getWiFi_STA_NWPluginData_static_runtime() == nullptr) {
|
||||
Scheduler.setNetworkInitTimer(0, NETWORK_INDEX_WIFI_STA);
|
||||
}
|
||||
_state_timeout.setMillisFromNow(Settings.getNetworkInterfaceStartupDelay(NETWORK_INDEX_WIFI_STA) + 100);
|
||||
*/
|
||||
break;
|
||||
case WiFiState_e::IdleWaiting:
|
||||
// Do nothing here as we're waiting till the timeout is over
|
||||
break;
|
||||
case WiFiState_e::STA_AP_Scanning:
|
||||
case WiFi_STA_State_e::STA_AP_Scanning:
|
||||
|
||||
// Start scanning per channel
|
||||
if (_scan_channel == 0) { _scan_channel = 1; }
|
||||
|
||||
// break;
|
||||
case WiFiState_e::STA_Scanning:
|
||||
// fall through
|
||||
case WiFi_STA_State_e::STA_Scanning:
|
||||
// Start scanning
|
||||
startScanning();
|
||||
break;
|
||||
case WiFiState_e::STA_Connecting:
|
||||
case WiFiState_e::STA_Reconnecting:
|
||||
case WiFi_STA_State_e::STA_Connecting:
|
||||
case WiFi_STA_State_e::STA_Reconnecting:
|
||||
|
||||
// Start connecting
|
||||
++_connect_attempt;
|
||||
@@ -421,28 +382,12 @@ void ESPEasyWiFi_t::setState(WiFiState_e newState, uint32_t timeout) {
|
||||
if (!connectSTA()) {
|
||||
// TODO TD-er: Must keep track of failed attempts and start AP when either no credentials present or nr. of attempts failed > some
|
||||
// threshold.
|
||||
if (!WiFi_AP_Candidates.hasCandidates()) {
|
||||
setState(WiFiState_e::STA_Scanning, WIFI_STATE_MACHINE_STA_CONNECTING_TIMEOUT);
|
||||
} else {
|
||||
setState(WiFiState_e::IdleWaiting, 100);
|
||||
}
|
||||
addLog(LOG_LEVEL_ERROR, F("WiFi : Connect STA failed"));
|
||||
setState(WiFi_STA_State_e::TimeOut, 100);
|
||||
}
|
||||
break;
|
||||
|
||||
case WiFiState_e::STA_Connected_Setup:
|
||||
{
|
||||
Scheduler.setNetworkInitTimer(0, NETWORK_INDEX_WIFI_AP);
|
||||
_last_seen_connected.setNow();
|
||||
auto wifi_STA_data = getWiFi_STA_NWPluginData_static_runtime();
|
||||
|
||||
if (wifi_STA_data) {
|
||||
wifi_STA_data->mark_connected();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case WiFiState_e::STA_Connected:
|
||||
case WiFi_STA_State_e::STA_Connected:
|
||||
{
|
||||
# ifdef ESP32
|
||||
|
||||
@@ -450,20 +395,14 @@ void ESPEasyWiFi_t::setState(WiFiState_e newState, uint32_t timeout) {
|
||||
// WiFi.STA.setDefault();
|
||||
# endif // ifdef ESP32
|
||||
|
||||
if (_state == WiFiState_e::STA_Connected_Setup) {
|
||||
Scheduler.setNetworkExitTimer(0, NETWORK_INDEX_WIFI_AP);
|
||||
setMode(ESPEasyWiFi_mode_e::STA_only);
|
||||
}
|
||||
_connect_attempt = 0;
|
||||
_last_seen_connected.setNow();
|
||||
_state_timeout.clear();
|
||||
auto wifi_STA_data = getWiFi_STA_NWPluginData_static_runtime();
|
||||
_state_timeout.setMillisFromNow(WIFI_STATE_MACHINE_STA_CONNECTED_STABLE);
|
||||
|
||||
if (wifi_STA_data) {
|
||||
wifi_STA_data->mark_connected();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
if (Settings.UseRules)
|
||||
{
|
||||
@@ -472,11 +411,14 @@ void ESPEasyWiFi_t::setState(WiFiState_e newState, uint32_t timeout) {
|
||||
statusLED(true);
|
||||
*/
|
||||
break;
|
||||
|
||||
}
|
||||
case WiFi_STA_State_e::STA_Connected_Stable:
|
||||
_state_timeout.clear();
|
||||
|
||||
// TODO TD-er: Must mark connection as stable
|
||||
break;
|
||||
}
|
||||
|
||||
auto wifi_STA_data = getWiFi_STA_NWPluginData_static_runtime();
|
||||
|
||||
if (wifi_STA_data) {
|
||||
wifi_STA_data->processEvents();
|
||||
@@ -487,7 +429,7 @@ void ESPEasyWiFi_t::checkConnectProgress() {}
|
||||
|
||||
void ESPEasyWiFi_t::startScanning()
|
||||
{
|
||||
_state = _scan_channel == 0 ? WiFiState_e::STA_Scanning : WiFiState_e::STA_AP_Scanning;
|
||||
_state = _scan_channel == 0 ? WiFi_STA_State_e::STA_Scanning : WiFi_STA_State_e::STA_AP_Scanning;
|
||||
setSTA(true);
|
||||
WifiScan(true, _scan_channel);
|
||||
_last_state_change.setNow();
|
||||
@@ -668,8 +610,13 @@ bool ESPEasyWiFi_t::shouldStartAP_fallback() const
|
||||
|
||||
bool ESPEasyWiFi_t::shouldRedirectTo_setup() const
|
||||
{
|
||||
if (!Settings.ApCaptivePortal()) return false;
|
||||
if (Settings.StartAPfallback_NoCredentials() && !SecuritySettings.hasWiFiCredentials()) {
|
||||
if (!Settings.ApCaptivePortal()) { return false; }
|
||||
|
||||
if (Settings.StartAPfallback_NoCredentials()
|
||||
&& !WiFi_AP_Candidates.hasCandidateCredentials()
|
||||
|
||||
// && !SecuritySettings.hasWiFiCredentials()
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# include "../../../src/Helpers/LongTermTimer.h"
|
||||
|
||||
# include "../wifi/WiFi_STA_connected_state.h"
|
||||
# include "../wifi/WiFi_State.h"
|
||||
# include "../wifi/WiFi_STA_State.h"
|
||||
|
||||
# include <IPAddress.h>
|
||||
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
// Process the state machine for managing WiFi connection
|
||||
void loop();
|
||||
|
||||
WiFiState_e getState() const
|
||||
WiFi_STA_State_e getState() const
|
||||
{
|
||||
return _state;
|
||||
}
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
void setState(WiFiState_e newState,
|
||||
void setState(WiFi_STA_State_e newState,
|
||||
uint32_t timeout = 0);
|
||||
|
||||
// Handle timeouts + start of AP mode
|
||||
@@ -110,7 +110,7 @@ private:
|
||||
// String _last_ssid;
|
||||
// MAC_address _last_bssid;
|
||||
// uint8_t _last_channel = 0;
|
||||
WiFiState_e _state = WiFiState_e::Disabled;
|
||||
WiFi_STA_State_e _state = WiFi_STA_State_e::Disabled;
|
||||
|
||||
ESPEasyWiFi_mode_e _mode = ESPEasyWiFi_mode_e::Off;
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
StateDiagram [frame=true framecolor=steelblue label="State Diagram ESPEasy WiFi_STA_State_e"] {
|
||||
state Disabled
|
||||
state TimeOut
|
||||
state begin
|
||||
state WiFiOFF
|
||||
state AP_only
|
||||
state AP_Fallback
|
||||
state IdleWaiting
|
||||
state STA_Scanning
|
||||
state STA_AP_Scanning
|
||||
state STA_Connecting
|
||||
state STA_Reconnecting
|
||||
state STA_Connected
|
||||
state STA_Connected_Setup
|
||||
choice bConn
|
||||
choice bCand
|
||||
choice bScan
|
||||
choice bAPmode
|
||||
choice timeoutSetupMode
|
||||
initialState->WiFiOFF
|
||||
TimeOut -> timeoutSetupMode "Setup"
|
||||
timeoutSetupMode -> IdleWaiting "!connected"
|
||||
timeoutSetupMode -> WiFiOFF "!Setup"
|
||||
WiFiOFF -> begin
|
||||
begin -> bConn
|
||||
bConn -> STA_Connected "connected"
|
||||
bConn -> bCand "!connected"
|
||||
bCand -> IdleWaiting "hasCandidates"
|
||||
bCand -> bScan "!hasCandidates"
|
||||
bScan -> AP_only "scanned"
|
||||
bScan -> bAPmode "!scanned"
|
||||
bAPmode -> STA_AP_Scanning "APmode"
|
||||
bAPmode -> STA_Scanning "!APmode"
|
||||
AP_only -> IdleWaiting "!APactiveUsed & (hasCandidates || timeout)"
|
||||
AP_Fallback -> IdleWaiting "!APactiveUsed & (hasCandidates || timeout)"
|
||||
IdleWaiting -> STA_Connected_Setup "connected() && mode==setup"
|
||||
IdleWaiting -> STA_Connected "connected() && mode!=setup"
|
||||
IdleWaiting -> STA_Connecting "timeout & hasCandidates"
|
||||
IdleWaiting -> STA_AP_Scanning "timeout & !hasCandidates & scanfail & APmode"
|
||||
IdleWaiting -> STA_Scanning "timeout & !hasCandidates & scanfail & !APmode"
|
||||
STA_Scanning -> WiFiOFF "scanfail || scansuccess || !startAP_fallback"
|
||||
STA_Scanning -> AP_Fallback "scanfail & startAP_fallback"
|
||||
STA_AP_Scanning -> WiFiOFF "scanfail || scansuccess || !startAP_fallback"
|
||||
STA_AP_Scanning -> AP_Fallback "scanfail & startAP_fallback"
|
||||
STA_AP_Scanning -> AP_only "scanfail & !startAP_fallback"
|
||||
STA_AP_Scanning -> STA_AP_Scanning "scan next channel"
|
||||
STA_Connecting -> STA_Connected_Setup "connected & SetupMode"
|
||||
STA_Connecting -> STA_Connected "connected & !SetupMode"
|
||||
STA_Connecting -> STA_Reconnecting "!connected & timeout"
|
||||
STA_Reconnecting -> IdleWaiting "!connected & timeout"
|
||||
STA_Reconnecting -> STA_Connected_Setup "connected & SetupMode"
|
||||
STA_Reconnecting -> STA_Connected "connected & !SetupMode"
|
||||
STA_Connected -> STA_Connecting "!connected & hasCandidates"
|
||||
STA_Connected -> STA_Scanning "!connected & !hasCandidates"
|
||||
STA_Connected -> STA_Connected "connected + setTXpwr"
|
||||
}
|
||||
@@ -21,7 +21,9 @@
|
||||
# define SOFTAP_STATION_COUNT WiFi.AP.stationCount()
|
||||
# endif
|
||||
# ifdef ESP8266
|
||||
# define SOFTAP_STATION_COUNT WiFi.softAPgetStationNum()
|
||||
//# define SOFTAP_STATION_COUNT WiFi.softAPgetStationNum()
|
||||
|
||||
# define SOFTAP_STATION_COUNT wifi_softap_get_station_num()
|
||||
# endif
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "../wifi/WiFi_STA_State.h"
|
||||
|
||||
#if FEATURE_WIFI
|
||||
|
||||
|
||||
namespace ESPEasy {
|
||||
namespace net {
|
||||
namespace wifi {
|
||||
|
||||
const __FlashStringHelper* toString(WiFi_STA_State_e state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case ESPEasy::net::wifi::WiFi_STA_State_e::Disabled: return F("Disabled");
|
||||
case ESPEasy::net::wifi::WiFi_STA_State_e::TimeOut: return F("TimeOut");
|
||||
case ESPEasy::net::wifi::WiFi_STA_State_e::STA_Initializing: return F("STA_Initializing");
|
||||
case ESPEasy::net::wifi::WiFi_STA_State_e::STA_Scanning: return F("STA_Scanning");
|
||||
case ESPEasy::net::wifi::WiFi_STA_State_e::STA_AP_Scanning: return F("STA_AP_Scanning");
|
||||
case ESPEasy::net::wifi::WiFi_STA_State_e::STA_Connecting: return F("STA_Connecting");
|
||||
case ESPEasy::net::wifi::WiFi_STA_State_e::STA_Reconnecting: return F("STA_Reconnecting");
|
||||
case ESPEasy::net::wifi::WiFi_STA_State_e::STA_Connected: return F("STA_Connected");
|
||||
case ESPEasy::net::wifi::WiFi_STA_State_e::STA_Connected_Stable: return F("STA_Conn_Stable");
|
||||
}
|
||||
return F("");
|
||||
}
|
||||
|
||||
} // namespace wifi
|
||||
} // namespace net
|
||||
} // namespace ESPEasy
|
||||
|
||||
#endif // if FEATURE_WIFI
|
||||
@@ -52,24 +52,18 @@ namespace wifi {
|
||||
*/
|
||||
|
||||
|
||||
enum class WiFiState_e
|
||||
enum class WiFi_STA_State_e
|
||||
{
|
||||
// WiFi radio is off and no new attempt should be made (e.g. low power mode or Ethernet active)
|
||||
// WiFi STA radio is off.
|
||||
// When timeout is set to 0, no new attempt should be made (e.g. low power mode or Ethernet active)
|
||||
// Will only change to STA_Initializing when WiFi is allowed/required to start
|
||||
Disabled,
|
||||
|
||||
// WiFi radio off, to continue, everything needs to be (re)initialized
|
||||
WiFiOFF,
|
||||
// Error state, some action failed
|
||||
TimeOut,
|
||||
|
||||
// Only running in AP mode
|
||||
// Typically this is only used when STA is off and AP Auto Start is checked
|
||||
// TODO TD-er: Must implement this.
|
||||
AP_only,
|
||||
|
||||
// Fallback mode which is started when connecting to AP was not possible
|
||||
AP_Fallback,
|
||||
|
||||
// WiFi was in some kind of error state or needs waiting period
|
||||
IdleWaiting,
|
||||
// State from where we decide to start scanning or connecting
|
||||
STA_Initializing,
|
||||
|
||||
// STA mode + scanning
|
||||
STA_Scanning,
|
||||
@@ -87,12 +81,10 @@ enum class WiFiState_e
|
||||
|
||||
// Connected to an AP
|
||||
STA_Connected,
|
||||
|
||||
STA_Connected_Setup
|
||||
|
||||
STA_Connected_Stable
|
||||
};
|
||||
|
||||
const __FlashStringHelper* toString(WiFiState_e state);
|
||||
const __FlashStringHelper* toString(WiFi_STA_State_e state);
|
||||
|
||||
|
||||
} // namespace wifi
|
||||
@@ -1,33 +0,0 @@
|
||||
#include "../wifi/WiFi_State.h"
|
||||
|
||||
#if FEATURE_WIFI
|
||||
|
||||
|
||||
namespace ESPEasy {
|
||||
namespace net {
|
||||
namespace wifi {
|
||||
|
||||
const __FlashStringHelper* toString(WiFiState_e state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case ESPEasy::net::wifi::WiFiState_e::Disabled: return F("Disabled");
|
||||
case ESPEasy::net::wifi::WiFiState_e::WiFiOFF: return F("OFF");
|
||||
case ESPEasy::net::wifi::WiFiState_e::AP_only: return F("AP_only");
|
||||
case ESPEasy::net::wifi::WiFiState_e::AP_Fallback: return F("AP_Fallback");
|
||||
case ESPEasy::net::wifi::WiFiState_e::IdleWaiting: return F("IdleWaiting");
|
||||
case ESPEasy::net::wifi::WiFiState_e::STA_Scanning: return F("STA_Scanning");
|
||||
case ESPEasy::net::wifi::WiFiState_e::STA_AP_Scanning: return F("STA_AP_Scanning");
|
||||
case ESPEasy::net::wifi::WiFiState_e::STA_Connecting: return F("STA_Connecting");
|
||||
case ESPEasy::net::wifi::WiFiState_e::STA_Reconnecting: return F("STA_Reconnecting");
|
||||
case ESPEasy::net::wifi::WiFiState_e::STA_Connected: return F("STA_Connected");
|
||||
case ESPEasy::net::wifi::WiFiState_e::STA_Connected_Setup: return F("STA_Connected_Setup");
|
||||
}
|
||||
return F("");
|
||||
}
|
||||
|
||||
} // namespace wifi
|
||||
} // namespace net
|
||||
} // namespace ESPEasy
|
||||
|
||||
#endif // if FEATURE_WIFI
|
||||
@@ -236,6 +236,9 @@ bool set_Gpio_PWM(int gpio, uint32_t dutyCycle, uint32_t fadeDuration_ms, uint32
|
||||
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
|
||||
// FIXME TD-er: For now fade is disabled. See: https://github.com/espressif/arduino-esp32/issues/12709
|
||||
fadeDuration_ms = 0;
|
||||
|
||||
if (fadeDuration_ms == 0)
|
||||
{
|
||||
analogWriteESP32(gpio, dutyCycle, frequency);
|
||||
|
||||
@@ -1506,7 +1506,7 @@ String getDigestAuth(const String& authReq,
|
||||
md5.toString().c_str()); // response
|
||||
}
|
||||
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
# if !defined(BUILD_NO_DEBUG) || defined(ESP32)
|
||||
|
||||
void log_http_result(const ESPEasy_HTTPClient& http,
|
||||
const String & logIdentifier,
|
||||
@@ -1617,7 +1617,7 @@ int http_authenticate(const String& logIdentifier,
|
||||
http.setRedirectLimit(2);
|
||||
}
|
||||
# if FEATURE_TLS
|
||||
http.setTimeout(timeout);
|
||||
http.setConnectTimeout(10000);
|
||||
#else
|
||||
# ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS
|
||||
|
||||
@@ -1643,6 +1643,9 @@ int http_authenticate(const String& logIdentifier,
|
||||
# if defined(CORE_POST_2_6_0) || defined(ESP32)
|
||||
# if FEATURE_TLS
|
||||
const String fullURL = joinURL(user, pass, host, port, uri, protocol);
|
||||
#ifndef LIMIT_BUILD_SIZE
|
||||
addLog(LOG_LEVEL_INFO, concat(F("SendToHTTP full URL: "), fullURL));
|
||||
#endif
|
||||
if (fullURL.startsWith(F("https")))
|
||||
http.begin(fullURL, nullptr); // HTTPS
|
||||
else
|
||||
@@ -1676,12 +1679,14 @@ int http_authenticate(const String& logIdentifier,
|
||||
// }
|
||||
}
|
||||
}
|
||||
String tmp = HttpMethod;
|
||||
const char* httpMethod_cstr = &tmp[0];
|
||||
|
||||
// start connection and send HTTP header (and body)
|
||||
if (equals(HttpMethod, F("HEAD")) || equals(HttpMethod, F("GET"))) {
|
||||
httpCode = http.sendRequest(HttpMethod.c_str());
|
||||
httpCode = http.sendRequest(httpMethod_cstr);
|
||||
} else {
|
||||
httpCode = http.sendRequest(HttpMethod.c_str(), postStr);
|
||||
httpCode = http.sendRequest(httpMethod_cstr, postStr);
|
||||
}
|
||||
|
||||
// Check to see if we need to try digest auth
|
||||
@@ -1723,9 +1728,9 @@ int http_authenticate(const String& logIdentifier,
|
||||
|
||||
// start connection and send HTTP header (and body)
|
||||
if (equals(HttpMethod, F("HEAD")) || equals(HttpMethod, F("GET"))) {
|
||||
httpCode = http.sendRequest(HttpMethod.c_str());
|
||||
httpCode = http.sendRequest(httpMethod_cstr);
|
||||
} else {
|
||||
httpCode = http.sendRequest(HttpMethod.c_str(), postStr);
|
||||
httpCode = http.sendRequest(httpMethod_cstr, postStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1750,7 +1755,7 @@ int http_authenticate(const String& logIdentifier,
|
||||
}
|
||||
// -----------------------------------------------------------
|
||||
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
# if !defined(BUILD_NO_DEBUG) || defined(ESP32)
|
||||
log_http_result(http, logIdentifier, host + ':' + port, HttpMethod, httpCode, EMPTY_STRING);
|
||||
# endif
|
||||
return httpCode;
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
#include "../../ESPEasy/net/Globals/ESPEasyWiFiEvent.h"
|
||||
#include "../../ESPEasy/net/Globals/NetworkState.h"
|
||||
#include "../../ESPEasy/net/Globals/NWPlugins.h"
|
||||
#include "../../ESPEasy/net/wifi/WiFi_State.h"
|
||||
#include "../../ESPEasy/net/wifi/WiFi_STA_State.h"
|
||||
|
||||
|
||||
#ifdef USES_C015
|
||||
|
||||
@@ -703,15 +703,19 @@ bool getCheckWebserverArg_int(const String& key,
|
||||
}
|
||||
|
||||
bool update_whenset_FormItemInt(const __FlashStringHelper * key,
|
||||
int & value)
|
||||
int & value,
|
||||
bool* changed)
|
||||
{
|
||||
return update_whenset_FormItemInt(String(key), value);
|
||||
return update_whenset_FormItemInt(String(key), value, changed);
|
||||
}
|
||||
|
||||
bool update_whenset_FormItemInt(const String& key, int& value) {
|
||||
bool update_whenset_FormItemInt(const String& key, int& value,
|
||||
bool* changed) {
|
||||
int tmpVal;
|
||||
|
||||
if (getCheckWebserverArg_int(key, tmpVal)) {
|
||||
if (changed != nullptr && value != tmpVal)
|
||||
*changed = true;
|
||||
value = tmpVal;
|
||||
return true;
|
||||
}
|
||||
@@ -719,17 +723,21 @@ bool update_whenset_FormItemInt(const String& key, int& value) {
|
||||
}
|
||||
|
||||
bool update_whenset_FormItemInt(const __FlashStringHelper * key,
|
||||
uint32_t & value)
|
||||
uint32_t & value,
|
||||
bool* changed)
|
||||
{
|
||||
return update_whenset_FormItemInt(String(key), value);
|
||||
return update_whenset_FormItemInt(String(key), value, changed);
|
||||
}
|
||||
|
||||
bool update_whenset_FormItemInt(const String& key,
|
||||
uint32_t & value)
|
||||
uint32_t & value,
|
||||
bool* changed)
|
||||
{
|
||||
uint32_t tmpVal;
|
||||
|
||||
if (getCheckWebserverArg_int(key, tmpVal)) {
|
||||
if (changed != nullptr && value != tmpVal)
|
||||
*changed = true;
|
||||
value = tmpVal;
|
||||
return true;
|
||||
}
|
||||
@@ -738,16 +746,20 @@ bool update_whenset_FormItemInt(const String& key,
|
||||
|
||||
|
||||
bool update_whenset_FormItemInt(const __FlashStringHelper * key,
|
||||
int8_t& value)
|
||||
int8_t& value,
|
||||
bool* changed)
|
||||
{
|
||||
return update_whenset_FormItemInt(String(key), value);
|
||||
return update_whenset_FormItemInt(String(key), value, changed);
|
||||
}
|
||||
|
||||
|
||||
bool update_whenset_FormItemInt(const String& key, int8_t& value) {
|
||||
bool update_whenset_FormItemInt(const String& key, int8_t& value,
|
||||
bool* changed) {
|
||||
int tmpVal;
|
||||
|
||||
if (getCheckWebserverArg_int(key, tmpVal)) {
|
||||
if (changed != nullptr && value != tmpVal)
|
||||
*changed = true;
|
||||
value = tmpVal;
|
||||
return true;
|
||||
}
|
||||
@@ -755,16 +767,20 @@ bool update_whenset_FormItemInt(const String& key, int8_t& value) {
|
||||
}
|
||||
|
||||
bool update_whenset_FormItemInt(const __FlashStringHelper * key,
|
||||
uint8_t& value)
|
||||
uint8_t& value,
|
||||
bool* changed)
|
||||
{
|
||||
return update_whenset_FormItemInt(String(key), value);
|
||||
return update_whenset_FormItemInt(String(key), value, changed);
|
||||
}
|
||||
|
||||
|
||||
bool update_whenset_FormItemInt(const String& key, uint8_t& value) {
|
||||
bool update_whenset_FormItemInt(const String& key, uint8_t& value,
|
||||
bool* changed) {
|
||||
int tmpVal;
|
||||
|
||||
if (getCheckWebserverArg_int(key, tmpVal)) {
|
||||
if (changed != nullptr && value != tmpVal)
|
||||
*changed = true;
|
||||
value = tmpVal;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -330,28 +330,36 @@ bool getCheckWebserverArg_int(const String& key,
|
||||
uint32_t & value);
|
||||
|
||||
bool update_whenset_FormItemInt(const __FlashStringHelper * key,
|
||||
int & value);
|
||||
int & value,
|
||||
bool* changed = nullptr);
|
||||
|
||||
bool update_whenset_FormItemInt(const String& key,
|
||||
int & value);
|
||||
int & value,
|
||||
bool* changed = nullptr);
|
||||
|
||||
bool update_whenset_FormItemInt(const __FlashStringHelper * key,
|
||||
uint32_t & value);
|
||||
uint32_t & value,
|
||||
bool* changed = nullptr);
|
||||
|
||||
bool update_whenset_FormItemInt(const String& key,
|
||||
uint32_t & value);
|
||||
uint32_t & value,
|
||||
bool* changed = nullptr);
|
||||
|
||||
bool update_whenset_FormItemInt(const __FlashStringHelper * key,
|
||||
int8_t & value);
|
||||
int8_t & value,
|
||||
bool* changed = nullptr);
|
||||
|
||||
bool update_whenset_FormItemInt(const String& key,
|
||||
int8_t & value);
|
||||
int8_t & value,
|
||||
bool* changed = nullptr);
|
||||
|
||||
bool update_whenset_FormItemInt(const __FlashStringHelper * key,
|
||||
uint8_t & value);
|
||||
uint8_t & value,
|
||||
bool* changed = nullptr);
|
||||
|
||||
bool update_whenset_FormItemInt(const String& key,
|
||||
uint8_t & value);
|
||||
uint8_t & value,
|
||||
bool* changed = nullptr);
|
||||
|
||||
// Note: Checkbox values will not appear in POST Form data if unchecked.
|
||||
// So if webserver does not have an argument for a checkbox form, it means it should be considered unchecked.
|
||||
|
||||
Reference in New Issue
Block a user