2
0
Эх сурвалжийг харах

ESP32: add DNS and SNTP services; prepare for adding lite-uploader

Add and initialize DNS and SNTP service on ESP32; add a submodule
for the lite-uploader Javascript package which handles uploading files
over HTTP POST (as opposed to using HTML form encoding.)
H. Peter Anvin 2 жил өмнө
parent
commit
469f59f53b

+ 3 - 0
.gitmodules

@@ -1,3 +1,6 @@
 [submodule "fw/tools/riscv-gnu-toolchain"]
 	path = tools/riscv-gnu-toolchain
 	url = https://github.com/riscv/riscv-gnu-toolchain
+[submodule "esp32/lite-uploader"]
+	path = esp32/lite-uploader
+	url = https://github.com/burt202/lite-uploader

+ 1 - 0
esp32/lite-uploader

@@ -0,0 +1 @@
+Subproject commit 6844d342e07a8d4c2bd1a485365a66ad23d5bd77

+ 22 - 20
esp32/max80/httpd.c

@@ -54,27 +54,30 @@ INCBIN(wwwzip, "data/www.zip");
 
 struct mime_type {
     const char *ext;
-    size_t ext_len;
+    uint16_t ext_len;
+    uint16_t flags;
     const char *mime;
 };
 
+#define MT_CHARSET	1	/* Add charset to Content-Type */
+
 static const struct mime_type mime_types[] = {
-    { ".html",  5, "text/html"                 },
-    { ".xhtml", 6, "text/html"                 },
-    { ".css",   4, "text/css"                  },
-    { ".webp",  5, "image/webp"                },
-    { ".jpg",   4, "image/jpeg"                },
-    { ".png",   4, "image/png"                 },
-    { ".ico",   4, "image/png"                 }, /* favicon.ico */
-    { ".svg",   4, "image/svg+xml"             },
-    { ".pdf",   4, "application/pdf"           },
-    { ".js",    3, "text/javascript"           },
-    { ".mjs",   4, "text/javascript"           },
-    { ".json",  5, "application/json"          },
-    { ".xml",   4, "text/xml"                  },
-    { ".bin",   4, "application/octet-stream"  },
-    { ".fw",    3, "application/octet-stream"  },
-    { NULL,     0, "text/plain; charset=UTF-8" }  /* default */
+    { ".html",  5, MT_CHARSET, "text/html"                 },
+    { ".xhtml", 6, MT_CHARSET, "text/html"                 },
+    { ".css",   4, MT_CHARSET, "text/css"                  },
+    { ".webp",  5, 0,          "image/webp"                },
+    { ".jpg",   4, 0,          "image/jpeg"                },
+    { ".png",   4, 0,          "image/png"                 },
+    { ".ico",   4, 0,          "image/png"                 }, /* favicon.ico */
+    { ".svg",   4, MT_CHARSET, "image/svg+xml"             },
+    { ".pdf",   4, 0,          "application/pdf"           },
+    { ".js",    3, MT_CHARSET, "text/javascript"           },
+    { ".mjs",   4, MT_CHARSET, "text/javascript"           },
+    { ".json",  5, MT_CHARSET, "application/json"          },
+    { ".xml",   4, MT_CHARSET, "text/xml"                  },
+    { ".bin",   4, 0,          "application/octet-stream"  },
+    { ".fw",    3, 0,          "application/octet-stream"  },
+    { NULL,     0, MT_CHARSET, "text/plain"                }  /* default */
 };
 
 static esp_err_t httpd_static_handler(httpd_req_t *req)
