Просмотр исходного кода

Improve initial setup proc (#1692)

* Initial Test

* Initial functional version.
jomjol 3 лет назад
Родитель
Сommit
85030e39fa

+ 11 - 9
code/components/jomjol_fileserver_ota/server_file.cpp

@@ -895,7 +895,7 @@ void delete_all_in_directory(std::string _directory)
     closedir(dir);
 }
 
-std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::string _target_bin, std::string _main)
+std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::string _target_bin, std::string _main, bool _initial_setup)
 {
     int i, sort_iter;
     mz_bool status;
@@ -905,13 +905,9 @@ std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::st
     char archive_filename[64];
     std::string zw, ret = "";
     std::string directory = "";
-//    static const char* s_Test_archive_filename = "testhtml.zip";
 
     ESP_LOGD(TAG, "miniz.c version: %s", MZ_VERSION);
     ESP_LOGD(TAG, "Zipfile: %s", _in_zip_file.c_str());
-//    ESP_LOGD(TAG, "Target Dir ZIP: %s", _target_zip.c_str());
-//    ESP_LOGD(TAG, "Target Dir BIN: %s", _target_bin.c_str());
-//    ESP_LOGD(TAG, "Target Dir main: %s", _main.c_str());
 
     // Now try to open the archive.
     memset(&zip_archive, 0, sizeof(zip_archive));
@@ -928,7 +924,6 @@ std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::st
 
 
     sort_iter = 0;
-//    for (sort_iter = 0; sort_iter < 2; sort_iter++)
     {
         memset(&zip_archive, 0, sizeof(zip_archive));
         status = mz_zip_reader_init_file(&zip_archive, _in_zip_file.c_str(), sort_iter ? MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY : 0);
@@ -966,6 +961,16 @@ std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::st
                 else
                 {
                     std::string _dir = getDirectory(zw);
+                    if ((_dir == "config-initial") && !_initial_setup)
+                    {
+                        continue;
+                    }
+                    else
+                    {
+                        _dir = "config";
+                        std::string _s1 = "config-initial";
+                        FindReplace(zw, _s1, _dir);
+                    }
 
                     if (_dir.length() > 0)
                     {
@@ -1006,9 +1011,6 @@ std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::st
                 isokay = isokay && RenameFile(filename_zw, zw);
                 if (!isokay)
                     ESP_LOGE(TAG, "ERROR in Rename \"%s\" to \"%s\"", filename_zw.c_str(), zw.c_str());
-//                isokay = isokay && DeleteFile(filename_zw);
-//                if (!isokay)
-//                    ESP_LOGE(TAG, "ERROR in Delete \"%s\"", filename_zw.c_str());
 
                 if (isokay)
                     ESP_LOGI(TAG, "Successfully extracted file \"%s\", size %u", archive_filename, (uint)uncomp_size);

+ 1 - 1
code/components/jomjol_fileserver_ota/server_file.h

@@ -9,7 +9,7 @@
 void register_server_file_uri(httpd_handle_t server, const char *base_path);
 
 void unzip(std::string _in_zip_file, std::string _target_directory);
-std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::string _target_bin, std::string _main = "/sdcard/");
+std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::string _target_bin, std::string _main = "/sdcard/", bool _initial_setup = false);
 
 
 void delete_all_in_directory(std::string _directory);

+ 17 - 2
code/components/jomjol_fileserver_ota/server_ota.cpp

@@ -48,7 +48,7 @@ esp_err_t handler_reboot(httpd_req_t *req);
 static bool ota_update_task(std::string fn);
 
 std::string _file_name_update;
-
+bool initial_setup = false;
 
 
 static void infinite_loop(void)
@@ -76,7 +76,7 @@ void task_do_Update_ZIP(void *pvParameter)
         out = "/sdcard/html";
         outbin = "/sdcard/firmware";
 
-        retfirmware = unzip_new(_file_name_update, out+"/", outbin+"/");
+        retfirmware = unzip_new(_file_name_update, out+"/", outbin+"/", "/sdcard/", initial_setup);
     	LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Files unzipped.");
 
         if (retfirmware.length() > 0)
@@ -84,6 +84,12 @@ void task_do_Update_ZIP(void *pvParameter)
         	LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Found firmware.bin");
             ota_update_task(retfirmware);
         }
+
+        if (initial_setup)
+        {
+
+        }
+
         LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Trigger reboot due to firmware update.");
         doReboot();
     }
@@ -106,6 +112,15 @@ void CheckUpdate()
 	char zw[1024] = "";
 	fgets(zw, 1024, pfile);
     _file_name_update = std::string(zw);
