diff --git a/.drone.yml b/.drone.yml index f7b6ec6..aee63ac 100644 --- a/.drone.yml +++ b/.drone.yml @@ -7,6 +7,7 @@ steps: commands: - echo "clear out local cache in case we are running locally" - rm -Rf /drone/src/.cpcache + - rm -f /drone/src/resources/public/*.js - clojure -m figwheel.main --build-once prod - name: compress @@ -14,7 +15,6 @@ steps: commands: - apk add gzip - cd /drone/src/resources/public/ - - rm -f /drone/src/resources/public/*.js - rm -f /drone/src/resources/public/*.gz - rm -f /drone/src/resources/public/webfonts/*.gz - gzip *.css *.js *.html *.png -k diff --git a/example/example.ino b/example/example.ino new file mode 100644 index 0000000..8a4f7d4 --- /dev/null +++ b/example/example.ino @@ -0,0 +1,77 @@ +#include "weecfg.h" +#include +#include +#include + + +// array indexes of form field values +// these should match the order in form_fields +// defining in this way makes your code clearer +// you can then access config[cfg_device_id] to get the value +#define cfg_device_id 0 +#define cfg_wifi_ssid 1 +#define cfg_wifi_key 2 +#define cfg_server 3 + +const char *form_fields[] = {"device-id", "wifi-ssid", "wifi-key", "server"}; +int number_of_fields = 3; // total form fields minus 1 because zero based arrays +char config[3][50] = {}; + +bool setup_wifi(String hostname, char ssid[], char password[], int retrys) { + + Serial.print("Connecting to "); + Serial.println(ssid); + + WiFi.mode(WIFI_STA); + WiFi.persistent(false); + WiFi.hostname(hostname); + WiFi.begin(ssid, password); + + int retry_count = 0; + // Might as well keep working if internet is unavailable + while (WiFi.status() != WL_CONNECTED && retry_count < retrys) { + delay(1000); + Serial.print("."); + retry_count += 1; + } + Serial.println(""); + + if (WiFi.status() == WL_CONNECTED) { + Serial.println("WiFi connected"); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + return true; + } + Serial.print("WiFi unable to connect with status of "); + Serial.println(WiFi.status()); + WiFi.disconnect(true); + delay(2000); + return false; +} + + +void setup() { + Serial.begin(115200); + + // config_startup will return the mode it started in + // either AP or normal mode + if (config_startup("/config.txt", form_fields, config, number_of_fields) == + WEECFG_ACCESS_POINT_MODE) { + Serial.println("Please setup."); + return; + } + + // config loaded + if (setup_wifi("wemos", config[cfg_wifi_ssid], config[cfg_wifi_key], 20) == + false) { + // wifi connection failed fal back to access point mode + config_access_point(form_fields, number_of_fields); + return; + } + Serial.println("Connected."); + // continue setup here +} + +void loop() { + // put actual code here +} diff --git a/example/weecfg.cpp b/example/weecfg.cpp new file mode 100644 index 0000000..df9ae32 --- /dev/null +++ b/example/weecfg.cpp @@ -0,0 +1,140 @@ +#include "ESPAsyncWebServer.h" +//#include +#include "weecfg.h" +#include +#include +#include +#include +#include + +bool weecfg_compressed = ""; +bool weecfg_debug = true; +bool config_mode = WEECFG_NORMAL_MODE; +AsyncWebServer server(80); + +void debug(const char msg[]) { + if (weecfg_debug == false) + return; + Serial.println(msg); +} + +void info(const char msg[]) { Serial.println(msg); } + +bool loadConfig(String fileName, const char *form_fields[], char (*config)[50], + int number_of_fields) { + SPIFFS.begin(); + File dataFile = SPIFFS.open(fileName, "r"); + // no file so fail loadings + if (!dataFile) + return false; + char *config_key; + char *config_value; + + while (dataFile.available()) { + // Lets read line by line from the file + String line = dataFile.readStringUntil('\n'); + char char_array[line.length()]; + // convert to char array so wee can string compare + line.toCharArray(char_array, line.length()); + config_key = strtok(char_array, "="); + config_value = strtok(NULL, "="); + + // if strtok does not find a character it returns a null pointer + // so handle it and skip the rest of this iteration + if (config_key == NULL || config_value == NULL) { + continue; + } + for (int i = 0; i < number_of_fields + 1; i++) { + if (strcmp(form_fields[i], config_key) == 0) { + strcpy(config[i], config_value); + } + } + } + info("+++ Loaded configuration from config.txt"); + for (int i = 0; i <= number_of_fields; i++) { + debug(form_fields[i]); + debug(config[i]); + } + return true; +} + +bool handlePayload(AsyncWebServerRequest *request, const char *form_fields[], + int number_of_fields) { + if (request->args() != number_of_fields) + request->send(404, "text/plain", "Missing params."); + + File dataFile = SPIFFS.open("/config.txt", "w"); + for (int i = 0; i < number_of_fields + 1; i++) { + int str_len = request->argName(i).length() + 1; + char char_array[str_len]; + request->argName(i).toCharArray(char_array, str_len); + for (int f = 0; f < number_of_fields + 1; f++) { + if (strcmp(form_fields[f], char_array) == 0) { + dataFile.print(request->argName(i)); + dataFile.print("="); + dataFile.println(request->arg(i)); + } + } + } + + dataFile.close(); + request->send(200, "text/plain", "success"); + info("Config saved restarting with new settings."); + WiFi.softAPdisconnect(true); + WiFi.disconnect(true); + delay(2000); + ESP.restart(); + return true; +} + +int config_access_point(const char *form_fields[], int number_of_fields) { + info("Access point launched, please connect to weeeecfg and configure " + "device."); + WiFi.disconnect(); + WiFi.hostname("WEECFGAP"); + WiFi.persistent(false); + WiFi.mode(WIFI_AP); + + if (WiFi.softAP("weeeecfg", "weeeecfg") == false) // Start the access point + debug("Failed to launch access point, values to short must be 8 chars or " + "greater."); + info("Config page available on IP address:\t"); + info(WiFi.softAPIP().toString().c_str()); + + // access point configured to setup required routes + server.rewrite("/", "/index.html"); + server.serveStatic("/index.html", SPIFFS, "/index.html" WEECFG_COMPRESSED); + server.serveStatic("/tachyon.css", SPIFFS, "/tachyon.css" WEECFG_COMPRESSED); + server.serveStatic("/all.min.css", SPIFFS, "/all.min.css" WEECFG_COMPRESSED); + server.serveStatic("/form.edn", SPIFFS, "/form.edn" WEECFG_COMPRESSED); + server.serveStatic("/main.js", SPIFFS, "/main.js" WEECFG_COMPRESSED); + server.serveStatic("/dev-main.js", SPIFFS, "/dev-main.js" WEECFG_COMPRESSED); + server.serveStatic("/faavicon.ico", SPIFFS, "/favicon.ico" WEECFG_COMPRESSED); + server.serveStatic("/webfonts/fa-solid-900.woff2", SPIFFS, + "/fa-solid-900.woff2" WEECFG_COMPRESSED); + // only serve the config file in debug mode + if (weecfg_debug == true) + server.serveStatic("/config.txt", SPIFFS, "/config.txt"); + + // Route to actually store the config file. + server.on("/save", HTTP_GET, + [form_fields, number_of_fields](AsyncWebServerRequest *request) { + handlePayload(request, form_fields, number_of_fields); + }); + + server.begin(); + config_mode = WEECFG_ACCESS_POINT_MODE; + return config_mode; +} + +int config_startup(String fileName, const char *fields[], char (*config)[50], + int number_of_fields) { + SPIFFS.begin(); + if (loadConfig(fileName, fields, config, number_of_fields) == true) { + info("Configuration file detected and loaded."); + return config_mode; + } + config_mode = WEECFG_ACCESS_POINT_MODE; + config_access_point(fields, number_of_fields); + return config_mode; +} diff --git a/example/weecfg.h b/example/weecfg.h new file mode 100644 index 0000000..60aafc4 --- /dev/null +++ b/example/weecfg.h @@ -0,0 +1,15 @@ +#ifndef device_setup_H +#define device_setup_H + +#define WEECFG_COMPRESSED ".gz" +#define WEECFG_NORMAL_MODE 0 +#define WEECFG_ACCESS_POINT_MODE 1 + + +bool loadConfig(String fileName, char (*config)[50], int number_of_elements); +bool handleRequestedPage(const char *form_fields[], int number_of_fields); +void config_via_access_point(); +int config_access_point(const char *form_fields[], int number_of_fields); +int config_startup(String fileName, const char *fields[], char (*config)[50], int number_of_fields); + +#endif