());
+#endif
+ }
+ }
+ }
+}
+
+void webHandleRoot()
+{
+ if(!httpIsAuthenticated(F("root"))) return;
+
+ saveConfig();
+ {
+ String httpMessage((char*)0);
+ httpMessage.reserve(HTTP_PAGE_SIZE);
+ httpMessage += F("");
+ httpMessage += httpGetNodename();
+ httpMessage += F("
");
+
+ httpMessage += F("");
+
+ httpMessage += F("");
+ httpMessage +=
+ F("");
+ add_form_button(httpMessage, F(D_HTTP_CONFIGURATION), F("/config"), F(""));
+ // httpMessage += F("");
+
+ httpMessage += F("");
+
+#if HASP_USE_SPIFFS > 0 || HASP_USE_LITTLEFS > 0
+ if(HASP_FS.exists(F("/edit.htm.gz"))) {
+ httpMessage +=
+ F("");
+ }
+#endif
+
+ httpMessage += F("");
+
+ webSendPage(httpGetNodename(), httpMessage.length(), false);
+ webServer.sendContent(httpMessage);
+ }
+ // httpMessage.clear();
+ webSendFooter();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+void httpHandleReboot()
+{ // http://plate01/reboot
+ if(!httpIsAuthenticated(F("reboot"))) return;
+
+ {
+ String httpMessage((char*)0);
+ httpMessage.reserve(HTTP_PAGE_SIZE);
+ httpMessage += F("");
+ httpMessage += httpGetNodename();
+ httpMessage += F("
");
+ httpMessage = F(D_DISPATCH_REBOOT);
+
+ webSendPage(httpGetNodename(), httpMessage.length(), true);
+ webServer.sendContent(httpMessage);
+ }
+ // httpMessage.clear();
+ webSendFooter();
+
+ delay(200);
+ dispatch_reboot(true);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+void webHandleScreenshot()
+{ // http://plate01/screenshot
+ if(!httpIsAuthenticated(F("screenshot"))) return;
+
+ if(webServer.hasArg(F("a"))) {
+ if(webServer.arg(F("a")) == F("next")) {
+ dispatch_page_next();
+ } else if(webServer.arg(F("a")) == F("prev")) {
+ dispatch_page_prev();
+ }
+ }
+
+ if(webServer.hasArg(F("q"))) {
+ lv_disp_t* disp = lv_disp_get_default();
+ webServer.setContentLength(122 + disp->driver.hor_res * disp->driver.ver_res * sizeof(lv_color_t));
+ webServer.send_P(200, PSTR("image/bmp"), "");
+ guiTakeScreenshot();
+ webServer.client().stop();
+
+ } else {
+ {
+ String httpMessage((char*)0);
+ httpMessage.reserve(HTTP_PAGE_SIZE);
+ httpMessage += F("");
+ httpMessage += httpGetNodename();
+ httpMessage += F("
");
+
+ httpMessage +=
+ F("");
+ httpMessage += F("
");
+
+ httpMessage += F("");
+ httpMessage +=
+ F("");
+ httpMessage +=
+ F("");
+ httpMessage += FPSTR(MAIN_MENU_BUTTON);
+
+ webSendPage(httpGetNodename(), httpMessage.length(), false);
+ webServer.sendContent(httpMessage);
+ }
+ // httpMessage.clear();
+ webSendFooter();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void webHandleAbout()
+{ // http://plate01/about
+ if(!httpIsAuthenticated(F("about"))) return;
+
+ {
+ String httpMessage((char*)0);
+ httpMessage.reserve(HTTP_PAGE_SIZE);
+
+ httpMessage += F("HASP OpenHardware edition
Copyright© 2020 Francis Van Roie ");
+ httpMessage += FPSTR(MIT_LICENSE);
+ httpMessage += F("Based on the previous work of the following open source developers.
");
+ httpMessage += F("HASwitchPlate
Copyright© 2019 Allen Derusha allen@derusha.org");
+ httpMessage += FPSTR(MIT_LICENSE);
+ httpMessage +=
+ F("LittlevGL
Copyright© 2016 Gábor Kiss-VámosiCopyright© 2019 "
+ "LittlevGL");
+ httpMessage += FPSTR(MIT_LICENSE);
+ httpMessage += F("zi Font Engine
Copyright© 2020 Francis Van Roie");
+ httpMessage += FPSTR(MIT_LICENSE);
+ httpMessage += F("TFT_eSPI Library
Copyright© 2020 Bodmer (https://github.com/Bodmer) All "
+ "rights reserved.FreeBSD License");
+ httpMessage +=
+ F("includes parts from the Adafruit_GFX libraryCopyright© 2012 Adafruit Industries. "
+ "All rights reservedBSD License
");
+ httpMessage += F("ArduinoJson
Copyright© 2014-2020 Benoit BLANCHON");
+ httpMessage += FPSTR(MIT_LICENSE);
+ httpMessage += F("PubSubClient
Copyright© 2008-2015 Nicholas O'Leary");
+ httpMessage += FPSTR(MIT_LICENSE);
+ httpMessage +=
+ F("ArduinoLog
Copyright© 2017,2018 Thijs Elenbaas, MrRobot62, rahuldeo2047, NOX73, "
+ "dhylands, Josha blemasle, mfalkvidd");
+ httpMessage += FPSTR(MIT_LICENSE);
+#if HASP_USE_SYSLOG > 0
+ // Replaced with WiFiUDP client
+ // httpMessage += F("Syslog
Copyright© 2016 Martin Sloup");
+ // httpMessage += FPSTR(MIT_LICENSE);
+#endif
+#if HASP_USE_QRCODE > 0
+ httpMessage += F("QR Code generator
Copyright© Project Nayuki");
+ httpMessage += FPSTR(MIT_LICENSE);
+#endif
+ httpMessage += F("AceButton
Copyright© 2018 Brian T. Park");
+ httpMessage += FPSTR(MIT_LICENSE);
+
+ httpMessage += FPSTR(MAIN_MENU_BUTTON);
+
+ webSendPage(httpGetNodename(), httpMessage.length(), false);
+ webServer.sendContent(httpMessage);
+ }
+ // httpMessage.clear();
+ webSendFooter();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+void webHandleInfo()
+{ // http://plate01/
+ if(!httpIsAuthenticated(F("info"))) return;
+
+ {
+ char size_buf[32];
+ String httpMessage((char*)0);
+ httpMessage.reserve(HTTP_PAGE_SIZE);
+ httpMessage += F("");
+ httpMessage += httpGetNodename();
+ httpMessage += F("
");
+
+ /* HASP Stats */
+ httpMessage += F("HASP Version: ");
+ {
+ char version[32];
+ haspGetVersion(version, sizeof(version));
+ httpMessage += version;
+ }
+ httpMessage += F("
Build DateTime: ");
+ httpMessage += __DATE__;
+ httpMessage += F(" ");
+ httpMessage += __TIME__;
+ httpMessage += F(" CET
Uptime: ");
+
+ unsigned long time = millis() / 1000;
+ uint16_t day = time / 86400;
+ time = time % 86400;
+ uint8_t hour = time / 3600;
+ time = time % 3600;
+ uint8_t min = time / 60;
+ time = time % 60;
+ uint8_t sec = time;
+
+ if(day > 0) {
+ httpMessage += String(day);
+ httpMessage += F("d ");
+ }
+ if(day > 0 || hour > 0) {
+ httpMessage += String(hour);
+ httpMessage += F("h ");
+ }
+ if(day > 0 || hour > 0 || min > 0) {
+ httpMessage += String(min);
+ httpMessage += F("m ");
+ }
+ httpMessage += String(sec);
+ httpMessage += F("s");
+
+ httpMessage += F("
Free Memory: ");
+ hasp_util_format_bytes(halGetFreeHeap(), size_buf, sizeof(size_buf));
+ httpMessage += size_buf;
+ httpMessage += F("
Memory Fragmentation: ");
+ httpMessage += String(halGetHeapFragmentation());
+
+#if ARDUINO_ARCH_ESP32
+ if(psramFound()) {
+ httpMessage += F("
Free PSRam: ");
+ hasp_util_format_bytes(ESP.getFreePsram(), size_buf, sizeof(size_buf));
+ httpMessage += size_buf;
+ httpMessage += F("
PSRam Size: ");
+ hasp_util_format_bytes(ESP.getPsramSize(), size_buf, sizeof(size_buf));
+ httpMessage += size_buf;
+ }
+#endif
+
+ /* LVGL Stats */
+ lv_mem_monitor_t mem_mon;
+ lv_mem_monitor(&mem_mon);
+ httpMessage += F("LVGL Memory: ");
+ hasp_util_format_bytes(mem_mon.total_size, size_buf, sizeof(size_buf));
+ httpMessage += size_buf;
+ httpMessage += F("
LVGL Free: ");
+ hasp_util_format_bytes(mem_mon.free_size, size_buf, sizeof(size_buf));
+ httpMessage += size_buf;
+ httpMessage += F("
LVGL Fragmentation: ");
+ httpMessage += mem_mon.frag_pct;
+
+ // httpMessage += F("
LCD Model: ")) + String(LV_HASP_HOR_RES_MAX) + " x " +
+ // String(LV_HASP_VER_RES_MAX); httpMessage += F("
LCD Version: ")) +
+ // String(lcdVersion);
+ httpMessage += F("
LCD Active Page: ");
+ httpMessage += String(haspGetPage());
+
+ /* Wifi Stats */
+#if HASP_USE_WIFI > 0
+ httpMessage += F("
SSID: ");
+ httpMessage += String(WiFi.SSID());
+ httpMessage += F("Signal Strength: ");
+
+ int8_t rssi = WiFi.RSSI();
+ httpMessage += String(rssi);
+ httpMessage += F("dBm (");
+
+ if(rssi >= -50) {
+ httpMessage += F("Excellent)");
+ } else if(rssi >= -60) {
+ httpMessage += F("Good)");
+ } else if(rssi >= -70) {
+ httpMessage += F("Fair)");
+ } else if(rssi >= -80) {
+ httpMessage += F("Weak)");
+ } else {
+ httpMessage += F("Very Bad)");
+ }
+#if defined(STM32F4xx)
+ byte mac[6];
+ WiFi.macAddress(mac);
+ char macAddress[16];
+ snprintf_P(macAddress, sizeof(macAddress), PSTR("%02x%02x%02x"), mac[0], mac[1], mac[2], mac[3], mac[4],
+ mac[5]);
+ httpMessage += F("IP Address: ");
+ httpMessage += String(WiFi.localIP());
+ httpMessage += F("Gateway: ");
+ httpMessage += String(WiFi.gatewayIP());
+ httpMessage += F("MAC Address: ");
+ httpMessage += String(macAddress);
+#else
+ httpMessage += F("IP Address: ");
+ httpMessage += String(WiFi.localIP().toString());
+ httpMessage += F("Gateway: ");
+ httpMessage += String(WiFi.gatewayIP().toString());
+ httpMessage += F("DNS Server: ");
+ httpMessage += String(WiFi.dnsIP().toString());
+ httpMessage += F("MAC Address: ");
+ httpMessage += String(WiFi.macAddress());
+#endif
+#endif
+#if HASP_USE_ETHERNET > 0
+#if defined(ARDUINO_ARCH_ESP32)
+ httpMessage += F("
Ethernet: ");
+ httpMessage += String(ETH.linkSpeed());
+ httpMessage += F(" Mbps");
+ if(ETH.fullDuplex()) {
+ httpMessage += F(" FULL_DUPLEX");
+ }
+ httpMessage += F("IP Address: ");
+ httpMessage += String(ETH.localIP().toString());
+ httpMessage += F("Gateway: ");
+ httpMessage += String(ETH.gatewayIP().toString());
+ httpMessage += F("DNS Server: ");
+ httpMessage += String(ETH.dnsIP().toString());
+ httpMessage += F("MAC Address: ");
+ httpMessage += String(ETH.macAddress());
+#endif
+#endif
+/* Mqtt Stats */
+#if HASP_USE_MQTT > 0
+ httpMessage += F("
MQTT Status: ");
+ if(mqttIsConnected()) { // Check MQTT connection
+ httpMessage += F("Connected");
+ } else {
+ httpMessage += F("Disconnected, return code: ");
+ // +String(mqttClient.returnCode());
+ }
+ httpMessage += F("
MQTT ClientID: ");
+
+ {
+ char mqttClientId[64];
+ String mac = halGetMacAddress(3, "");
+ mac.toLowerCase();
+ snprintf_P(mqttClientId, sizeof(mqttClientId), PSTR("%s-%s"), mqttNodeName, mac.c_str());
+ httpMessage += mqttClientId;
+ }
+
+#endif // MQTT
+
+ /* ESP Stats */
+ httpMessage += F("
MCU Model: ");
+ httpMessage += halGetChipModel();
+ httpMessage += F("
CPU Frequency: ");
+ httpMessage += String(halGetCpuFreqMHz());
+ httpMessage += F("MHz");
+
+#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_ESP8266)
+ httpMessage += F("
Flash Chip Size: ");
+ hasp_util_format_bytes(ESP.getFlashChipSize(), size_buf, sizeof(size_buf));
+ httpMessage += size_buf;
+
+ httpMessage += F("Program Size: ");
+ hasp_util_format_bytes(ESP.getSketchSize(), size_buf, sizeof(size_buf));
+ httpMessage += size_buf;
+
+ httpMessage += F("
Free Program Space: ");
+ hasp_util_format_bytes(ESP.getFreeSketchSpace(), size_buf, sizeof(size_buf));
+ httpMessage += size_buf;
+#endif
+
+ //#if defined(ARDUINO_ARCH_ESP32)
+ // httpMessage += F("
ESP SDK version: ");
+ // httpMessage += String(ESP.getSdkVersion());
+ //#else
+ httpMessage += F("
Core version: ");
+ httpMessage += String(halGetCoreVersion());
+ //#endif
+ httpMessage += F("
Last Reset: ");
+ httpMessage += halGetResetInfo();
+
+ httpMessage += FPSTR(MAIN_MENU_BUTTON);
+
+ webSendPage(httpGetNodename(), httpMessage.length(), false);
+ webServer.sendContent(httpMessage);
+ }
+ // httpMessage.clear();
+ webSendFooter();
+}
+
+// String getContentType(String filename)
+// {
+// if(webServer.hasArg(F("download"))) {
+// return F("application/octet-stream");
+// } else if(filename.endsWith(F(".htm")) || filename.endsWith(F(".html"))) {
+// return F("text/html");
+// } else if(filename.endsWith(F(".css"))) {
+// return F("text/css");
+// } else if(filename.endsWith(F(".js"))) {
+// return F("application/javascript");
+// } else if(filename.endsWith(F(".png"))) {
+// return F("image/png");
+// } else if(filename.endsWith(F(".gif"))) {
+// return F("image/gif");
+// } else if(filename.endsWith(F(".jpg"))) {
+// return F("image/jpeg");
+// } else if(filename.endsWith(F(".ico"))) {
+// return F("image/x-icon");
+// } else if(filename.endsWith(F(".xml"))) {
+// return F("text/xml");
+// } else if(filename.endsWith(F(".pdf"))) {
+// return F("application/x-pdf");
+// } else if(filename.endsWith(F(".zip"))) {
+// return F("application/x-zip");
+// } else if(filename.endsWith(F(".gz"))) {
+// return F("application/x-gzip");
+// }
+// return F("text/plain");
+// }
+
+static String getContentType(const String& path)
+{
+ char buff[sizeof(mime::mimeTable[0].mimeType)];
+ // Check all entries but last one for match, return if found
+ for(size_t i = 0; i < sizeof(mime::mimeTable) / sizeof(mime::mimeTable[0]) - 1; i++) {
+ strcpy_P(buff, mime::mimeTable[i].endsWith);
+ if(path.endsWith(buff)) {
+ strcpy_P(buff, mime::mimeTable[i].mimeType);
+ return String(buff);
+ }
+ }
+ // Fall-through and just return default type
+ strcpy_P(buff, mime::mimeTable[sizeof(mime::mimeTable) / sizeof(mime::mimeTable[0]) - 1].mimeType);
+ return String(buff);
+}
+
+/* String urldecode(String str)
+{
+ String encodedString = "";
+ char c;
+ for(unsigned int i = 0; i < str.length(); i++) {
+ c = str.charAt(i);
+ if(c == '+') {
+ encodedString += ' ';
+ } else if(c == '%') {
+ // char buffer[3];
+ char buffer[128];
+ i++;
+ buffer[0] = str.charAt(i);
+ i++;
+ buffer[1] = str.charAt(i);
+ buffer[2] = '\0';
+ c = (char)strtol((const char *)&buffer, NULL, 16);
+ encodedString += c;
+ } else {
+ encodedString += c;
+ }
+ yield();
+ }
+ return encodedString;
+} */
+
+static unsigned long htppLastLoopTime = 0;
+void webUploadProgress()
+{
+ long t = webServer.header("Content-Length").toInt();
+ if(millis() - htppLastLoopTime >= 1250) {
+ LOG_VERBOSE(TAG_HTTP, F(D_BULLET "Uploaded %u / %d bytes"), upload->totalSize + upload->currentSize, t);
+ htppLastLoopTime = millis();
+ }
+ if(t > 0) t = (upload->totalSize + upload->currentSize) * 100 / t;
+ haspProgressVal(t);
+}
+
+#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_ESP8266)
+static inline void webUpdatePrintError()
+{
+#if defined(ARDUINO_ARCH_ESP8266)
+ String output((char*)0);
+ output.reserve(128);
+ StringStream stream((String&)output);
+ Update.printError(stream); // ESP8266 only has printError()
+ LOG_ERROR(TAG_HTTP, output.c_str());
+ haspProgressMsg(output.c_str());
+#elif defined(ARDUINO_ARCH_ESP32)
+ LOG_ERROR(TAG_HTTP, Update.errorString()); // ESP32 has errorString()
+ haspProgressMsg(Update.errorString());
+#endif
+}
+
+void webUpdateReboot()
+{
+ LOG_INFO(TAG_HTTP, F("Update Success: %u bytes received. Rebooting..."), upload->totalSize);
+
+ {
+ String httpMessage((char*)0);
+ httpMessage.reserve(HTTP_PAGE_SIZE);
+ httpMessage += F("
");
+ httpMessage += httpGetNodename();
+ httpMessage += F("
");
+ httpMessage += F("Upload complete. Rebooting device, please wait...");
+
+ webSendPage(httpGetNodename(), httpMessage.length(), true);
+ webServer.sendContent(httpMessage);
+ }
+ // httpMessage.clear();
+ webSendFooter();
+
+ delay(250);
+ dispatch_reboot(true); // Save the current config
+}
+
+void webHandleFirmwareUpload()
+{
+ upload = &webServer.upload();
+
+ if(upload->status == UPLOAD_FILE_START) {
+ if(!httpIsAuthenticated(F("update"))) return;
+ LOG_TRACE(TAG_HTTP, F("Update: %s"), upload->filename.c_str());
+ haspProgressMsg(upload->filename.c_str());
+ // WiFiUDP::stopAll();
+ uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
+ // if(!Update.begin(UPDATE_SIZE_UNKNOWN)) { // start with max available size
+ if(!Update.begin(maxSketchSpace)) { // start with max available size
+ webUpdatePrintError();
+ }
+
+ } else if(upload->status == UPLOAD_FILE_WRITE) {
+ // flashing firmware to ESP
+ if(Update.write(upload->buf, upload->currentSize) != upload->currentSize) {
+ webUpdatePrintError();
+ } else {
+ webUploadProgress();
+ }
+
+ } else if(upload->status == UPLOAD_FILE_END) {
+ haspProgressVal(100);
+ if(Update.end(true)) { // true to set the size to the current progress
+ haspProgressMsg(F(D_OTA_UPDATE_APPLY));
+ webUpdateReboot();
+ } else {
+ webUpdatePrintError();
+ }
+ }
+}
+#endif
+
+#if HASP_USE_SPIFFS > 0 || HASP_USE_LITTLEFS > 0
+bool handleFileRead(String path)
+{
+ if(!httpIsAuthenticated(F("fileread"))) return false;
+
+ path = webServer.urlDecode(path).substring(0, 31);
+ if(path.endsWith("/")) {
+ path += F("index.htm");
+ }
+ String pathWithGz = path + F(".gz");
+ if(HASP_FS.exists(pathWithGz) || HASP_FS.exists(path)) {
+ if(HASP_FS.exists(pathWithGz)) path += F(".gz");
+
+ File file = HASP_FS.open(path, "r");
+ String contentType = getContentType(path);
+ if(path == F("/edit.htm.gz")) {
+ contentType = F("text/html");
+ }
+ webServer.streamFile(file, contentType);
+ file.close();
+ return true;
+ }
+ return false;
+}
+
+void handleFileUpload()
+{
+ if(webServer.uri() != "/edit") {
+ return;
+ }
+ upload = &webServer.upload();
+ if(upload->status == UPLOAD_FILE_START) {
+ if(!httpIsAuthenticated(F("fileupload"))) return;
+ LOG_INFO(TAG_HTTP, F("Total size: %s"), webServer.headerName(0).c_str());
+ String filename((char*)0);
+ filename.reserve(128);
+ filename = upload->filename;
+ if(!filename.startsWith("/")) {
+ filename = "/";
+ filename += upload->filename;
+ }
+ if(filename.length() < 32) {
+ fsUploadFile = HASP_FS.open(filename, "w");
+ LOG_TRACE(TAG_HTTP, F("handleFileUpload Name: %s"), filename.c_str());
+ haspProgressMsg(fsUploadFile.name());
+ } else {
+ LOG_ERROR(TAG_HTTP, F("Filename %s is too long"), filename.c_str());
+ }
+ } else if(upload->status == UPLOAD_FILE_WRITE) {
+ // DBG_OUTPUT_PORT.print("handleFileUpload Data: "); debugPrintln(upload.currentSize);
+ if(fsUploadFile) {
+ if(fsUploadFile.write(upload->buf, upload->currentSize) != upload->currentSize) {
+ LOG_ERROR(TAG_HTTP, F("Failed to write received data to file"));
+ } else {
+ webUploadProgress(); // Moved to httpEverySecond Loop
+ }
+ }
+ } else if(upload->status == UPLOAD_FILE_END) {
+ if(fsUploadFile) {
+ LOG_INFO(TAG_HTTP, F("Uploaded %s (%u bytes)"), fsUploadFile.name(), upload->totalSize);
+ fsUploadFile.close();
+ }
+ haspProgressVal(255);
+
+ // Redirect to /config/hasp page. This flushes the web buffer and frees the memory
+ webServer.sendHeader(String(F("Location")), String(F("/config/hasp")), true);
+ webServer.send_P(302, PSTR("text/plain"), "");
+ // httpReconnect();
+ }
+}
+
+void handleFileDelete()
+{
+ if(!httpIsAuthenticated(F("filedelete"))) return;
+
+ char mimetype[16];
+ snprintf_P(mimetype, sizeof(mimetype), PSTR("text/plain"));
+
+ if(webServer.args() == 0) {
+ return webServer.send_P(500, mimetype, PSTR("BAD ARGS"));
+ }
+ String path = webServer.arg(0);
+ LOG_TRACE(TAG_HTTP, F("handleFileDelete: %s"), path.c_str());
+ if(path == "/") {
+ return webServer.send_P(500, mimetype, PSTR("BAD PATH"));
+ }
+ if(!HASP_FS.exists(path)) {
+ return webServer.send_P(404, mimetype, PSTR("FileNotFound"));
+ }
+ HASP_FS.remove(path);
+ webServer.send_P(200, mimetype, PSTR(""));
+ // path.clear();
+}
+
+void handleFileCreate()
+{
+ if(!httpIsAuthenticated(F("filecreate"))) return;
+
+ if(webServer.args() == 0) {
+ return webServer.send(500, PSTR("text/plain"), PSTR("BAD ARGS"));
+ }
+ String path = webServer.arg(0);
+ LOG_TRACE(TAG_HTTP, F("handleFileCreate: %s"), path.c_str());
+ if(path == "/") {
+ return webServer.send(500, PSTR("text/plain"), PSTR("BAD PATH"));
+ }
+ if(HASP_FS.exists(path)) {
+ return webServer.send(500, PSTR("text/plain"), PSTR("FILE EXISTS"));
+ }
+ File file = HASP_FS.open(path, "w");
+ if(file) {
+ file.close();
+ } else {
+ return webServer.send(500, PSTR("text/plain"), PSTR("CREATE FAILED"));
+ }
+ webServer.send(200, PSTR("text/plain"), "");
+ path.clear();
+}
+
+void handleFileList()
+{
+ if(!httpIsAuthenticated(F("filelist"))) return;
+
+ if(!webServer.hasArg(F("dir"))) {
+ webServer.send(500, PSTR("text/plain"), PSTR("BAD ARGS"));
+ return;
+ }
+
+ String path = webServer.arg(F("dir"));
+ LOG_TRACE(TAG_HTTP, F("handleFileList: %s"), path.c_str());
+ path.clear();
+
+#if defined(ARDUINO_ARCH_ESP32)
+ File root = HASP_FS.open("/", FILE_READ);
+ File file = root.openNextFile();
+ String output = "[";
+
+ while(file) {
+ if(output != "[") {
+ output += ',';
+ }
+ bool isDir = false;
+ output += F("{\"type\":\"");
+ output += (isDir) ? F("dir") : F("file");
+ output += F("\",\"name\":\"");
+ if(file.name()[0] == '/') {
+ output += &(file.name()[1]);
+ } else {
+ output += file.name();
+ }
+ output += F("\"}");
+
+ // file.close();
+ file = root.openNextFile();
+ }
+ output += "]";
+ webServer.send(200, PSTR("text/json"), output);
+#elif defined(ARDUINO_ARCH_ESP8266)
+ Dir dir = HASP_FS.openDir(path);
+ String output = "[";
+ while(dir.next()) {
+ File entry = dir.openFile("r");
+ if(output != "[") {
+ output += ',';
+ }
+ bool isDir = false;
+ output += F("{\"type\":\"");
+ output += (isDir) ? F("dir") : F("file");
+ output += F("\",\"name\":\"");
+ if(entry.name()[0] == '/') {
+ output += &(entry.name()[1]);
+ } else {
+ output += entry.name();
+ }
+ output += F("\"}");
+ entry.close();
+ }
+ output += "]";
+ webServer.send(200, PSTR("text/json"), output);
+#endif
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+#if HASP_USE_CONFIG > 0
+void webHandleConfig()
+{ // http://plate01/config
+ if(!httpIsAuthenticated(F("config"))) return;
+
+ saveConfig();
+
+// Reboot after saving wifi config in AP mode
+#if HASP_USE_WIFI > 0 && !defined(STM32F4xx)
+ if(WiFi.getMode() != WIFI_STA) {
+ httpHandleReboot();
+ }
+#endif
+
+ {
+ String httpMessage((char*)0);
+ httpMessage.reserve(HTTP_PAGE_SIZE);
+ httpMessage += F("");
+ httpMessage += httpGetNodename();
+ httpMessage += F("
");
+
+#if HASP_USE_WIFI > 0
+ httpMessage += F("");
+#endif
+
+#if HASP_USE_MQTT > 0
+ httpMessage += F("");
+#endif
+
+ httpMessage += F("");
+
+ httpMessage += F("");
+
+ // httpMessage +=
+ // F("");
+
+#if HASP_USE_GPIO > 0
+ httpMessage += F("");
+#endif
+
+ httpMessage += F("");
+
+ httpMessage +=
+ F("");
+
+ httpMessage += FPSTR(MAIN_MENU_BUTTON);
+
+ webSendPage(httpGetNodename(), httpMessage.length(), false);
+ webServer.sendContent(httpMessage);
+ }
+ // httpMessage.clear();
+ webSendFooter();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+#if HASP_USE_MQTT > 0
+void webHandleMqttConfig()
+{ // http://plate01/config/mqtt
+ if(!httpIsAuthenticated(F("config/mqtt"))) return;
+
+ StaticJsonDocument<256> settings;
+ mqttGetConfig(settings.to());
+
+ {
+ // char buffer[128];
+ String httpMessage((char*)0);
+ httpMessage.reserve(HTTP_PAGE_SIZE);
+ httpMessage += F("");
+ httpMessage += httpGetNodename();
+ httpMessage += F("
");
+
+ httpMessage += F("");
+
+ add_form_button(httpMessage, F("↩ " D_HTTP_CONFIGURATION), F("/config"), F(""));
+ // httpMessage += PSTR("");
+
+ webSendPage(httpGetNodename(), httpMessage.length(), false);
+ webServer.sendContent(httpMessage);
+ }
+ // httpMessage.clear();
+ webSendFooter();
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+void webHandleGuiConfig()
+{ // http://plate01/config/wifi
+ if(!httpIsAuthenticated(F("config/gui"))) return;
+
+ {
+ StaticJsonDocument<256> settings;
+ guiGetConfig(settings.to());
+
+ String httpMessage((char*)0);
+ httpMessage.reserve(HTTP_PAGE_SIZE);
+ httpMessage += F("");
+ httpMessage += httpGetNodename();
+ httpMessage += F("
");
+
+ httpMessage += F("");
+
+#if TOUCH_DRIVER == 2046 && defined(TOUCH_CS)
+ add_form_button(httpMessage, F(D_HTTP_CALIBRATE), F("/config/gui"), F("name='action' value='calibrate'"));
+
+// httpMessage += PSTR("");
+#endif
+
+ add_form_button(httpMessage, F("↩ " D_HTTP_CONFIGURATION), F("/config"), F(""));
+
+ // httpMessage += PSTR("");
+
+ webSendPage(httpGetNodename(), httpMessage.length(), false);
+ webServer.sendContent(httpMessage);
+ }
+ webSendFooter();
+
+ if(webServer.hasArg(F("action"))) dispatch_text_line(webServer.arg(F("action")).c_str());
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+#if HASP_USE_WIFI > 0
+void webHandleWifiConfig()
+{ // http://plate01/config/wifi
+ if(!httpIsAuthenticated(F("config/wifi"))) return;
+
+ StaticJsonDocument<256> settings;
+ wifiGetConfig(settings.to());
+
+ String httpMessage((char*)0);
+ httpMessage.reserve(HTTP_PAGE_SIZE);
+ httpMessage += F("");
+ httpMessage += httpGetNodename();
+ httpMessage += F("
");
+
+ httpMessage += F("");
+
+#if HASP_USE_WIFI > 0 && !defined(STM32F4xx)
+ if(WiFi.getMode() == WIFI_STA) {
+ add_form_button(httpMessage, F("↩ " D_HTTP_CONFIGURATION), F("/config"), F(""));
+ // httpMessage += PSTR("");
+ }
+#endif
+
+ webSendPage(httpGetNodename(), httpMessage.length(), false);
+ webServer.sendContent(httpMessage);
+#if defined(STM32F4xx)
+ httpMessage = "";
+#else
+ httpMessage.clear();
+#endif
+ webSendFooter();
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+#if HASP_USE_HTTP > 0
+void webHandleHttpConfig()
+{ // http://plate01/config/http
+ if(!httpIsAuthenticated(F("config/http"))) return;
+
+ {
+ StaticJsonDocument<256> settings;
+ httpGetConfig(settings.to());
+
+ // String httpMessage((char *)0);
+ // httpMessage.reserve(HTTP_PAGE_SIZE);
+ // httpMessage += F("");
+ // httpMessage += httpGetNodename();
+ // httpMessage += F("
");
+
+ // httpMessage += F("");
+
+ // httpMessage += PSTR("");
+
+ char httpMessage[HTTP_PAGE_SIZE];
+
+ size_t len = snprintf_P(
+ httpMessage, sizeof(httpMessage),
+ PSTR("%s
"
+ ""
+ ""),
+ httpGetNodename(), settings[FPSTR(FP_CONFIG_USER)].as().c_str(),
+ settings[FPSTR(FP_CONFIG_PASS)].as().c_str());
+
+ // if(settings[FPSTR(FP_CONFIG_PASS)].as() != "") {
+ // httpMessage += F(D_PASSWORD_MASK);
+ // }
+
+ webSendPage(httpGetNodename(), len, false);
+ webServer.sendContent(httpMessage);
+ }
+ // httpMessage.clear();
+ webSendFooter();
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+#if defined(HASP_USE_GPIO) && (HASP_USE_GPIO > 0)
+void webHandleGpioConfig()
+{ // http://plate01/config/gpio
+ if(!httpIsAuthenticated(F("config/gpio"))) return;
+ uint8_t configCount = 0;
+
+ // StaticJsonDocument<256> settings;
+ // gpioGetConfig(settings.to());
+
+ if(webServer.hasArg(PSTR("save"))) {
+ uint8_t id = webServer.arg(F("id")).toInt();
+ uint8_t pin = webServer.arg(F("pin")).toInt();
+ uint8_t type = webServer.arg(F("type")).toInt() + webServer.arg(F("state")).toInt();
+ uint8_t group = webServer.arg(F("group")).toInt();
+ uint8_t pinfunc = webServer.arg(F("func")).toInt();
+ gpioSavePinConfig(id, pin, type, group, pinfunc);
+ }
+ if(webServer.hasArg(PSTR("del"))) {
+ uint8_t id = webServer.arg(F("id")).toInt();
+ uint8_t pin = webServer.arg(F("pin")).toInt();
+ gpioSavePinConfig(id, pin, HASP_GPIO_FREE, 0, 0);
+ }
+
+ {
+ String httpMessage((char*)0);
+ httpMessage.reserve(HTTP_PAGE_SIZE);
+ httpMessage += F("");
+ httpMessage += httpGetNodename();
+ httpMessage += F("
");
+
+ httpMessage += F("");
+
+ if(configCount < HASP_NUM_GPIO_CONFIG) {
+ httpMessage += F("");
+ }
+
+ add_form_button(httpMessage, F("↩ " D_HTTP_CONFIGURATION), F("/config"), F(""));
+ // httpMessage += F("");
+
+ webSendPage(httpGetNodename(), httpMessage.length(), false);
+ webServer.sendContent(httpMessage);
+ }
+ // httpMessage.clear();
+ webSendFooter();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+void webHandleGpioOptions()
+{ // http://plate01/config/gpio/options
+ if(!httpIsAuthenticated(F("config/gpio/options"))) return;
+
+ {
+ StaticJsonDocument<256> settings;
+ guiGetConfig(settings.to());
+
+ uint8_t config_id = webServer.arg(F("id")).toInt();
+
+ String httpMessage((char*)0);
+ httpMessage.reserve(HTTP_PAGE_SIZE);
+ httpMessage += F("");
+ httpMessage += httpGetNodename();
+ httpMessage += F("
");
+
+ httpMessage += F("");
+
+ httpMessage += PSTR("");
+
+ webSendPage(httpGetNodename(), httpMessage.length(), false);
+ webServer.sendContent(httpMessage);
+ }
+ webSendFooter();
+
+ if(webServer.hasArg(F("action"))) dispatch_text_line(webServer.arg(F("action")).c_str()); // Security check
+}
+#endif // HASP_USE_GPIO
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+void webHandleDebugConfig()
+{ // http://plate01/config/debug
+ if(!httpIsAuthenticated(F("config/debug"))) return;
+
+ StaticJsonDocument<256> settings;
+ debugGetConfig(settings.to());
+
+ {
+ String httpMessage((char*)0);
+ httpMessage.reserve(HTTP_PAGE_SIZE);
+ httpMessage += F("");
+ httpMessage += httpGetNodename();
+ httpMessage += F("
");
+
+ httpMessage += F("");
+
+ add_form_button(httpMessage, F("↩ " D_HTTP_CONFIGURATION), F("/config"), F(""));
+ // httpMessage += PSTR("");
+
+ webSendPage(httpGetNodename(), httpMessage.length(), false);
+ webServer.sendContent(httpMessage);
+ }
+ // httpMessage.clear();
+ webSendFooter();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+void webHandleHaspConfig()
+{ // http://plate01/config/http
+ if(!httpIsAuthenticated(F("config/hasp"))) return;
+
+ StaticJsonDocument<256> settings;
+ haspGetConfig(settings.to());
+
+ {
+ String httpMessage((char*)0);
+ httpMessage.reserve(HTTP_PAGE_SIZE);
+ httpMessage += F("");
+ httpMessage += httpGetNodename();
+ httpMessage += F("
");
+
+ httpMessage += F("
");
+
+ // httpMessage += F("");
+
+ // httpMessage +=
+ // F("");
+ httpMessage += FPSTR(MAIN_MENU_BUTTON);
+
+ webSendPage(httpGetNodename(), httpMessage.length(), false);
+ webServer.sendContent(httpMessage);
+ }
+ // httpMessage.clear();
+ webSendFooter();
+}
+#endif // HASP_USE_CONFIG
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+void httpHandleNotFound()
+{ // webServer 404
+#if HASP_USE_SPIFFS > 0 || HASP_USE_LITTLEFS > 0
+ if(handleFileRead(webServer.uri())) return;
+#endif
+
+#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_ESP8266)
+ LOG_TRACE(TAG_HTTP, F("Sending 404 to client connected from: %s"),
+ webServer.client().remoteIP().toString().c_str());
+#else
+ // LOG_TRACE(TAG_HTTP,F("Sending 404 to client connected from: %s"), String(webServer.client().remoteIP()).c_str());
+#endif
+
+ String httpMessage((char*)0);
+ httpMessage.reserve(HTTP_PAGE_SIZE);
+
+ httpMessage += F("File Not Found\n\nURI: ");
+ httpMessage += webServer.uri();
+ httpMessage += F("\nMethod: ");
+ httpMessage += (webServer.method() == HTTP_GET) ? F("GET") : F("POST");
+ httpMessage += F("\nArguments: ");
+ httpMessage += webServer.args();
+ httpMessage += "\n";
+ for(int i = 0; i < webServer.args(); i++) {
+ httpMessage += " " + webServer.argName(i) + ": " + webServer.arg(i) + "\n";
+ }
+ webServer.send(404, PSTR("text/plain"), httpMessage.c_str());
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+void webHandleFirmware()
+{
+ if(!httpIsAuthenticated(F("firmware"))) return;
+
+ {
+ String httpMessage((char*)0);
+ httpMessage.reserve(HTTP_PAGE_SIZE);
+ httpMessage += F("");
+ httpMessage += httpGetNodename();
+ httpMessage += F("
");
+
+ httpMessage += F("");
+
+ // httpMessage += F("");
+
+ httpMessage += FPSTR(MAIN_MENU_BUTTON);
+
+ webSendPage(httpGetNodename(), httpMessage.length(), false);
+ webServer.sendContent(httpMessage);
+ }
+ // httpMessage.clear();
+ webSendFooter();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+void httpHandleEspFirmware()
+{ // http://plate01/espfirmware
+ if(!httpIsAuthenticated(F("espfirmware"))) return;
+
+ {
+ String httpMessage((char*)0);
+ httpMessage.reserve(HTTP_PAGE_SIZE);
+ httpMessage += F("");
+ httpMessage += httpGetNodename();
+ httpMessage += F("
");
+
+ httpMessage += F("ESP update
Updating ESP firmware from: ");
+ httpMessage += webServer.arg("espFirmware");
+
+ webSendPage(httpGetNodename(), httpMessage.length(), true);
+ webServer.sendContent(httpMessage);
+ // httpMessage.clear();
+ }
+ webSendFooter();
+
+ LOG_TRACE(TAG_HTTP, F("Attempting ESP firmware update from: %s"), webServer.arg("espFirmware").c_str());
+ // espStartOta(webServer.arg("espFirmware"));
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+#if HASP_USE_CONFIG > 0
+void webHandleSaveConfig()
+{
+ if(!httpIsAuthenticated(F("saveConfig"))) return;
+
+ configWriteConfig();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+void httpHandleResetConfig()
+{ // http://plate01/resetConfig
+ if(!httpIsAuthenticated(F("resetConfig"))) return;
+
+ bool resetConfirmed = webServer.arg(F("confirm")) == F("yes");
+
+ {
+ String httpMessage((char*)0);
+ httpMessage.reserve(HTTP_PAGE_SIZE);
+ httpMessage += F("");
+ httpMessage += httpGetNodename();
+ httpMessage += F("
");
+
+ if(resetConfirmed) { // User has confirmed, so reset everything
+ bool formatted = configClearEeprom();
+ if(formatted) {
+ httpMessage += F("Resetting all saved settings and restarting device");
+ } else {
+ httpMessage += F("Failed to format the internal flash partition");
+ resetConfirmed = false;
+ }
+ } else {
+ httpMessage +=
+ F("Warning
This process will reset all settings to the default values. The internal flash "
+ "will "
+ "be erased and the device is restarted. You may need to connect to the WiFi AP displayed on the "
+ "panel to "
+ "re-configure the device before accessing it again. ALL FILES WILL BE LOST!"
+ "
"
+ "
");
+
+ add_form_button(httpMessage, F("↩ " D_HTTP_CONFIGURATION), F("/config"), F(""));
+ // httpMessage +=
+ // PSTR("");
+ }
+
+ webSendPage(httpGetNodename(), httpMessage.length(), resetConfirmed);
+ webServer.sendContent(httpMessage);
+ }
+ // httpMessage.clear();
+ webSendFooter();
+
+ if(resetConfirmed) {
+ delay(250);
+ // configClearSaved();
+ dispatch_reboot(false); // Do not save the current config
+ }
+}
+#endif // HASP_USE_CONFIG
+
+void httpStart()
+{
+ webServer.begin();
+ webServerStarted = true;
+#if HASP_USE_WIFI > 0
+#if defined(STM32F4xx)
+ IPAddress ip;
+ ip = WiFi.localIP();
+ LOG_INFO(TAG_HTTP, F(D_SERVICE_STARTED " @ http://%d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]);
+#else
+ LOG_INFO(TAG_HTTP, F(D_SERVICE_STARTED " @ http://%s"),
+ (WiFi.getMode() != WIFI_STA ? WiFi.softAPIP().toString().c_str() : WiFi.localIP().toString().c_str()));
+#endif
+#else
+ IPAddress ip;
+#if defined(ARDUINO_ARCH_ESP32)
+ ip = ETH.localIP();
+#else
+ ip = Ethernet.localIP();
+#endif
+ LOG_INFO(TAG_HTTP, F(D_SERVICE_STARTED " @ http://%d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]);
+#endif
+}
+
+void httpStop()
+{
+ webServer.stop();
+ webServerStarted = false;
+ LOG_WARNING(TAG_HTTP, F(D_SERVICE_STOPPED));
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+void httpSetup()
+{
+ // httpSetConfig(settings);
+
+ // ask server to track these headers
+ const char* headerkeys[] = {"Content-Length"}; // "Authentication"
+ size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*);
+ webServer.collectHeaders(headerkeys, headerkeyssize);
+
+ // Shared pages
+ webServer.on(F("/about"), webHandleAbout);
+ webServer.onNotFound(httpHandleNotFound);
+
+#if HASP_USE_WIFI > 0
+#if !defined(STM32F4xx)
+
+#if HASP_USE_CONFIG > 0
+ if(WiFi.getMode() != WIFI_STA) {
+ LOG_TRACE(TAG_HTTP, F("Wifi access point"));
+ webServer.on(F("/"), webHandleWifiConfig);
+ webServer.on(F("/config"), webHandleConfig);
+ return;
+ }
+
+#endif
+#endif
+#endif
+
+ webServer.on(F("/page/"), []() {
+ String pageid = webServer.arg(F("page"));
+ webServer.send(200, PSTR("text/plain"), "Page: '" + pageid + "'");
+ haspSetPage(pageid.toInt());
+ });
+
+#if HASP_USE_SPIFFS > 0 || HASP_USE_LITTLEFS > 0
+ webServer.on(F("/list"), HTTP_GET, handleFileList);
+ // load editor
+ webServer.on(F("/edit"), HTTP_GET, []() {
+ if(!handleFileRead("/edit.htm")) {
+ char mimetype[16];
+ snprintf_P(mimetype, sizeof(mimetype), PSTR("text/plain"));
+ webServer.send_P(404, mimetype, PSTR("FileNotFound"));
+ }
+ });
+ webServer.on(F("/edit"), HTTP_PUT, handleFileCreate);
+ webServer.on(F("/edit"), HTTP_DELETE, handleFileDelete);
+ // first callback is called after the request has ended with all parsed arguments
+ // second callback handles file uploads at that location
+ webServer.on(
+ F("/edit"), HTTP_POST,
+ []() {
+ webServer.send(200, "text/plain", "");
+ LOG_VERBOSE(TAG_HTTP, F("Headers: %d"), webServer.headers());
+ },
+ handleFileUpload);
+#endif
+
+ webServer.on(F("/"), webHandleRoot);
+ webServer.on(F("/info"), webHandleInfo);
+ webServer.on(F("/screenshot"), webHandleScreenshot);
+ webServer.on(F("/firmware"), webHandleFirmware);
+ webServer.on(F("/reboot"), httpHandleReboot);
+
+#if HASP_USE_CONFIG > 0
+ webServer.on(F("/config/hasp"), webHandleHaspConfig);
+ webServer.on(F("/config/http"), webHandleHttpConfig);
+ webServer.on(F("/config/gui"), webHandleGuiConfig);
+ webServer.on(F("/config/debug"), webHandleDebugConfig);
+#if HASP_USE_MQTT > 0
+ webServer.on(F("/config/mqtt"), webHandleMqttConfig);
+#endif
+#if HASP_USE_WIFI > 0
+ webServer.on(F("/config/wifi"), webHandleWifiConfig);
+#endif
+#if HASP_USE_GPIO > 0
+ webServer.on(F("/config/gpio"), webHandleGpioConfig);
+ webServer.on(F("/config/gpio/options"), webHandleGpioOptions);
+#endif
+ webServer.on(F("/saveConfig"), webHandleSaveConfig);
+ webServer.on(F("/resetConfig"), httpHandleResetConfig);
+ webServer.on(F("/config"), webHandleConfig);
+#endif // HASP_USE_CONFIG
+
+#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_ESP8266)
+ webServer.on(
+ F("/update"), HTTP_POST,
+ []() {
+ webServer.send(200, "text/plain", "");
+ LOG_VERBOSE(TAG_HTTP, F("Total size: %s"), webServer.hostHeader().c_str());
+ },
+ webHandleFirmwareUpload);
+ webServer.on(F("/espfirmware"), httpHandleEspFirmware);
+#endif
+
+ LOG_INFO(TAG_HTTP, F(D_SERVICE_STARTED));
+ // webStart(); Wait for network connection
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+void httpReconnect()
+{
+ if(!http_config.enable) return;
+
+ if(webServerStarted) {
+ httpStop();
+ } else
+#if HASP_USE_WIFI > 0 && !defined(STM32F4xx)
+ if(WiFi.status() == WL_CONNECTED || WiFi.getMode() != WIFI_STA)
+#endif
+ {
+ httpStart();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+void httpLoop(void)
+{
+ if(http_config.enable) webServer.handleClient();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+void httpEvery5Seconds()
+{
+ // if(httpEnable && !webServerStarted) httpReconnect();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+#if HASP_USE_CONFIG > 0
+bool httpGetConfig(const JsonObject& settings)
+{
+ bool changed = false;
+
+ settings[FPSTR(FP_CONFIG_ENABLE)] = http_config.enable;
+
+ if(http_config.port != settings[FPSTR(FP_CONFIG_PORT)].as()) changed = true;
+ settings[FPSTR(FP_CONFIG_PORT)] = http_config.port;
+
+ if(strcmp(http_config.user, settings[FPSTR(FP_CONFIG_USER)].as().c_str()) != 0) changed = true;
+ settings[FPSTR(FP_CONFIG_USER)] = http_config.user;
+
+ if(strcmp(http_config.password, settings[FPSTR(FP_CONFIG_PASS)].as().c_str()) != 0) changed = true;
+ settings[FPSTR(FP_CONFIG_PASS)] = http_config.password;
+
+ if(changed) configOutput(settings, TAG_HTTP);
+ return changed;
+}
+
+/** Set HTTP Configuration.
+ *
+ * Read the settings from json and sets the application variables.
+ *
+ * @note: data pixel should be formated to uint32_t RGBA. Imagemagick requirements.
+ *
+ * @param[in] settings JsonObject with the config settings.
+ **/
+bool httpSetConfig(const JsonObject& settings)
+{
+ configOutput(settings, TAG_HTTP);
+ bool changed = false;
+
+ changed |= configSet(http_config.port, settings[FPSTR(FP_CONFIG_PORT)], F("httpPort"));
+
+ if(!settings[FPSTR(FP_CONFIG_USER)].isNull()) {
+ changed |= strcmp(http_config.user, settings[FPSTR(FP_CONFIG_USER)]) != 0;
+ strncpy(http_config.user, settings[FPSTR(FP_CONFIG_USER)], sizeof(http_config.user));
+ }
+
+ if(!settings[FPSTR(FP_CONFIG_PASS)].isNull()) {
+ changed |= strcmp(http_config.password, settings[FPSTR(FP_CONFIG_PASS)]) != 0;
+ strncpy(http_config.password, settings[FPSTR(FP_CONFIG_PASS)], sizeof(http_config.password));
+ }
+
+ return changed;
+}
+#endif // HASP_USE_CONFIG
+
+size_t httpClientWrite(const uint8_t* buf, size_t size)
+{
+ /***** Sending 16Kb at once freezes on STM32 EthernetClient *****/
+ size_t bytes_sent = 0;
+ while(bytes_sent < size) {
+ if(!webServer.client()) return bytes_sent;
+ if(size - bytes_sent >= 2048) {
+ bytes_sent += webServer.client().write(buf + bytes_sent, 2048);
+ } else {
+ bytes_sent += webServer.client().write(buf + bytes_sent, size - bytes_sent);
+ }
+ // Serial.println(bytes_sent);
+
+ // stm32_eth_scheduler(); // already in write
+ // webServer.client().flush();
+ delay(1); // Fixes the freeze
+ }
+ return bytes_sent;
+}
+
+#endif
\ No newline at end of file