+    if (fgets(zw, 1024, pfile))
+	{
+		std::string _szw = std::string(zw);
+        if (_szw == "init")
+        {
+       		LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Inital Setup triggered.");
+            initial_setup = true;        }
+	}
+
     fclose(pfile);
     DeleteFile("/sdcard/update.txt");   // Prevent Boot Loop!!!
 	LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Update during boot triggered - Update File: " + _file_name_update);

+ 22 - 0
code/components/jomjol_helper/Helper.cpp

@@ -301,6 +301,17 @@ bool RenameFile(string from, string to)
 	return true;
 }
 
+bool FileExists(string filename)
+{
+	FILE* fpSourceFile = fopen(filename.c_str(), "rb");
+	if (!fpSourceFile)	// Sourcefile existiert nicht sonst gibt es einen Fehler beim Kopierversuch!
+	{
+		return false;
+	}
+	fclose(fpSourceFile);
+	return true;    
+}
+
 
 bool DeleteFile(string fn)
 {
@@ -582,6 +593,17 @@ std::vector<string> ZerlegeZeile(std::string input, std::string delimiter)
 }
 
 
+std::string ReplaceString(std::string subject, const std::string& search,
+                          const std::string& replace) {
+    size_t pos = 0;
+    while ((pos = subject.find(search, pos)) != std::string::npos) {
+         subject.replace(pos, search.length(), replace);
+         pos += replace.length();
+    }
+    return subject;
+}
+
+
 /* Source: https://git.kernel.org/pub/scm/utils/mmc/mmc-utils.git/tree/lsmmc.c */
 /* SD Card Manufacturer Database */
 struct SDCard_Manufacturer_database {

+ 2 - 0
code/components/jomjol_helper/Helper.h

@@ -18,6 +18,7 @@ bool CopyFile(string input, string output);
 bool DeleteFile(string fn);
 bool RenameFile(string from, string to);
 bool MakeDir(std::string _what);
+bool FileExists(string filename);
 
 
 string RundeOutput(double _in, int _anzNachkomma);
@@ -31,6 +32,7 @@ string getFileType(string filename);
 string getFileFullFileName(string filename);
 string getDirectory(string filename);
 
+
 int mkdir_r(const char *dir, const mode_t mode);
 int removeFolder(const char* folderPath, const char* logTag);
 

+ 6 - 2
code/components/jomjol_wlan/read_wlanini.cpp

@@ -41,7 +41,7 @@ std::vector<string> ZerlegeZeileWLAN(std::string input, std::string _delimiter =
 
 
 
-void LoadWlanFromFile(std::string fn, char *&_ssid, char *&_password, char *&_hostname, char *&_ipadr, char *&_gw,  char *&_netmask, char *&_dns, int &_rssithreashold)
+bool LoadWlanFromFile(std::string fn, char *&_ssid, char *&_password, char *&_hostname, char *&_ipadr, char *&_gw,  char *&_netmask, char *&_dns, int &_rssithreashold)
 {
     std::string ssid = "";
     std::string passphrase = "";
@@ -59,10 +59,13 @@ void LoadWlanFromFile(std::string fn, char *&_ssid, char *&_password, char *&_ho
     fn = FormatFileName(fn);
 
     pFile = fopen(fn.c_str(), "r");
+    if (!pFile)
+        return false;
+
     ESP_LOGD(TAG, "file loaded");
 
     if (pFile == NULL)
-        return;
+        return false;
 
     char zw[1024];
     fgets(zw, 1024, pFile);
@@ -193,6 +196,7 @@ void LoadWlanFromFile(std::string fn, char *&_ssid, char *&_password, char *&_ho
 
     _rssithreashold = rssithreshold;
     RSSIThreashold = rssithreshold;
+    return true;
 }
 
 

+ 1 - 1
code/components/jomjol_wlan/read_wlanini.h

@@ -5,7 +5,7 @@
 
 #include <string>
 
-void LoadWlanFromFile(std::string fn, char *&_ssid, char *&_password, char *&_hostname, char *&_ipadr, char *&_gw,  char *&_netmask, char *&_dns, int &_rssithreashold);
+bool LoadWlanFromFile(std::string fn, char *&_ssid, char *&_password, char *&_hostname, char *&_ipadr, char *&_gw,  char *&_netmask, char *&_dns, int &_rssithreashold);
 
 bool ChangeHostName(std::string fn, std::string _newhostname);
 bool ChangeRSSIThreashold(std::string fn, int _newrssithreashold);

+ 5 - 0
code/main/main.cpp

@@ -39,6 +39,8 @@
 #include "../../include/defines.h"
 //#include "server_GPIO.h"
 
+#include "softAP.h"
+
 extern const char* GIT_TAG;
 extern const char* GIT_REV;
 extern const char* GIT_BRANCH;
@@ -175,6 +177,8 @@ extern "C" void app_main(void)
 
     CheckOTAUpdate();
     CheckUpdate();
+    CheckStartAPMode();          // if no wlan.ini and/or config.ini --> AP ist startet and this function does not exit anymore until reboot
+
 
     char *ssid = NULL, *passwd = NULL, *hostname = NULL, *ip = NULL, *gateway = NULL, *netmask = NULL, *dns = NULL; int rssithreashold = 0;
     LoadWlanFromFile("/sdcard/wlan.ini", ssid, passwd, hostname, ip, gateway, netmask, dns, rssithreashold);
@@ -318,3 +322,4 @@ extern "C" void app_main(void)
         LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Initialization failed. Not starting flows!");
     }
 }
+

+ 4 - 0
code/main/server_main.cpp

@@ -16,6 +16,8 @@
 #include "server_tflite.h"
 #include "esp_log.h"
 
+#include <stdio.h>
+
 #include "Helper.h"
 
 httpd_handle_t server = NULL;   
@@ -350,6 +352,7 @@ esp_err_t img_tmp_virtual_handler(httpd_req_t *req)
 
 
 
+
 esp_err_t sysinfo_handler(httpd_req_t *req)
 {
     const char* resp_str; 
@@ -421,6 +424,7 @@ void register_server_main_uri(httpd_handle_t server, const char *base_path)
     httpd_register_uri_handler(server, &starttime_tmp_handle);
 
 
+
     httpd_uri_t img_tmp_handle = {
         .uri       = "/img_tmp/*",  // Match all URIs of type /path/to/file
         .method    = HTTP_GET,

+ 489 - 0
code/main/softAP.cpp

@@ -0,0 +1,489 @@
+#include "softAP.h"
+
+/*  WiFi softAP Example
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+#include <string.h>
+#include <string>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "esp_mac.h"
+#include "esp_wifi.h"
+#include "esp_event.h"
+#include "esp_log.h"
+#include "nvs_flash.h"
+
+#include "stdio.h"
+
+#include "ClassLogFile.h"
+#include "server_help.h"
+#include "defines.h"
+#include "Helper.h"
+#include "server_ota.h"
+
+#include "lwip/err.h"
+#include "lwip/sys.h"
+
+/* The examples use WiFi configuration that you can set via project configuration menu.
+   If you'd rather not, just change the below entries to strings with
+   the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
+*/
+#define EXAMPLE_ESP_WIFI_SSID      "AI-on-the-Edge"
+#define EXAMPLE_ESP_WIFI_PASS      ""
+#define EXAMPLE_ESP_WIFI_CHANNEL   11
+#define EXAMPLE_MAX_STA_CONN       1
+
+bool isConfigINI = false;
+bool isWlanINI = false;
+
+static const char *TAG = "wifi softAP";
+
+static void wifi_event_handler(void* arg, esp_event_base_t event_base,
+                                    int32_t event_id, void* event_data)
+{
+    if (event_id == WIFI_EVENT_AP_STACONNECTED) {
+        wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
+        ESP_LOGI(TAG, "station "MACSTR" join, AID=%d",
+                 MAC2STR(event->mac), event->aid);
+    } else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
+        wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
+        ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d",
+                 MAC2STR(event->mac), event->aid);
+    }
+}
+
+void wifi_init_softAP(void)
+{
+    ESP_ERROR_CHECK(esp_netif_init());
+    ESP_ERROR_CHECK(esp_event_loop_create_default());
+    esp_netif_create_default_wifi_ap();
+
+    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
+
+    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
+                                                        ESP_EVENT_ANY_ID,
+                                                        &wifi_event_handler,
+                                                        NULL,
+                                                        NULL));
+
+    wifi_config_t wifi_config = { };
+
+    strcpy((char*)wifi_config.ap.ssid, (const char*) EXAMPLE_ESP_WIFI_SSID);
+    strcpy((char*)wifi_config.ap.password, (const char*) EXAMPLE_ESP_WIFI_PASS);
+    wifi_config.ap.channel = EXAMPLE_ESP_WIFI_CHANNEL;
+    wifi_config.ap.max_connection = EXAMPLE_MAX_STA_CONN;
+    wifi_config.ap.authmode = WIFI_AUTH_WPA_WPA2_PSK;
+
+    if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0) {
+        wifi_config.ap.authmode = WIFI_AUTH_OPEN;
+    }
+
+    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
+    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
+    ESP_ERROR_CHECK(esp_wifi_start());
+
+    ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
+             EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL);
+}
+
+
+void SendHTTPResponse(httpd_req_t *req)
+{
+    std::string message = "<h1>AI-on-the-edge - BASIC SETUP</h1><p>This is an access point with a minimal server to setup the minimum required files and information on the device and the SD-card. ";
+    message += "This mode is always startet if one of the following files is missing: /wlan.ini or the /config/config.ini.<p>";
+    message += "The setup is done in 3 steps: 1. upload full inital configuration (sd-card content), 2. store WLAN acces information, 3. reboot (and connect to WLANs)<p><p>";
+    message += "Please follow the below instructions.<p>";
+    httpd_resp_send_chunk(req, message.c_str(), strlen(message.c_str()));
+
+    isWlanINI = FileExists("/sdcard/wlan.ini");
+
+    if (!isConfigINI)
+    {
+        message = "<h3>1. Upload initial configuration to sd-card</h3><p>";
+        message += "The configuration file config.ini is missing and most propably the full configuration and html folder on the sd-card. ";
+        message += "This is normal after the first flashing of the firmware and an empty sd-card. Please upload \"remote_setup.zip\", which contains an full inital configuration.<p>";
+        message += "<input id=\"newfile\" type=\"file\">";
+        message += "<button class=\"button\" style=\"width:300px\" id=\"doUpdate\" type=\"button\" onclick=\"upload()\">Upload File</button><p>";
+        httpd_resp_send_chunk(req, message.c_str(), strlen(message.c_str()));
+
+        message = "<script language=\"JavaScript\">";
+        message += "function upload() {";
+        message += "var xhttp = new XMLHttpRequest();";
+        message += "xhttp.onreadystatechange = function() {if (xhttp.readyState == 4) {if (xhttp.status == 200) {location.reload();}}};";
+        message += "var filePath = document.getElementById(\"newfile\").value.split(/[\\\\/]/).pop();";
+        message += "var file = document.getElementById(\"newfile\").files[0];";
+        message += "if (!file.name.includes(\"remote-setup\")){if (!confirm(\"The zip file name should contain '...remote-setup...'. Are you sure that you have downloaded the correct file?\"))return;};";
+        message += "var upload_path = \"/upload/firmware/\" + filePath; xhttp.open(\"POST\", upload_path, true); xhttp.send(file);document.reload();}";
+        message += "</script>";
+        isConfigINI = true;
+        httpd_resp_send_chunk(req, message.c_str(), strlen(message.c_str()));
+        return;
+    }
+    if (!isWlanINI)
+    {
+        message = "<h3>2. WLAN access credentials</h3><p>";
+        message = "<table>";
+        message += "<tr><td>WLAN-SSID</td><td><input type=\"text\" name=\"ssid\" id=\"ssid\"></td><td>SSID of the WLAN</td></tr>";
+        message += "<tr><td>WLAN-Password</td><td><input type=\"text\" name=\"password\" id=\"password\"></td><td>ATTENTION: the password will not be encrypted during the sending.</td>";
+        httpd_resp_send_chunk(req, message.c_str(), strlen(message.c_str()));
+
+//        message = "</tr><tr><td> Hostname</td><td><input type=\"text\" name=\"hostname\" id=\"hostname\"></td><td></td>";
+//        message += "</tr><tr><td>Fixed IP</td><td><input type=\"text\" name=\"ip\" id=\"ip\"></td><td>Leave emtpy if set by router</td></tr>";
+//        message += "<tr><td>gateway</td><td><input type=\"text\" name=\"gateway\" id=\"gateway\"></td><td>Leave emtpy if set by router</td></tr>";
+//        message += "<tr><td>netmask</td><td><input type=\"text\" name=\"netmask\" id=\"netmask\"></td><td>Leave emtpy if set by router</td>";
+//        message += "</tr><tr><td>DNS</td><td><input type=\"text\" name=\"dns\" id=\"dns\"></td><td>Leave emtpy if set by router</td></tr>";
+//        message += "<tr><td>RSSI Threashold</td><td><input type=\"number\" name=\"name\" id=\"threashold\" min=\"-100\"  max=\"0\" step=\"1\" value = \"0\"></td><td>WLAN Mesh Parameter: Threashold for RSSI value to check for start switching access point in a mesh system.Possible values: -100 to 0, 0 = disabled - Value will be transfered to wlan.ini at next startup)</td></tr>";
+//        httpd_resp_send_chunk(req, message.c_str(), strlen(message.c_str()));
+
+        message = "/<table><p>";
+        message += "<h4>ATTENTION:<h4>Be sure about the WLAN settings. They cannot be reset afterwards. If ssid or password is wrong, you need to take out the sd-card and manually change them in \"wlan.ini\"!<p>";
+        httpd_resp_send_chunk(req, message.c_str(), strlen(message.c_str()));
+
+        message = "<button class=\"button\" type=\"button\" onclick=\"wr()\">Write wlan.ini</button>";
+        message += "<script language=\"JavaScript\">async function wr(){";
+        message += "api = \"/config?\"+\"ssid=\"+document.getElementById(\"ssid\").value+\"&pwd=\"+document.getElementById(\"password\").value;";
+//        message += "api = \"/config?\"+\"ssid=\"+document.getElementById(\"ssid\").value+\"&pwd=\"+document.getElementById(\"password\").value+\"&hn=\"+document.getElementById(\"hostname\").value+\"&ip=\"+document.getElementById(\"ip\").value+\"&gw=\"+document.getElementById(\"gateway\").value+\"&nm=\"+document.getElementById(\"netmask\").value+\"&dns=\"+document.getElementById(\"dns\").value+\"&rssi=\"+document.getElementById(\"threashold\").value;";
+        message += "fetch(api);await new Promise(resolve => setTimeout(resolve, 1000));location.reload();}</script>";
+        httpd_resp_send_chunk(req, message.c_str(), strlen(message.c_str()));
+        return;
+    }
+
+    message = "<h3>3. Reboot</h3><p>";
+    message += "After triggering the reboot, the zip-files gets extracted and written to the sd-card.<br>The ESP32 will restart two times and then connect to your access point. Please find the IP in your router settings and access it with the new ip-address.<p>";
+    message += "The first update and initialization process can take up to 3 minutes before you find it in the wlan. Errors can be found on the console / serial logout.<p>Have fun!<p>";
+    message += "<button class=\"button\" type=\"button\" onclick=\"rb()\")>Reboot to first setup.</button>";
+    message += "<script language=\"JavaScript\">async function rb(){";
+    message += "api = \"/reboot\";";
+    message += "fetch(api);await new Promise(resolve => setTimeout(resolve, 1000));location.reload();}</script>";
+    httpd_resp_send_chunk(req, message.c_str(), strlen(message.c_str()));
+}
+
+
+
+
+esp_err_t test_handler(httpd_req_t *req)
+{
+    SendHTTPResponse(req);
+    httpd_resp_send_chunk(req, NULL, 0);
+    return ESP_OK;
+}
+
+
+esp_err_t reboot_handlerAP(httpd_req_t *req)
+{
+#ifdef DEBUG_DETAIL_ON     
+    LogFile.WriteHeapInfo("handler_ota_update - Start");    
+#endif
+    LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Trigger reboot due to firmware update.");
+    doReboot();
+    return ESP_OK;
+};
+
+
+esp_err_t config_ini_handler(httpd_req_t *req)
+{
+#ifdef DEBUG_DETAIL_ON     
+    LogFile.WriteHeapInfo("handler_ota_update - Start");    
+#endif
+
+    LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "config_ini_handler");
+    char _query[400];
+    char _valuechar[100];    
+    std::string fn = "/sdcard/firmware/";
+    std::string _task = "";
+    std::string ssid = "";
+    std::string pwd = "";
+    std::string hn = "";    // hostname
+    std::string ip = "";
+    std::string gw = "";    // gateway
+    std::string nm = "";    // nm
+    std::string dns = "";
+    std::string rssi = "";
+
+
+    if (httpd_req_get_url_query_str(req, _query, 400) == ESP_OK)
+    {
+        ESP_LOGD(TAG, "Query: %s", _query);
+        
+        if (httpd_query_key_value(_query, "ssid", _valuechar, 30) == ESP_OK)
+        {
+            ESP_LOGD(TAG, "ssid is found: %s", _valuechar);
+            ssid = std::string(_valuechar);
+        }
+
+        if (httpd_query_key_value(_query, "pwd", _valuechar, 30) == ESP_OK)
+        {
+            ESP_LOGD(TAG, "pwd is found: %s", _valuechar);
+            pwd = std::string(_valuechar);
+        }
+
+        if (httpd_query_key_value(_query, "ssid", _valuechar, 30) == ESP_OK)
+        {
+            ESP_LOGD(TAG, "ssid is found: %s", _valuechar);
+            ssid = std::string(_valuechar);
+        }
+
+        if (httpd_query_key_value(_query, "hn", _valuechar, 30) == ESP_OK)
+        {
+            ESP_LOGD(TAG, "hostname is found: %s", _valuechar);
+            hn = std::string(_valuechar);
+        }
+
+        if (httpd_query_key_value(_query, "ip", _valuechar, 30) == ESP_OK)
+        {
+            ESP_LOGD(TAG, "ip is found: %s", _valuechar);
+            ip = std::string(_valuechar);
+        }
+
+        if (httpd_query_key_value(_query, "gw", _valuechar, 30) == ESP_OK)
+        {
+            ESP_LOGD(TAG, "gateway is found: %s", _valuechar);
+            gw = std::string(_valuechar);
+        }
+
+        if (httpd_query_key_value(_query, "nm", _valuechar, 30) == ESP_OK)
+        {
+            ESP_LOGD(TAG, "netmask is found: %s", _valuechar);
+            nm = std::string(_valuechar);
+        }
+
+        if (httpd_query_key_value(_query, "dns", _valuechar, 30) == ESP_OK)
+        {
+            ESP_LOGD(TAG, "dns is found: %s", _valuechar);
+            dns = std::string(_valuechar);
+        }
+
+        if (httpd_query_key_value(_query, "rssi", _valuechar, 30) == ESP_OK)
+        {
+            ESP_LOGD(TAG, "rssi is found: %s", _valuechar);
+            rssi = std::string(_valuechar);
+        }
+    };
+
+    FILE* configfilehandle = fopen("/sdcard/wlan.ini", "w");
+    
+    if (ssid.length())
+        ssid = "ssid = \"" + ssid + "\"\n";
+    else
+        ssid = ";ssid = \"\"\n";
+
+    fputs(ssid.c_str(), configfilehandle);
+
+    if (pwd.length())
+        pwd = "password = \"" + pwd + "\"\n";
+    else
+        pwd = ";password = \"\"\n";
+    fputs(pwd.c_str(), configfilehandle);
+
+    if (hn.length())
+        hn = "hostname = \"" + hn + "\"\n";
+    else
+        hn = ";hostname = \"\"\n";
+    fputs(hn.c_str(), configfilehandle);
+
+    if (ip.length())
+        ip = "ip = \"" + ip + "\"\n";
+    else
+        ip = ";ip = \"\"\n";
+    fputs(ip.c_str(), configfilehandle);
+
+    if (gw.length())
+        gw = "gateway = \"" + gw + "\"\n";
+    else
+        gw = ";gateway = \"\"\n";
+    fputs(gw.c_str(), configfilehandle);
+
+    if (nm.length())
+        nm = "netmask = \"" + nm + "\"\n";
+    else
+        nm = ";netmask = \"\"\n";
+    fputs(nm.c_str(), configfilehandle);
+
+    if (dns.length())
+        dns = "dns = \"" + dns + "\"\n";
+    else
+        dns = ";dns = \"\"\n";
+    fputs(dns.c_str(), configfilehandle);
+
+    if (rssi.length())
+        rssi = "RSSIThreashold = \"" + rssi + "\"\n";
+    else
+        rssi = ";rssi = \"\"\n";
+    fputs(rssi.c_str(), configfilehandle);
+
+    fflush(configfilehandle);
+    fclose(configfilehandle);
+
+    std::string zw = "ota without parameter - should not be the case!";
+    httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
+    httpd_resp_send(req, zw.c_str(), strlen(zw.c_str())); 
+    httpd_resp_send_chunk(req, NULL, 0);  
+
+    ESP_LOGE(TAG, "end config.ini");
+
+
+    return ESP_OK;
+};
+
+
+esp_err_t upload_post_handlerAP(httpd_req_t *req)
+{
+    printf("Start des Post Handlers\n");
+    MakeDir("/sdcard/config");
+    MakeDir("/sdcard/firmware");
+    MakeDir("/sdcard/html");
+    MakeDir("/sdcard/img_tmp");
+    MakeDir("/sdcard/log");
+    printf("Nach Start des Post Handlers\n");
+
+    LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "upload_post_handlerAP");
+    char filepath[FILE_PATH_MAX];
+    FILE *fd = NULL;
+
+    const char *filename = get_path_from_uri(filepath, "/sdcard",
+                                             req->uri + sizeof("/upload") - 1, sizeof(filepath));
+    if (!filename) {
+        httpd_resp_send_err(req, HTTPD_414_URI_TOO_LONG, "Filename too long");
+        return ESP_FAIL;
+    }
+
+    printf("filepath: %s, filename: %s\n", filepath, filename);
+
+    DeleteFile(std::string(filepath));
+
+
+
+    fd = fopen(filepath, "w");
+    if (!fd) {
+        ESP_LOGE(TAG, "Failed to create file: %s", filepath);
+        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to create file");
+        return ESP_FAIL;
+    }
+
+    ESP_LOGI(TAG, "Receiving file: %s...", filename);
+
+    char buf[1024];
+    int received;
+
+    int remaining = req->content_len;
+
+    printf("remaining: %d\n", remaining);
+
+
+
+    while (remaining > 0) {
+
+        ESP_LOGI(TAG, "Remaining size: %d", remaining);
+        if ((received = httpd_req_recv(req, buf, MIN(remaining, 1024))) <= 0) {
+            if (received == HTTPD_SOCK_ERR_TIMEOUT) {
+                continue;
+            }
+
+            fclose(fd);
+            unlink(filepath);
+
+            ESP_LOGE(TAG, "File reception failed!");
+            httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive file");
+            return ESP_FAIL;
+        }
+
+        if (received && (received != fwrite(buf, 1, received, fd))) {
+            fclose(fd);
+            unlink(filepath);
+
+            ESP_LOGE(TAG, "File write failed!");
+            httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to write file to storage");
+            return ESP_FAIL;
+        }
+
+        remaining -= received;
+    }
+
+    FILE* pfile = fopen("/sdcard/update.txt", "w");
+    std::string _s_zw= "/sdcard" + std::string(filename);
+    fwrite(_s_zw.c_str(), strlen(_s_zw.c_str()), 1, pfile);
+    fclose(pfile);
+
+
+    fclose(fd);
+    ESP_LOGI(TAG, "File reception complete");
+    httpd_resp_set_hdr(req, "Location", "/test");
+    httpd_resp_set_status(req, "303 See Other");
+    httpd_resp_set_hdr(req, "Location", "/test");
+//    httpd_resp_sendstr(req, "File uploaded successfully");
+    httpd_resp_send_chunk(req, NULL, 0);
+
+    ESP_LOGI(TAG, "Update page send out");
+
+//    httpd_resp_sendstr(req, "File uploaded successfully");
+    return ESP_OK;
+}
+
+
+httpd_handle_t start_webserverAP(void)
+{
+    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
+    config.uri_match_fn = httpd_uri_match_wildcard;
+    httpd_handle_t server = NULL;
+    if (httpd_start(&server, &config) == ESP_OK) {
+        // Do something
+    }
+
+    httpd_uri_t reboot_handle = {
+        .uri       = "/reboot",  // Match all URIs of type /path/to/file
+        .method    = HTTP_GET,
+        .handler   = reboot_handlerAP,
+        .user_ctx  = NULL    // Pass server data as context
+    };
+    httpd_register_uri_handler(server, &reboot_handle);
+
+    httpd_uri_t config_ini_handle = {
+        .uri       = "/config",  // Match all URIs of type /path/to/file
+        .method    = HTTP_GET,
+        .handler   = config_ini_handler,
+        .user_ctx  = NULL    // Pass server data as context
+    };
+    httpd_register_uri_handler(server, &config_ini_handle);
+
+    /* URI handler for uploading files to server */
+    httpd_uri_t file_uploadAP = {
+        .uri       = "/upload/*",   // Match all URIs of type /upload/path/to/file
+        .method    = HTTP_POST,
+        .handler   = upload_post_handlerAP,
+        .user_ctx  = NULL    // Pass server data as context
+    };
+    httpd_register_uri_handler(server, &file_uploadAP);
+
+    httpd_uri_t test_uri = {
+        .uri      = "*",
+        .method   = HTTP_GET,
+        .handler  = test_handler,
+        .user_ctx = NULL
+    };
+    httpd_register_uri_handler(server, &test_uri);
+
+    return NULL;
+}
+
+void CheckStartAPMode()
+{
+    isConfigINI = FileExists("/sdcard/config/config.ini");
+    isWlanINI = FileExists("/sdcard/wlan.ini");
+
+    if (!isConfigINI or !isWlanINI)
+    {
+        wifi_init_softAP();
+        start_webserverAP();
+        while(1) { // wait until reboot within task_do_Update_ZIP
+            vTaskDelay(1000 / portTICK_PERIOD_MS);
+        }
+    }
+
+
+}
+
+