@@ -174,13 +177,14 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
 
     len = snprintf(buffer, buffer_size,
 		   "HTTP/1.1 200 OK\r\n"
-		   "Content-Type: %s\r\n"
+		   "Content-Type: %s%s\r\n"
 		   "Content-Length: %u\r\n"
 		   "Allow: GET, HEAD\r\n"
 		   "Etag: \"%08x:%08x\"\r\n"
 		   "Connection: close\r\n"
 		   "\r\n",
 		   mime_type->mime,
+		   mime_type->flags & MT_CHARSET ? "; charset=\"UTF-8\"" : "",
 		   fileinfo.uncompressed_size,
 		   /*
 		    * Hopefully the combination of date and CRC
@@ -260,7 +264,6 @@ static const httpd_uri_t uri_handlers[] = {
 void my_httpd_stop(void)
 {
     if (httpd) {
-	esp_unregister_shutdown_handler(my_httpd_stop);
 	httpd_stop(httpd);
 	httpd = NULL;
     }
@@ -289,6 +292,5 @@ void my_httpd_start(void)
     for (size_t i = 0; i < ARRAY_SIZE(uri_handlers); i++)
 	httpd_register_uri_handler(httpd, &uri_handlers[i]);
 
-    esp_register_shutdown_handler(my_httpd_stop);
     printf("[HTTP] httpd started\n");
 }

+ 103 - 38
esp32/max80/wifi.cpp

@@ -1,20 +1,106 @@
-#include "Arduino.h"
-#include "ArduinoOTA.h"
+#include "common.h"
 #include "WiFi.h"
 #include "wifi.h"
 #include "storage.h"
 #include "httpd.h"
 
-String ssid        = "";
-String password    = "";
-String otaPassword = "";
-String hostname    = "max80";
+#include <lwip/dns.h>
+#include <lwip/inet.h>
+#include <lwip/apps/sntp.h>
+#include <esp_sntp.h>
 
-bool inOTA = false;
+static String ssid        = "";
+static String password    = "";
+static String otaPassword = "";
+static String hostname    = "max80";
+static String DNSServer   = "";
+static String SNTPServer  = "";
+static String TimeZone    = "CET-1CEST,M3.5.0,M10.5.0/3"; // Sweden
+
+static void sntp_sync_cb(struct timeval *tv)
+{
+    static uint8_t prev_sync_status = SNTP_SYNC_STATUS_RESET;
+    uint8_t sync_status = sntp_get_sync_status();
+    
+    switch (sync_status) {
+    case SNTP_SYNC_STATUS_RESET:
+	sntp_set_sync_mode(SNTP_SYNC_MODE_IMMED); // Until first sync
+	if (prev_sync_status != sync_status) {
+	    printf("[SNTP] time synchronization lost\n");
+	}
+	break;
+    case SNTP_SYNC_STATUS_COMPLETED:
+	sntp_set_sync_mode(SNTP_SYNC_MODE_SMOOTH); // After successful sync
+	if (prev_sync_status != sync_status) {
+	    char timebuf[64];
+	    const struct tm *tm = localtime(&tv->tv_sec);
+	    strftime(timebuf, sizeof timebuf, "%a %Y-%m-%d %H:%M:%S %z", tm);
+	    printf("[SNTP] Time synchronized: %s\n", timebuf);
+	}
+	break;
+    default:
+	break;
+    }
+
+    prev_sync_status = sync_status;
+}
+
+static void my_sntp_start()
+{
+    sntp_set_time_sync_notification_cb(sntp_sync_cb);
+    sntp_setoperatingmode(SNTP_OPMODE_POLL);
+    sntp_servermode_dhcp(1);
+    sntp_set_sync_mode(SNTP_SYNC_MODE_IMMED); // Until first sync
+    sntp_init();
+}
+
+static bool services_started;
+
+static void stop_services(void)
+{
+    if (services_started) {
+	esp_unregister_shutdown_handler(stop_services);
+	my_httpd_stop();
+	services_started = false;
+    }
+}
+
+static inline bool invalid_ip(const ip_addr_t *ip)
+{
+    return !memcmp(ip, &ip_addr_any, sizeof *ip);
+}
 
 static void start_services(void)
 {
-    my_httpd_start();
+    /* Always run after connect */
+    const ip_addr_t *dns_ip = dns_getserver(0);
+    if (invalid_ip(dns_ip)) {
+	/* No DNS server from DHCP? */
+	ip_addr_t addr;
+	if (inet_aton(DNSServer.c_str(), &addr)) {
+	    dns_setserver(0, &addr);
+	}
+    }
+
+    dns_ip = dns_getserver(0);
+    printf("[DNS]  DNS server: %s\n", inet_ntoa(*dns_ip));
+ 
+    // If Arduino supported both of these at the same that would be
+    // awesome, but it requires ESP-IDF reconfiguration...
+    const ip_addr_t *sntp_ip = sntp_getserver(0);
+
+    if (invalid_ip(sntp_ip) && !SNTPServer.isEmpty())
+	sntp_setservername(0, SNTPServer.c_str());
+
+    sntp_ip = sntp_getserver(0);
+    printf("[SNTP] Time server: %s\n", inet_ntoa(*sntp_ip));
+
+    /* Only run on first start */
+    if (!services_started) {
+	services_started = true;
+	my_httpd_start();
+	esp_register_shutdown_handler(stop_services);
+    }
 }
 
 static String wifi_local_ip(void)
@@ -133,11 +219,19 @@ static void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
 }
 
 void SetupWiFi() {
+    services_started = false;
+
     ssid = GetWifiSSID();
     password = GetWifiPassword();
     hostname = GetHostname();
-    inOTA = false;
 
+    if (!TimeZone.isEmpty()) {
+	printf("[INFO] Setting TZ = %s\n", TimeZone.c_str());
+	setenv("TZ", TimeZone.c_str(), 1);
+	tzset();
+    }
+    my_sntp_start();
+    
     printf("[INFO] Setting up WiFi\n");
     printf("[INFO] SSID: %s\n", ssid.c_str());
 
@@ -159,32 +253,3 @@ void SetupWiFi() {
     WiFi.setAutoReconnect(true);
     WiFi.setHostname(hostname.c_str());
 }
-
-void SetupOTA() {
-    // Start OTA server.
-    ArduinoOTA.setHostname((const char *)hostname.c_str());
-
-    ArduinoOTA.onStart([]() {
-	    inOTA = true;
-	    printf("[INFO] OTA Update Start\n");
-	});
-
-    ArduinoOTA.onEnd([]() {
-	    printf("[INFO] OTA Update End\n");
-	});
-
-    ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
-	    printf("[INFO] Progress: %u%%\n", (unsigned int)((progress*100ULL)/total));
-	});
-
-    ArduinoOTA.onError([](ota_error_t error) {
-	    printf("[ERROR] %u: ", error);
-	    if (error == OTA_AUTH_ERROR) printf("Auth failed\n");
-	    else if (error == OTA_BEGIN_ERROR) printf("Start Failed\n");
-	    else if (error == OTA_CONNECT_ERROR) printf("Connection failed\n");
-	    else if (error == OTA_RECEIVE_ERROR) printf("Receive Error\n");
-	    else if (error == OTA_END_ERROR) printf("End Fail\n");
-	});
-
-    ArduinoOTA.begin();
-}

+ 0 - 2
esp32/max80/wifi.h

@@ -1,5 +1,3 @@
 #pragma once
 
 void SetupWiFi();
-void SetupOTA();
-bool InOTA();

BIN
esp32/output/max80.ino.bin


BIN
fpga/output/v1.fw


BIN
fpga/output/v2.fw