Merge branch 'main' into feature/event-log

This commit is contained in:
Cabooman 2024-02-11 11:24:03 +01:00
commit 25cdc7a98a
17 changed files with 400 additions and 231 deletions

View file

@ -37,32 +37,31 @@ const char index_html[] PROGMEM = R"rawliteral(
</html>
)rawliteral";
// Wifi connect time declarations and definition
const unsigned long MAX_WIFI_RECONNECT_BACKOFF_TIME = 60000; // Maximum backoff time of 1 minute
const unsigned long DEFAULT_WIFI_RECONNECT_BACKOFF_TIME =
1000; // Default wifi reconnect backoff time. Start with 1 second
const unsigned long WIFI_CONNECT_TIMEOUT = 10000; // Timeout for WiFi connect in milliseconds
const unsigned long WIFI_MONITOR_LOOP_TIME =
1000; // Will check if WiFi is connected and try reconnect every x milliseconds
unsigned long last_wifi_monitor_run = 0;
unsigned long wifi_connect_start_time;
unsigned long wifi_reconnect_backoff_time = DEFAULT_WIFI_RECONNECT_BACKOFF_TIME;
enum WifiState {
INIT, //before connecting first time
RECONNECTING, //we've connected before, but lost connection
CONNECTED //we are connected
};
enum WiFiState { DISCONNECTED, CONNECTING, CONNECTED };
WifiState wifi_state = INIT;
WiFiState wifi_state =
DISCONNECTED; //the esp library has no specific state to indicate if its connecting (only WL_IDLE_STATUS) so we keep track of it here
unsigned const long WIFI_MONITOR_INTERVAL_TIME = 15000;
unsigned const long INIT_WIFI_CONNECT_TIMEOUT = 8000; // Timeout for initial WiFi connect in milliseconds
unsigned const long DEFAULT_WIFI_RECONNECT_INTERVAL = 1000; // Default WiFi reconnect interval in ms
unsigned const long MAX_WIFI_RETRY_INTERVAL = 30000; // Maximum wifi retry interval in ms
unsigned long last_wifi_monitor_time = millis(); //init millis so wifi monitor doesn't run immediately
unsigned long wifi_reconnect_interval = DEFAULT_WIFI_RECONNECT_INTERVAL;
unsigned long last_wifi_attempt_time = millis(); //init millis so wifi monitor doesn't run immediately
void init_webserver() {
// Configure WiFi
if (AccessPointEnabled) {
WiFi.mode(WIFI_AP_STA); // Simultaneous WiFi AP and Router connection
init_WiFi_AP();
init_WiFi_STA(ssid, password);
} else {
WiFi.mode(WIFI_STA); // Only Router connection
init_WiFi_STA(ssid, password);
}
init_WiFi_STA(ssid, password, wifi_channel);
// Route for root / web page
server.on("/", HTTP_GET,
@ -77,6 +76,11 @@ void init_webserver() {
request->send_P(200, "text/html", index_html, cellmonitor_processor);
});
#ifdef EVENTLOGGING
server.on("/events", HTTP_GET,
[](AsyncWebServerRequest* request) { request->send_P(200, "text/html", index_html, events_processor); });
#endif
// Route for editing Wh
server.on("/updateBatterySize", HTTP_GET, [](AsyncWebServerRequest* request) {
if (request->hasParam("value")) {
@ -256,120 +260,77 @@ void init_webserver() {
#endif
}
void print_wifi_status_message(wl_status_t status) {
switch (status) {
case WL_CONNECTED:
Serial.println("Connected to WiFi network: " + String(ssid));
Serial.println("IP address: " + WiFi.localIP().toString());
Serial.println("Signal Strength: " + String(WiFi.RSSI()) + " dBm");
break;
case WL_CONNECT_FAILED:
Serial.println("Failed to connect to WiFi network: " + String(ssid));
break;
case WL_CONNECTION_LOST:
Serial.println("Connection to WiFi network: " + String(ssid) + " lost");
break;
case WL_DISCONNECTED:
Serial.println("Disconnected from WiFi network: " + String(ssid));
break;
case WL_NO_SSID_AVAIL:
Serial.println("Could not find network with SSID: " + String(ssid));
break;
case WL_IDLE_STATUS:
Serial.println("WiFi is in idle status. This can indicate it is currently trying to connect.");
break;
case WL_SCAN_COMPLETED:
Serial.println("WiFi scan completed");
break;
case WL_NO_SHIELD:
Serial.println("No WiFi shield detected");
break;
default:
Serial.println("Unknown WiFi status: " + String(status));
break;
}
}
// Function to handle WiFi reconnection. Use some timeouts and backoffs here to avoid flooding reconnection attempts/spamming the serial console
void handle_WiFi_reconnection(unsigned long currentMillis, wl_status_t status) {
if (wifi_state == CONNECTING && currentMillis - wifi_connect_start_time > WIFI_CONNECT_TIMEOUT) {
// we are here if we were trying to connect to wifi, but it took too long (more than configured timeout)
Serial.println("Failed to connect to WiFi network before timeout");
print_wifi_status_message(status);
WiFi.disconnect(); //disconnect to clear any previous settings
wifi_state = DISCONNECTED;
wifi_connect_start_time = currentMillis; //reset the start time to now so backoff is respected on next try
// We use a backoff time before trying to connect again. Increase backoff time, up to a maximum
wifi_reconnect_backoff_time = min(wifi_reconnect_backoff_time * 2, MAX_WIFI_RECONNECT_BACKOFF_TIME);
Serial.println("Will try again in " + String(wifi_reconnect_backoff_time / 1000) + " seconds.");
} else if (wifi_state != CONNECTING && currentMillis - wifi_connect_start_time > wifi_reconnect_backoff_time) {
// we are here if the connection failed for some reason and the backoff time has now passed
print_wifi_status_message(status);
init_WiFi_STA(ssid, password);
}
}
// Function to handle WiFi connection
void WiFi_monitor_loop() {
unsigned long currentMillis = millis();
if (currentMillis - last_wifi_monitor_run > WIFI_MONITOR_LOOP_TIME) {
last_wifi_monitor_run = currentMillis;
wl_status_t status = WiFi.status();
switch (status) {
case WL_CONNECTED:
if (wifi_state != CONNECTED) { //we need to update our own wifi state to indicate we are connected
wifi_reconnect_backoff_time =
DEFAULT_WIFI_RECONNECT_BACKOFF_TIME; // Reset backoff time after maintaining connection
wifi_state = CONNECTED;
print_wifi_status_message(status);
}
break;
case WL_CONNECT_FAILED:
case WL_CONNECTION_LOST:
case WL_DISCONNECTED:
case WL_NO_SSID_AVAIL:
handle_WiFi_reconnection(currentMillis, status);
break;
case WL_IDLE_STATUS: //this means the wifi is not ready to process any commands (it's probably trying to connect). do nothing
case WL_SCAN_COMPLETED: //this will only be set when scanning for networks. We don't do that yet
case WL_NO_SHIELD: //should not happen, this means no wifi chip detected, so we can't do much
break;
}
}
}
// Function to initialize WiFi in Station Mode (i.e. connect to another access point)
void init_WiFi_STA(const char* ssid, const char* password) {
Serial.println("Connecting to: " + String(ssid));
wifi_state = CONNECTING;
WiFi.begin(ssid, password);
WiFi.setAutoReconnect(true);
wifi_connect_start_time = millis();
}
// Function to convert WiFiState enum to String
String wifi_state_to_string(WiFiState state) {
switch (state) {
case DISCONNECTED:
return "Disconnected";
case CONNECTING:
return "Connecting";
case CONNECTED:
return "Connected";
default:
return "Unknown";
}
}
// Function to initialize WiFi in Access Point Mode
void init_WiFi_AP() {
Serial.println("Creating Access Point: " + String(ssidAP));
Serial.println("With password: " + String(passwordAP));
WiFi.softAP(ssidAP, passwordAP);
IPAddress IP = WiFi.softAPIP();
Serial.println("Access Point created.");
Serial.println("IP address: " + IP.toString());
Serial.print("IP address: ");
Serial.println(IP);
}
String getConnectResultString(wl_status_t status) {
switch (status) {
case WL_CONNECTED:
return "Connected";
case WL_NO_SHIELD:
return "No shield";
case WL_IDLE_STATUS:
return "Idle status";
case WL_NO_SSID_AVAIL:
return "No SSID available";
case WL_SCAN_COMPLETED:
return "Scan completed";
case WL_CONNECT_FAILED:
return "Connect failed";
case WL_CONNECTION_LOST:
return "Connection lost";
case WL_DISCONNECTED:
return "Disconnected";
default:
return "Unknown";
}
}
void wifi_monitor() {
unsigned long currentMillis = millis();
if (currentMillis - last_wifi_monitor_time > WIFI_MONITOR_INTERVAL_TIME) {
last_wifi_monitor_time = currentMillis;
wl_status_t status = WiFi.status();
if (status != WL_CONNECTED && status != WL_IDLE_STATUS) {
Serial.println(getConnectResultString(status));
if (wifi_state == INIT) { //we haven't been connected yet, try the init logic
init_WiFi_STA(ssid, password, wifi_channel);
} else { //we were connected before, try the reconnect logic
if (currentMillis - last_wifi_attempt_time > wifi_reconnect_interval) {
last_wifi_attempt_time = currentMillis;
Serial.println("WiFi not connected, trying to reconnect...");
wifi_state = RECONNECTING;
WiFi.reconnect();
wifi_reconnect_interval = min(wifi_reconnect_interval * 2, MAX_WIFI_RETRY_INTERVAL);
}
}
} else if (status == WL_CONNECTED && wifi_state != CONNECTED) {
wifi_state = CONNECTED;
wifi_reconnect_interval = DEFAULT_WIFI_RECONNECT_INTERVAL;
// Print local IP address and start web server
Serial.print("Connected to WiFi network: " + String(ssid));
Serial.print(" IP address: " + WiFi.localIP().toString());
Serial.print(" Signal Strength: " + String(WiFi.RSSI()) + " dBm");
Serial.println(" Channel: " + String(WiFi.channel()));
Serial.println(" Hostname: " + String(WiFi.getHostname()));
}
}
}
void init_WiFi_STA(const char* ssid, const char* password, const uint8_t wifi_channel) {
// Connect to Wi-Fi network with SSID and password
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password, wifi_channel);
WiFi.setAutoReconnect(true); // Enable auto reconnect
wl_status_t result = static_cast<wl_status_t>(WiFi.waitForConnectResult(INIT_WIFI_CONNECT_TIMEOUT));
}
// Function to initialize ElegantOTA
@ -393,7 +354,7 @@ String processor(const String& var) {
content += "<div style='background-color: #303E47; padding: 10px; margin-bottom: 10px;border-radius: 50px'>";
// Show version number
content += "<h4>Software version: " + String(versionNumber) + "</h4>";
content += "<h4>Software version: " + String(version_number) + "</h4>";
// Display LED color
content += "<h4>LED color: ";
@ -416,13 +377,15 @@ String processor(const String& var) {
default:
break;
}
wl_status_t status = WiFi.status();
// Display ssid of network connected to and, if connected to the WiFi, its own IP
content += "<h4>SSID: " + String(ssid) + "</h4>";
content += "<h4>Wifi status: " + wifi_state_to_string(wifi_state) + "</h4>";
if (WiFi.status() == WL_CONNECTED) {
content += "<h4>Wifi status: " + getConnectResultString(status) + "</h4>";
if (status == WL_CONNECTED) {
content += "<h4>IP: " + WiFi.localIP().toString() + "</h4>";
// Get and display the signal strength (RSSI)
content += "<h4>Signal Strength: " + String(WiFi.RSSI()) + " dBm</h4>";
content += "<h4>Channel: " + String(WiFi.channel()) + "</h4>";
}
// Close the block
content += "</div>";
@ -660,11 +623,16 @@ String processor(const String& var) {
content += " ";
content += "<button onclick='goToCellmonitorPage()'>Cellmonitor</button>";
content += " ";
content += "<button onclick='goToEventsPage()'>Events</button>";
content += " ";
content += "<button onclick='promptToReboot()'>Reboot Emulator</button>";
content += "<script>";
content += "function goToUpdatePage() { window.location.href = '/update'; }";
content += "function goToCellmonitorPage() { window.location.href = '/cellmonitor'; }";
content += "function goToSettingsPage() { window.location.href = '/settings'; }";
#ifdef EVENTLOGGING
content += "function goToEventsPage() { window.location.href = '/events'; }";
#endif
content +=
"function promptToReboot() { if (window.confirm('Are you sure you want to reboot the emulator? NOTE: If "
"emulator is handling contactors, they will open during reboot!')) { "
@ -976,6 +944,58 @@ String cellmonitor_processor(const String& var) {
return String();
}
#ifdef EVENTLOGGING
const char EVENTS_HTML_START[] PROGMEM = R"=====(
<style>
body { background-color: black; color: white; }
.event-log { display: flex; flex-direction: column; }
.event { display: flex; flex-wrap: wrap; border: 1px solid white; padding: 10px; }
.event > div { flex: 1; min-width: 100px; max-width: 90%; word-break: break-word; }
</style>
<div style='background-color: #303E47; padding: 10px; margin-bottom: 10px;border-radius: 50px'>
<h4 style='color: white;'>Event log:</h4>
<div class="event-log">
<div class="event">
<div>Event Type</div><div>LED Color</div><div>Last Event (seconds ago)</div><div>Count</div><div>Data</div><div>Message</div>
</div>
)=====";
const char EVENTS_HTML_END[] PROGMEM = R"=====(
</div></div>
<button onclick='goToMainPage()'>Back to main page</button>
<script>
function goToMainPage() {
window.location.href = '/';
}
</script>
)=====";
String events_processor(const String& var) {
if (var == "PLACEHOLDER") {
String content = "";
content.reserve(5000);
// Page format
content.concat(FPSTR(EVENTS_HTML_START));
for (int i = 0; i < EVENT_NOF_EVENTS; i++) {
Serial.println("Event: " + String(get_event_enum_string(static_cast<EVENTS_ENUM_TYPE>(i))) +
" count: " + String(entries[i].occurences) + " seconds: " + String(entries[i].timestamp) +
" data: " + String(entries[i].data));
if (entries[i].occurences > 0) {
content.concat("<div class='event'>");
content.concat("<div>" + String(get_event_enum_string(static_cast<EVENTS_ENUM_TYPE>(i))) + "</div>");
content.concat("<div>" + String(get_led_color_display_text(entries[i].led_color)) + "</div>");
content.concat("<div>" + String((millis() / 1000) - entries[i].timestamp) + "</div>");
content.concat("<div>" + String(entries[i].occurences) + "</div>");
content.concat("<div>" + String(entries[i].data) + "</div>");
content.concat("<div>" + String(get_event_message(static_cast<EVENTS_ENUM_TYPE>(i))) + "</div>");
content.concat("</div>"); // End of event row
}
}
content.concat(FPSTR(EVENTS_HTML_END));
return content;
}
return String();
}
#endif
void onOTAStart() {
// Log when OTA has started
ESP32Can.CANStop();