+ 19 - 0
code/main/softAP.h

@@ -0,0 +1,19 @@
+#ifndef SOFTAP_H
+#define SOFTAP_H
+
+#include <esp_wifi.h>
+#include <esp_event.h>
+#include <esp_log.h>
+#include <esp_system.h>
+#include <nvs_flash.h>
+#include <sys/param.h>
+#include "nvs_flash.h"
+#include "esp_netif.h"
+#include "esp_eth.h"
+#include "protocol_examples_common.h"
+#include "esp_tls_crypto.h"
+#include <esp_http_server.h>
+
+void CheckStartAPMode();
+
+#endif

+ 97 - 0
sd-card/html/wlan_config.html

@@ -0,0 +1,97 @@
+<table>
+<tr><td>WLAN-SSID</td><td><input type="text" name="ssid" id="ssid"></td><td>SSID of the WLAN</td></tr>
+<tr><td>WLAN-Password</td><td><input type="text" name="password" id="password"></td><td>ATTENTION: the password will not be encrypted during the sending.</td>
+</tr><tr><td> Hostname</td><td><input type="text" name="hostname" id="hostname"></td><td></td>
+</tr><tr><td>Fixed IP</td><td><input type="text" name="ip" id="ip"></td><td>Leave emtpy if set by router</td></tr>
+<tr><td>gateway</td><td><input type="text" name="gateway" id="gateway"></td><td>Leave emtpy if set by router</td></tr>
+<tr><td>netmask</td><td><input type="text" name="netmask" id="netmask"></td><td>Leave emtpy if set by router</td>
+</tr><tr><td>DNS</td><td><input type="text" name="dns" id="dns"></td><td>Leave emtpy if set by router</td></tr>
+<tr><td>RSSI Threashold</td><td><input type="number" name="name" id="threashold" min="-100"  max="0" step="1" value = "0"></td><td>WLAN Mesh Parameter: Threashold for RSSI value to check for start switching access point in a mesh system.Possible values: -100 to 0, 0 = disabled - Value will be transfered to wlan.ini at next startup)</td></tr>
+</table>
+<button class="button" type="button" onclick="wr()">Write wlan.ini</button>
+<input id="newfile" type="file">
+<button class="button" style="width:300px" id="doUpdate" type="button" onclick="upload()">Upload Files</button>
+
+
+<script language="JavaScript">function wr(){
+        api = "/config?"+"ssid"+document.getElementById("ssid").value+"&pwd="+document.getElementById("password").value;+"&hn="+document.getElementById("hostname").value;+"&ip="+document.getElementById("ip").value;+"&gw="+document.getElementById("gateway").value;+"&nm="+document.getElementById("netmask").value;+"&dns="+document.getElementById("dns").value;+"&rssi="+document.getElementById("threashold").value;
+        fetch(api);}
+
+    function upload() {
+        var xhttp = new XMLHttpRequest();
+        xhttp.onreadystatechange = function() {
+        if (xhttp.readyState == 4) {stopProgressTimer();if (xhttp.status == 200) {extract();} }};
+        var filePath = document.getElementById("newfile").value.split(/[\\\/]/).pop();
+        var file = document.getElementById("newfile").files[0];
+        if (!file.name.includes("remote-setup"))
+        {
+            if (!confirm("The zip file name should contain \"...remote-setup...\". Are you sure that you have downloaded the correct file?"))
+                return;
+        }
+
+        var upload_path = "/upload/firmware/" + filePath; xhttp.open("POST", upload_path, true); xhttp.send(file);}
+    
+    
+    function extract() {
+        document.getElementById("status").innerText = "Status: Processing on device (takes up to 3 minutes)...";
+    
+        var xhttp = new XMLHttpRequest();
+        /* first delete the old firmware */	
+        xhttp.onreadystatechange = function() {
+            if (xhttp.readyState == 4) {
+                stopProgressTimer();
+                if (xhttp.status == 200) {
+                    document.getElementById("status").innerText = "Status: Update completed!";
+                    document.getElementById("doUpdate").disabled = true;
+                    document.getElementById("newfile").disabled = false;
+                    document.cookie = "page=overview.html"; // Make sure after the reboot we go to the overview page
+    
+                    if (xhttp.responseText.startsWith("reboot"))
+                    {
+                        doRebootAfterUpdate();
+                    }
+                    else
+                    {
+                        alert("Processing done!\n\n" + xhttp.responseText);
+                    }
+                } else if (xhttp.status == 0) {
+                    alert("Server closed the connection abruptly!");
+                    UpdatePage();
+                } else {
+                    alert(xhttp.status + " Error!\n" + xhttp.responseText);
+                    UpdatePage();
+                }
+            }
+        };
+    
+        startProgressTimer("Extraction");
+    
+    
+        var nameneu = document.getElementById("newfile").value;
+        filePath = nameneu.split(/[\\\/]/).pop();
+        var _toDo = basepath + "/ota?task=update&file=" + filePath;
+        xhttp.open("GET", _toDo, true);
+        xhttp.send();
+    }
+    
+    
+    function startProgressTimer(step) {     
+        console.log(step + "...");
+        document.getElementById('progress').innerHTML = "(0s)";
+        action_runtime = 0;    
+        progressTimerHandle = setInterval(function() {
+            action_runtime += 1;
+            console.log("Progress: " + action_runtime + "s");  
+            document.getElementById('progress').innerHTML = "(" + action_runtime + "s)";
+        }, 1000);
+    }
+    
+    
+    function stopProgressTimer() {            
+        clearInterval(progressTimerHandle);
+        document.getElementById('progress').innerHTML = "";
+    }
+    
+
+    
+    </script>