diff --git a/Firmware/RTK_Everywhere/Developer.ino b/Firmware/RTK_Everywhere/Developer.ino index e5086a7d..9e8d712f 100644 --- a/Firmware/RTK_Everywhere/Developer.ino +++ b/Firmware/RTK_Everywhere/Developer.ino @@ -210,7 +210,7 @@ void wifiDisplayNetworkData() {} void wifiDisplaySoftApStatus() {} bool wifiEspNowOff(const char * fileName, uint32_t lineNumber) {return true;} bool wifiEspNowOn(const char * fileName, uint32_t lineNumber) {return false;} -void wifiEspNowSetChannel(WIFI_CHANNEL_t channel) {} +void wifiEspNowChannelSet(WIFI_CHANNEL_t channel) {} int wifiNetworkCount() {return 0;} void wifiResetTimeout() {} IPAddress wifiSoftApGetIpAddress() {return IPAddress((uint32_t)0);} diff --git a/Firmware/RTK_Everywhere/HTTP_Client.ino b/Firmware/RTK_Everywhere/HTTP_Client.ino index 0421f9cd..1ed2cee8 100644 --- a/Firmware/RTK_Everywhere/HTTP_Client.ino +++ b/Firmware/RTK_Everywhere/HTTP_Client.ino @@ -356,6 +356,7 @@ void httpClientUpdate() // Connect to the HTTP server case HTTP_CLIENT_CONNECTING_2_SERVER: { // Allocate the httpSecureClient structure + networkUseDefaultInterface(); httpSecureClient = new NetworkClientSecure(); if (!httpSecureClient) { @@ -371,6 +372,17 @@ void httpClientUpdate() if (!httpSecureClient->connect(THINGSTREAM_SERVER, HTTPS_PORT)) { // Failed to connect to the server + int length = 1024; + char * errMessage = (char *)rtkMalloc(length, "HTTP error message"); + if (errMessage) + { + memset(errMessage, 0, length); + httpSecureClient->lastError(errMessage, length - 1); + systemPrintf("Get %s failed, %s\r\n", THINGSTREAM_SERVER, errMessage); + rtkFree(errMessage, "HTTP error message"); + } + else + systemPrintf("Get %s failed!\r\n", THINGSTREAM_SERVER); systemPrintln("ERROR: Failed to connect to the Thingstream server!"); httpClientRestart(); // I _think_ we want to restart here - i.e. retry after the timeout? break; diff --git a/Firmware/RTK_Everywhere/MQTT_Client.ino b/Firmware/RTK_Everywhere/MQTT_Client.ino index 9ebab6b9..879f53fb 100644 --- a/Firmware/RTK_Everywhere/MQTT_Client.ino +++ b/Firmware/RTK_Everywhere/MQTT_Client.ino @@ -876,6 +876,7 @@ void mqttClientUpdate() // Connect to the MQTT broker case MQTT_CLIENT_CONNECTING_2_BROKER: { // Allocate the mqttSecureClient structure + networkUseDefaultInterface(); mqttSecureClient = new NetworkClientSecure(); if (!mqttSecureClient) { @@ -951,6 +952,16 @@ void mqttClientUpdate() // Attempt connection to the MQTT broker if (!mqttClient->connect(settings.pointPerfectBrokerHost, 8883)) { + // Failed to connect to the server + int length = 1024; + char * errMessage = (char *)rtkMalloc(length, "HTTP error message"); + if (errMessage) + { + memset(errMessage, 0, length); + mqttSecureClient->lastError(errMessage, length - 1); + systemPrintf("MQTT Error: %s\r\n", errMessage); + rtkFree(errMessage, "HTTP error message"); + } systemPrintf("Failed to connect to MQTT broker %s\r\n", settings.pointPerfectBrokerHost); mqttClientRestart(); break; diff --git a/Firmware/RTK_Everywhere/Network.ino b/Firmware/RTK_Everywhere/Network.ino index 9e1b0c2b..c06b25c5 100644 --- a/Firmware/RTK_Everywhere/Network.ino +++ b/Firmware/RTK_Everywhere/Network.ino @@ -1269,6 +1269,37 @@ void networkInterfaceInternetConnectionAvailable(NetIndex_t index) networkMulticastDNSStart(previousIndex); } +/* + Network Loss Handling: + + Arduino IP lost event + | + | + V + networkInterfaceEventInternetLost + | + | Set internet lost event flag + V + networkUpdate + | + | Clear internet lost event flag + V + +<------- Fake connection loss + | + V + networkInterfaceInternetConnectionLost + | + | Notify Interface of connection loss + V + .----------------+----------------. + | | + | | + V V + networkInterfaceRunning Interface stop sequence + called by xxxUpdate + or xxxEnabled +*/ + //---------------------------------------- // Mark network interface as having NO access to the internet //---------------------------------------- @@ -1377,6 +1408,35 @@ void networkInterfaceInternetConnectionLost(NetIndex_t index) } } +//---------------------------------------- +// Get the interface priority +//---------------------------------------- +NetPriority_t networkInterfacePriority(NetIndex_t index) +{ + NetPriority_t priority; + + // Validate the index + networkValidateIndex(index); + + // Get the interface priority + priority = networkIndexTable[index]; + return priority; +} + +//---------------------------------------- +// Determine if the interface should be running +//---------------------------------------- +NetPriority_t networkInterfaceRunning(NetIndex_t index) +{ + NetPriority_t priority; + + // Get the interface priority + priority = networkInterfacePriority(index); + + // Return the running status + return (networkPriority >= priority); +} + //---------------------------------------- // Determine if the specified network interface is higher priority than // the current network interface @@ -2434,6 +2494,25 @@ void networkUpdate() } } +//---------------------------------------- +// Set the default network interface +//---------------------------------------- +void networkUseDefaultInterface() +{ + NetIndex_t index; + bool isDefault; + + // Get the network index + index = networkGetCurrentInterfaceIndex(); + if (index < NETWORK_OFFLINE) + { + // Get the default network interface + isDefault = networkInterfaceTable[index].netif->isDefault(); + if (!isDefault) + networkInterfaceTable[index].netif->setDefault(); + } +} + //---------------------------------------- // Add a network user //---------------------------------------- diff --git a/Firmware/RTK_Everywhere/NtripClient.ino b/Firmware/RTK_Everywhere/NtripClient.ino index 24049882..a686367d 100644 --- a/Firmware/RTK_Everywhere/NtripClient.ino +++ b/Firmware/RTK_Everywhere/NtripClient.ino @@ -656,6 +656,7 @@ void ntripClientUpdate() if (connected) { // Allocate the ntripClient structure + networkUseDefaultInterface(); ntripClient = new NetworkClient(); if (!ntripClient) { diff --git a/Firmware/RTK_Everywhere/TcpClient.ino b/Firmware/RTK_Everywhere/TcpClient.ino index 0e67fdd9..1e2ff270 100644 --- a/Firmware/RTK_Everywhere/TcpClient.ino +++ b/Firmware/RTK_Everywhere/TcpClient.ino @@ -306,6 +306,7 @@ bool tcpClientStart() NetworkClient *client; // Allocate the TCP client + networkUseDefaultInterface(); client = new NetworkClient(); if (client) { diff --git a/Firmware/RTK_Everywhere/WiFi.ino b/Firmware/RTK_Everywhere/WiFi.ino index 759fe9f2..bb5106a7 100644 --- a/Firmware/RTK_Everywhere/WiFi.ino +++ b/Firmware/RTK_Everywhere/WiFi.ino @@ -24,7 +24,7 @@ enum WIFI_STATION_STATES { WIFI_STATION_STATE_OFF, WIFI_STATION_STATE_WAIT_NO_USERS, - WIFI_STATION_STATE_RESTART, + WIFI_STATION_STATE_RESTART_DELAY, WIFI_STATION_STATE_STARTING, WIFI_STATION_STATE_ONLINE, WIFI_STATION_STATE_STABLE, @@ -33,9 +33,14 @@ enum WIFI_STATION_STATES }; uint8_t wifiStationState; -const char *wifiStationStateName[] = { - "WIFI_STATION_STATE_OFF", "WIFI_STATION_STATE_WAIT_NO_USERS", "WIFI_STATION_STATE_RESTART", - "WIFI_STATION_STATE_STARTING", "WIFI_STATION_STATE_ONLINE", "WIFI_STATION_STATE_STABLE", +const char * wifiStationStateName[] = +{ + "WIFI_STATION_STATE_OFF", + "WIFI_STATION_STATE_WAIT_NO_USERS", + "WIFI_STATION_STATE_RESTART_DELAY", + "WIFI_STATION_STATE_STARTING", + "WIFI_STATION_STATE_ONLINE", + "WIFI_STATION_STATE_STABLE", }; const int wifiStationStateNameEntries = sizeof(wifiStationStateName) / sizeof(wifiStationStateName[0]); @@ -535,11 +540,18 @@ void wifiDisplayState() } } +//********************************************************************* +// Get the ESP-NOW channel +WIFI_CHANNEL_t wifiEspNowChannelGet() +{ + return wifi.espNowChannelGet(); +} + //********************************************************************* // Set the ESP-NOW channel -void wifiEspNowSetChannel(WIFI_CHANNEL_t channel) +void wifiEspNowChannelSet(WIFI_CHANNEL_t channel) { - wifi.espNowSetChannel(channel); + wifi.espNowChannelSet(channel); } //********************************************************************* @@ -638,6 +650,20 @@ void wifiPromiscuousRxHandler(void *buf, wifi_promiscuous_pkt_type_t type) packetRSSI = ppkt->rx_ctrl.rssi; } +//********************************************************************* +// Get the soft AP channel +WIFI_CHANNEL_t wifiSoftApChannelGet() +{ + return wifi.softApChannelGet(); +} + +//********************************************************************* +// Set the soft AP channel +void wifiSoftApChannelSet(WIFI_CHANNEL_t channel) +{ + wifi.softApChannelSet(channel); +} + //********************************************************************* // Get the IP address being used for the software access point (AP) // Outputs: @@ -741,9 +767,10 @@ bool wifiStationEnabled(const char **reason) break; } - // Is WiFi the highest priority - if (networkIsHighestPriority(NETWORK_WIFI_STATION) == false) + // Determine if WiFi should be running (is the highest priority) + if (networkInterfaceRunning(NETWORK_WIFI_STATION) == false) { + // Another network has higher priority // Allocate the reason buffer once if (reasonBuffer == nullptr) reasonBuffer = (char *)rtkMalloc(64, "WiFi reasonBuffer"); @@ -763,7 +790,7 @@ bool wifiStationEnabled(const char **reason) // WiFi should start and continue running enabled = true; - *reason = ", is enabled"; + *reason = ""; } while (0); return enabled; } @@ -859,7 +886,71 @@ void wifiStationUpdate() static uint32_t timer; int users; - // Determine if WiFi station should stop +/* + WiFi Station States: + + WIFI_STATION_STATE_OFF <--------------+<---. + | ^ | + | enabled | | + | | | + V !enabled | | + WIFI_STATION_STATE_RESTART_DELAY ----------' | + | | + | Timeout | + | Complete | + V !enabled | + WIFI_STATION_STATE_STARTING -------------. | + | | | + | WiFi connected | | + V !enabled | | + WIFI_STATION_STATE_ONLINE ------------->+ | + | ^ | + | Long delay | | + V | | + WIFI_STATION_STATE_STABLE | | + | | | + | !enabled | | + V | | + +<--------------------------' | + | | + V | + WIFI_STATION_STATE_WAIT_NO_USERS | + | | + | No Users | + | | + '--------------------------------' + + Network Loss Handling: + + ARDUINO_EVENT_WIFI_STA_LOST_IP + | + | + V + networkInterfaceEventInternetLost + | + | Set internet lost event flag + V + networkUpdate + | + | Clear internet lost event flag + V + +<------- Fake connection loss + | + V + networkInterfaceInternetConnectionLost + | + | Notify Interface of connection loss + V + .----------------+----------------. + | | + | | + V V + networkInterfaceRunning Interface stop sequence + in wifiStationEnabled + +*/ + + // Determine if WiFi station should start or stop enabled = wifiStationEnabled(&reason); online = wifiStationOnline; if ((enabled == false) && (wifiStationState >= WIFI_STATION_STATE_STARTING)) @@ -888,24 +979,6 @@ void wifiStationUpdate() // Update the WiFi station state switch (wifiStationState) { - // There are no WiFi station consumers - case WIFI_STATION_STATE_OFF: - if (enabled) - { - connectionAttempts = 0; - timer = millis(); - startTimeout = 0; - - // Display the major state transition - if (settings.debugWifiState) - systemPrintf("--------------- %s Starting ---------------\r\n", - networkInterfaceTable[NETWORK_WIFI_STATION].name); - - // Start WiFi station - wifiStationSetState(WIFI_STATION_STATE_STARTING); - } - break; - // Wait for WiFi station users to release resources before shutting // down WiFi station case WIFI_STATION_STATE_WAIT_NO_USERS: @@ -928,81 +1001,95 @@ void wifiStationUpdate() // No more network users else { + // Display the major state transition + if (wifiStationRunning) + { + if (settings.debugWifiState) + systemPrintf("--------------- %s Stopping ---------------\r\n", + networkInterfaceTable[NETWORK_WIFI_STATION].name); + wifiStationOff(__FILE__, __LINE__); + } + // Stop WiFi station if necessary if (enabled == false) - { - // Display the major state transition - if (wifiStationRunning) - { - if (settings.debugWifiState) - systemPrintf("--------------- %s Stopping ---------------\r\n", - networkInterfaceTable[NETWORK_WIFI_STATION].name); - wifiStationOff(__FILE__, __LINE__); - } + // Reset the start timeout wifiStationSetState(WIFI_STATION_STATE_OFF); - } // Restart WiFi after delay else { - // Clear the bits to perform the restart operation - wifi.clearStarted(WIFI_STA_RECONNECT); - wifiStationSetState(WIFI_STATION_STATE_RESTART); + // Display the restart delay and then start WiFi station + if (startTimeout && settings.debugWifiState) + { + // Display the delay + uint32_t seconds = startTimeout / MILLISECONDS_IN_A_SECOND; + uint32_t minutes = seconds / SECONDS_IN_A_MINUTE; + seconds -= minutes * SECONDS_IN_A_MINUTE; + systemPrintf("WiFi: Delaying %2d:%02d before restarting WiFi\r\n", minutes, seconds); + } + timer = millis(); + wifiStationSetState(WIFI_STATION_STATE_RESTART_DELAY); } } break; - // Display the restart delay and then start WiFi station - case WIFI_STATION_STATE_RESTART: - if (startTimeout && settings.debugWifiState) + // There are no WiFi station consumers + case WIFI_STATION_STATE_OFF: + // Check for disabled + if (!enabled) + break; + + // Reset the restart timeout when off + if (wifiStationState == WIFI_STATION_STATE_OFF) { - // Display the delay - uint32_t seconds = startTimeout / MILLISECONDS_IN_A_SECOND; - uint32_t minutes = seconds / SECONDS_IN_A_MINUTE; - seconds -= minutes * SECONDS_IN_A_MINUTE; - systemPrintf("WiFi: Delaying %2d:%02d before restarting WiFi\r\n", minutes, seconds); + connectionAttempts = 0; + timer = millis(); + startTimeout = 0; } - timer = millis(); - wifiStationSetState(WIFI_STATION_STATE_STARTING); - break; - // At least one consumer is requesting a network - case WIFI_STATION_STATE_STARTING: - // Delay before starting WiFi - if ((millis() - timer) >= startTimeout) + // Wait for the delay to complete + wifiStationSetState(WIFI_STATION_STATE_RESTART_DELAY); + + // | + // | Fall through + // V + + // Perform the restart delay + case WIFI_STATION_STATE_RESTART_DELAY: + // Stop WiFi station if necessary + if (enabled == false) { - timer = millis(); + wifiStationSetState(WIFI_STATION_STATE_OFF); + break; + } - // Increase the timeout - startTimeout <<= 1; - if (!startTimeout) - startTimeout = WIFI_MIN_TIMEOUT; - else if (startTimeout > WIFI_MAX_TIMEOUT) - startTimeout = WIFI_MAX_TIMEOUT; + // Delay before starting WiFi + if ((millis() - timer) < startTimeout) + break; - // Account for this connection attempt - connectionAttempts++; + // Display the major state transition + if (settings.debugWifiState) + systemPrintf("--------------- %s Starting ---------------\r\n", + networkInterfaceTable[NETWORK_WIFI_STATION].name); - // Attempt to start WiFi station - if (wifiStationOn(__FILE__, __LINE__)) - { - // Successfully connected to a remote AP - if (settings.debugWifiState) - systemPrintf("WiFi: WiFi station successfully started\r\n"); + // Timeout complete + wifiStationSetState(WIFI_STATION_STATE_STARTING); - // WiFi station is now available - wifiStationSetState(WIFI_STATION_STATE_ONLINE); - } - else - { - // Failed to connect to a remote AP - if (settings.debugWifiState) - systemPrintf("WiFi: WiFi station failed to start!\r\n"); + // | + // | Fall through + // V + + // At least one consumer is requesting a network + case WIFI_STATION_STATE_STARTING: + // Increase the timeout + startTimeout <<= 1; + if (!startTimeout) + startTimeout = WIFI_MIN_TIMEOUT; + else if (startTimeout > WIFI_MAX_TIMEOUT) + startTimeout = WIFI_MAX_TIMEOUT; - // Restart WiFi after delay - // Clear the bits to perform the restart operation - wifi.clearStarted(WIFI_STA_RECONNECT); - wifiStationSetState(WIFI_STATION_STATE_RESTART); + // Account for this connection attempt + connectionAttempts++; // Start the next network interface if necessary if (connectionAttempts >= 2) @@ -1016,6 +1103,7 @@ void wifiStationUpdate() // Wait until the WiFi link is stable if ((millis() - timer) >= WIFI_CONNECTION_STABLE_MSEC) { + // Reset restart timeout and the connection attempts connectionAttempts = 0; startTimeout = 0; wifiStationSetState(WIFI_STATION_STATE_STABLE); @@ -1303,23 +1391,32 @@ bool RTK_WIFI::enable(bool enableESPNow, bool enableSoftAP, bool enableStation, } //********************************************************************* -// Get the ESP-NOW status +// Get the ESP-NOW channel // Outputs: -// Returns true when ESP-NOW is online and ready for use -bool RTK_WIFI::espNowOnline() +// Returns the requested ESP-NOW channel +WIFI_CHANNEL_t RTK_WIFI::espNowChannelGet() { - return (_started & WIFI_EN_ESP_NOW_ONLINE) ? true : false; + return _espNowChannel; } //********************************************************************* // Set the ESP-NOW channel // Inputs: // channel: New ESP-NOW channel number -void RTK_WIFI::espNowSetChannel(WIFI_CHANNEL_t channel) +void RTK_WIFI::espNowChannelSet(WIFI_CHANNEL_t channel) { _espNowChannel = channel; } +//********************************************************************* +// Get the ESP-NOW status +// Outputs: +// Returns true when ESP-NOW is online and ready for use +bool RTK_WIFI::espNowOnline() +{ + return (_started & WIFI_EN_ESP_NOW_ONLINE) ? true : false; +} + //********************************************************************* // Handle the WiFi event void RTK_WIFI::eventHandler(arduino_event_id_t event, arduino_event_info_t info) @@ -1551,6 +1648,24 @@ bool RTK_WIFI::setWiFiProtocols(wifi_interface_t interface, bool enableWiFiProto return started; } +//********************************************************************* +// Get the soft AP channel +// Outputs: +// Returns the requested soft AP channel +WIFI_CHANNEL_t RTK_WIFI::softApChannelGet() +{ + return _apChannel; +} + +//********************************************************************* +// Set the soft AP channel +// Inputs: +// channel: Request the channel for WiFi soft AP +void RTK_WIFI::softApChannelSet(WIFI_CHANNEL_t channel) +{ + _apChannel = channel; +} + //********************************************************************* // Configure the soft AP // Inputs: @@ -1787,6 +1902,20 @@ bool RTK_WIFI::startAp(bool forceAP) return enable(wifiEspNowRunning, forceAP | settings.wifiConfigOverAP, wifiStationRunning, __FILE__, __LINE__); } +//********************************************************************* +// Get the station channel +WIFI_CHANNEL_t RTK_WIFI::stationChannelGet() +{ + return _stationChannel; +} + +//********************************************************************* +// Set the station channel +void RTK_WIFI::stationChannelSet(WIFI_CHANNEL_t channel) +{ + _stationChannel = channel; +} + //********************************************************************* // Connect the station to a remote AP // Return true if the connection was successful and false upon failure. diff --git a/Firmware/RTK_Everywhere/menuMain.ino b/Firmware/RTK_Everywhere/menuMain.ino index a7172de8..ae07aa69 100644 --- a/Firmware/RTK_Everywhere/menuMain.ino +++ b/Firmware/RTK_Everywhere/menuMain.ino @@ -689,7 +689,7 @@ void menuRadio() if (getNewSetting("Enter the WiFi channel to use for ESP-NOW communication", 1, 14, &settings.wifiChannel) == INPUT_RESPONSE_VALID) { - wifiEspNowSetChannel(settings.wifiChannel); + wifiEspNowChannelSet(settings.wifiChannel); if (settings.wifiChannel) { if (settings.wifiChannel == wifiChannel) diff --git a/Firmware/RTK_Everywhere/settings.h b/Firmware/RTK_Everywhere/settings.h index 98550f0a..1d32443b 100644 --- a/Firmware/RTK_Everywhere/settings.h +++ b/Firmware/RTK_Everywhere/settings.h @@ -2185,15 +2185,20 @@ class RTK_WIFI const char * fileName, int lineNumber); - // Get the ESP-NOW status + // Get the ESP-NOW channel // Outputs: - // Returns true when ESP-NOW is online and ready for use - bool espNowOnline(); + // Returns the requested ESP-NOW channel + WIFI_CHANNEL_t espNowChannelGet(); // Set the ESP-NOW channel // Inputs: // channel: New ESP-NOW channel number - void espNowSetChannel(WIFI_CHANNEL_t channel); + void espNowChannelSet(WIFI_CHANNEL_t channel); + + // Get the ESP-NOW status + // Outputs: + // Returns true when ESP-NOW is online and ready for use + bool espNowOnline(); // Handle the WiFi event // Inputs: @@ -2208,6 +2213,16 @@ class RTK_WIFI // Returns the current WiFi channel number WIFI_CHANNEL_t getChannel(); + // Get the soft AP channel + // Outputs: + // Returns the requested soft AP channel + WIFI_CHANNEL_t softApChannelGet(); + + // Set the soft AP channel + // Inputs: + // channel: Request the channel for WiFi soft AP + void softApChannelSet(WIFI_CHANNEL_t channel); + // Configure the soft AP // Inputs: // ipAddress: IP address of the soft AP @@ -2246,6 +2261,16 @@ class RTK_WIFI // otherwise bool startAp(bool forceAP); + // Get the station channel + // Outputs: + // Returns the requested station channel + WIFI_CHANNEL_t stationChannelGet(); + + // Set the station channel + // Inputs: + // channel: Request the channel for WiFi station + void stationChannelSet(WIFI_CHANNEL_t channel); + // Get the WiFi station IP address // Returns the IP address of the WiFi station IPAddress stationIpAddress();