├── LICENSE.txt ├── README.md ├── examples ├── EBS_Example05_SPIFFSandWebServer │ ├── EBS_Example05_SPIFFSandWebServer.ino │ ├── data │ │ ├── edit.htm.gz │ │ ├── favicon.ico │ │ ├── graphs.js.gz │ │ └── index.htm │ ├── server32.ino │ └── server8266.ino ├── EBS_example01_ParametersEEPROMMap │ ├── EBS_example01_ParametersEEPROMMap.ino │ └── config.json ├── EBS_example01a_ParametersEEPROMMap │ ├── EBS_example01a_ParametersEEPROMMap.ino │ └── config.json ├── EBS_example02_ParametersEEPROM │ ├── EBS_example02_ParametersEEPROM.ino │ ├── config.json │ └── config_comments.json ├── EBS_example02a_ParametersEEPROM │ ├── EBS_example02a_ParametersEEPROM.ino │ └── config.json ├── EBS_example03_ParametersEEPROM_alt │ ├── EBS_example03_ParametersEEPROM_alt.ino │ └── config.json ├── EBS_example03a_ParametersEEPROM_alt │ ├── EBS_example03a_ParametersEEPROM_alt.ino │ └── config.json ├── EBS_example03b_ParametersEEPROM_alt │ └── EBS_example03b_ParametersEEPROM_alt.ino ├── EBS_example04_ParametersSPIFFS │ ├── EBS_example04_ParametersSPIFFS.ino │ └── data │ │ └── EBS4.json └── EBS_example04a_ParametersSPIFFS │ ├── EBS_example04a_ParametersSPIFFS.ino │ └── data │ └── EBS4.json ├── keywords.txt ├── library.json ├── library.properties └── src ├── EspBootstrapBase.h ├── EspBootstrapDict.h ├── EspBootstrapMap.h ├── JsonConfigBase.h ├── JsonConfigHttp.h ├── JsonConfigHttpMap.h ├── JsonConfigSPIFFS.h ├── JsonConfigSPIFFSMap.h ├── ParametersBase.h ├── ParametersEEPROM.h ├── ParametersEEPROMMap.h └── ParametersSPIFFS.h /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2020, Anatoli Arkhipenko. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its contributors 15 | may be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 22 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 24 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EspBootstrap 2 2 | 3 | ### A simple way to get your ESP8266 or ESP32 project configured and online quickly 4 | 5 | [![arduino-library-badge](https://www.ardu-badge.com/badge/EspBootstrap.svg?)](https://www.ardu-badge.com/EspBootstrap) 6 | 7 | #### Background 8 | 9 | A typical journey of an IoT device on a first boot is: 10 | 11 | 1. Connect to WiFi network 12 | 2. Check for firmware update (download and install if available) 13 | 3. Connect to all the online services it has to connect to 14 | 4. Start doing whatever it was built to do 15 | 16 | The problem is: how would a device know which WiFi to connect to? Or how would it know that configuration of the communication network has changed? 17 | 18 | That's when **EspBootstrap** comes to rescue: you can quickly provision initial configuration parameters, get your device connected to the WiFi, load, parse and store to EEPROM full set of configuration parameters, and be ready for doing whatever amazing things you plan to do. 19 | 20 | 21 | 22 | ### **Concept** 23 | 24 | **EspBootstrap** consists of three major components: 25 | 26 | **Parameters** - a helper class that simplifies storing and loading configuration parameters in the EEPROM memory or SPIFFS filesystem of a device. 27 | 28 | **EspBootstrap** - a helper class to quickly collect configuration parameter values from a user via simple web form in an Access Point mode 29 | 30 | **JsonConfig** - a helper class to download/read and parse configuration file (simple JSON format) and populate respective configuration parameters 31 | 32 | Both direct structure mapping and use of [Dictionary](https://github.com/arkhipenko/Dictionary.git) data type is supported. Using dictionary is a simpler way, but may not be suitable for AVR devices with small memory. 33 | 34 | Dictionary approach is the default. 35 | 36 | 37 | 38 | ## DICTIONARY APPROACH: 39 | 40 | #### Typical Implementation Journey 41 | 42 | (see [example #2](https://github.com/arkhipenko/EspBootstrap/blob/master/examples/EBS_example02_ParametersEEPROM/EBS_example02_ParametersEEPROM.ino)) 43 | 44 | 1. Create a Dictionary object (Make sure there is a "Title" key created as a first dictionary element for the web form) 45 | 2. Pass dictionary to appropriate **ESPBootstrap** and **JSONConfig** objects to load or obtain configuration from a user, device file system and/or from the web 46 | 47 | 48 | 49 | #### Typical device boot process 50 | 51 | 1. Attempt to load parameters from EEPROM or file (fails first time since nothing was ever saved) 52 | 53 | 2. Collect initial configuration parameters via web form (http://10.1.1.1) created by **ESPBootstrap** (typically a WiFi SSID, password and a link to web-based configuration service) 54 | 55 | **NOTE**: fields with key containing words "password" or "pwd" will have characters masked with a `*` symbol. 56 | 57 | 3. Reboot and connect to WiFi with the recently obtained credentials 58 | 59 | 4. Load JSON configuration file from the web service or filesystem using **JSONConfig** and populate respective configuration parameters into the provided dictionary object 60 | 61 | 5. Save new configuration to EEPROM or file 62 | 63 | 6. Check for OTA update, download, install and reboot if update is available 64 | 65 | 7. Resume normal operation, periodically checking for OTA updates and config changes. 66 | 67 | 68 | 69 | 70 | 71 | ## STRUCTURE MAPPING APPROACH: 72 | 73 | #### Typical Implementation Journey 74 | 75 | (see [example #1](https://github.com/arkhipenko/EspBootstrap/blob/master/examples/EBS_example01_ParametersEEPROMMap/EBS_example01_ParametersEEPROMMap.ino)) 76 | 77 | 1. Define Parameters Structure 78 | 2. Define Parameter "defaults" 79 | 3. Describe Parameter structure layout for **ESPBootstrap** and **JSONConfig** 80 | 4. Load or obtain configuration from a user and/or from the web 81 | 82 | 83 | 84 | #### Typical device boot process 85 | 86 | 1. Attempt to load parameters from EEPROM/filesystem (fails first time since nothing was ever saved) 87 | 2. Collect initial configuration parameters via web form (http://10.1.1.1) created by **EspBootstrap** (typically a WiFi SSID, password and a link to web-based configuration service) 88 | 3. Reboot and connect to WiFi with the recently obtained credentials 89 | 4. Load JSON configuration file from the web service or a file using **JsonWebConfig** and populate respective configuration parameters 90 | 5. Save new configuration to EEPROM or to the filesystem 91 | 6. Check for OTA update, download, install and reboot if update is available 92 | 7. Resume normal operation, periodically checking for OTA updates and config changes. 93 | 94 | 95 | 96 | ## Object Types 97 | 98 | Types of **Parameters**, **JsonConfig** and **EspBootstrap** objects should be chosen based on where you want to store your configuration and where you want to source your updates from: 99 | 100 | 101 | 102 | #### Dictionary-based parameters, EEPROM storage, HTTP update 103 | 104 | ```c++ 105 | #include 106 | #include 107 | #include 108 | ``` 109 | 110 | See examples #2 and #3 (3 and 3b) for implementation details. 111 | 112 | 113 | 114 | #### Memory structure-based parameters, EEPROM storage, HTTP update 115 | 116 | ```C++ 117 | #include 118 | #include 119 | #include 120 | ``` 121 | 122 | See example #1 for implementation details. 123 | 124 | 125 | 126 | #### Dictionary-based parameters, SPIFFS filesystem, storage 127 | 128 | ```C++ 129 | #include 130 | #include 131 | ``` 132 | 133 | Parameters object uses **JsonConfig** file load/save capability to store the parameters. 134 | 135 | See examples #4 and #5 for implementation details. 136 | 137 | 138 | 139 | #### Full list of possible object types: 140 | 141 | ```C++ 142 | EspBootstrapDict 143 | EspBootstrapMap 144 | 145 | ESPBootstrap - static object to use for processing (correct type is assigned) 146 | ``` 147 | 148 | ```C++ 149 | JsonConfigHTTP 150 | JsonConfigHTTPMap 151 | JsonConfigSPIFFS 152 | JsonConfigSPIFFSMap 153 | 154 | JSONConfig - static object to use for processing (correct type is assigned) 155 | ``` 156 | 157 | ```C++ 158 | ParametersEEPROM 159 | ParametersEEPROMMap 160 | ParametersSPIFFS 161 | ``` 162 | 163 | If additional storage or update types are required, they could be implemented later (e.g., ParametersSD or JsonConfigFTP) 164 | 165 | **NOTE:** Only one type of storage is supported with the static `ESPBootstrap` and `JSONConfig` objects by default. This should cover 99% of the use cases. However, if you need to support multiple storage types, compile the library with `_JSONCONFIG_NOSTATIC` compile option and create appropriate objects explicitly. 166 | 167 | 168 | 169 | ## ERROR CODES: 170 | 171 | ### Parameters: 172 | 173 | ``` 174 | #define PARAMS_OK 0 175 | #define PARAMS_ERR (-1) 176 | #define PARAMS_LEN (-2) 177 | #define PARAMS_CRC (-3) 178 | #define PARAMS_TOK (-4) 179 | #define PARAMS_MEM (-98) 180 | #define PARAMS_ACT (-99) 181 | ``` 182 | 183 | 184 | 185 | `PARAMS_OK` - operation finished successfully 186 | 187 | `PARAMS_ERR` - operation finished with an error, unspecified 188 | 189 | `PARAMS_LEN` - requested size or EPROM size is not sufficient to store parameters 190 | 191 | `PARAMS_CRC` - data inconsistency between parameters in memory and in EEPROM 192 | 193 | `PARAMS_TOK` - parameter tokens do not match 194 | 195 | `PARAMS_MEM` - failed to allocate memory for parameters buffer 196 | 197 | `PARAMS_ACT` - parameters engine was not activated with `begin()` method (not allocated) 198 | 199 | 200 | 201 | ### EspBootstrap: 202 | 203 | ``` 204 | #define BOOTSTRAP_OK 0 205 | #define BOOTSTRAP_ERR (-1) 206 | #define BOOTSTRAP_TIMEOUT (-99) 207 | ``` 208 | 209 | 210 | 211 | `BOOTSTRAP_OK` - bootstrap was successful. Parameters were entered and stored. No timeouts. 212 | 213 | `BOOTSTRAP_ERR` - bootstrap process ended with errors (e.g., webserver failed to initiate) 214 | 215 | `BOOTSTRAP_TIMEOUT` - bootstrap process ran out of time waiting for user inputs. 216 | 217 | 218 | 219 | ### JsonConfig: 220 | 221 | ``` 222 | #define JSON_OK 0 223 | #define JSON_ERR (-1) 224 | #define JSON_COMMA (-20) 225 | #define JSON_COLON (-21) 226 | #define JSON_QUOTE (-22) 227 | #define JSON_BCKSL (-23) 228 | #define JSON_MEM (-24) 229 | #define JSON_FMT (-25) 230 | #define JSON_HTTPERR (-97) 231 | #define JSON_NOWIFI (-98) 232 | #define JSON_EOF (-99) 233 | ``` 234 | 235 | 236 | 237 | `JSON_OK` - operation finished successfully. All fields were processed as expected. 238 | 239 | `JSON_ERR` - operation finished with unspecified error (reserved for future use and/or testing) 240 | 241 | `JSON_COMMA` - missing comma. (Detected ":" before detecting a value) 242 | 243 | `JSON_COLON` - missing colon. (Detected a comma or a new line before detecting a value) 244 | 245 | `JSON_QUOTE` - missing closing quotation mark 246 | 247 | `JSON_BCKSL` - orphaned back-slash. Back-slash not followed by a "verbatim" character. 248 | 249 | `JSON_MEM` - error allocating memory for dictionary entry 250 | 251 | `JSON_FMT` - incorrect JSON formatting (invalid character) 252 | 253 | `JSON_HTTPERR` - general HTTP error. Cannot initiate a connection to provided URL. 254 | 255 | `JSON_NOWIFI` - device is not connected to WiFi 256 | 257 | `JSON_EOF` - unexpected end of file. 258 | 259 | **JsonConfig** also returns all HTTP error codes in case of HTTP GET operation failure. 260 | For instance: `404` - page not found. 261 | 262 | 263 | 264 | ------ 265 | 266 | ###### EspBootstrap v1 is deprecated, and no longer included in the library. 267 | -------------------------------------------------------------------------------- /examples/EBS_Example05_SPIFFSandWebServer/EBS_Example05_SPIFFSandWebServer.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020, Anatoli Arkhipenko 3 | All rights reserved. 4 | 5 | This example illustrates how to to read/write parameters to the filesystem and 6 | add/delete entries to the configuration dictionary object. 7 | 8 | For this example to run you need to upload the "data" folder into the device's SPIFFS 9 | filesystem using ESP8266 or ESP32 Sketch Data Upload tools 10 | https://github.com/esp8266/arduino-esp8266fs-plugin.git 11 | https://github.com/me-no-dev/arduino-esp32fs-plugin.git 12 | 13 | */ 14 | // Prameters token 15 | #define CTOKEN "EBS5" 16 | 17 | // Enable short debug printouts 18 | #define _DEBUG_ 19 | //#define _TEST_ 20 | 21 | #ifdef _DEBUG_ 22 | #define _PP(a) Serial.print(a); 23 | #define _PL(a) Serial.println(a); 24 | #else 25 | #define _PP(a) 26 | #define _PL(a) 27 | #endif 28 | 29 | 30 | // ==== Includes =================================== 31 | #include 32 | #include 33 | // Includes for running the webserver 34 | // SPIFFS browsing example. 35 | #if defined( ARDUINO_ARCH_ESP8266 ) 36 | #include 37 | #include 38 | #define WebServer ESP8266WebServer 39 | #endif 40 | 41 | #if defined( ARDUINO_ARCH_ESP32 ) 42 | #include 43 | #include 44 | #define WebServer WebServer 45 | #endif 46 | 47 | #include 48 | 49 | // The next header file needs to be created for your network 50 | // I place it into "libraries/MySettings" folder. 51 | // It should contain SSID1 and PWD1 defines 52 | // e.g., 53 | // #define SSID1 "your ssid" 54 | // #define PWD1 "your password" 55 | #include "home_wifi_multi.h" 56 | 57 | FS* filesystem = &SPIFFS; 58 | WebServer server(80); 59 | File fsUploadFile; 60 | 61 | const String TOKEN(CTOKEN); 62 | 63 | // Create dictionary object and parameters object 64 | // to be stored on the filesystem 65 | Dictionary d; 66 | ParametersSPIFFS p(TOKEN, d); 67 | 68 | 69 | // Utility method to print contents of a dictionary 70 | void printD(Dictionary& a) { 71 | _PL("\nDictionary content:"); 72 | for (int i = 0; i < a.count(); i++) { 73 | _PP(a(i)); _PP(" : "); _PL(a[i]); 74 | } 75 | _PP("Current count = "); _PL(a.count()); 76 | _PP("Current size = "); _PL(a.size()); 77 | _PL(); 78 | } 79 | 80 | 81 | void setup(void) { 82 | bool wifiTimeout; 83 | int rc; 84 | String id; 85 | 86 | #ifdef _DEBUG_ 87 | Serial.begin(115200); 88 | delay(500); 89 | { 90 | _PL("\n\nEspBootStrap SPIFFS Example"); _PL(); 91 | #if defined( ARDUINO_ARCH_ESP8266 ) 92 | id = "ESP8266-" + String(ESP.getChipId(), HEX); 93 | #endif 94 | #if defined( ARDUINO_ARCH_ESP32 ) 95 | id = "ESP32-" + String((uint32_t)( ESP.getEfuseMac() & 0xFFFFFFFFL ), HEX); 96 | #endif 97 | _PP("ESP Chip ID: "); _PL(id); 98 | _PL(); 99 | } 100 | #endif 101 | 102 | // The sketch is responsible for activating SPIFFS 103 | SPIFFS.begin(); 104 | 105 | // Populate the dictionary with test values 106 | d("This", "is test"); 107 | d("Title", "ESP Test 04"); 108 | d("ssid", SSID1); 109 | d("pwd", PWD1); 110 | d("cfg_url", String("/") + TOKEN + ".json"); 111 | printD(d); 112 | 113 | // Start the parameters object 114 | if ( (rc=p.begin()) != PARAMS_OK) { 115 | _PP("Parameters begin returned an error: "); _PL(rc); 116 | for (;;) ; // something is wrong with EEPROM!! 117 | } 118 | 119 | // Load values from the file on SPIFFS system 120 | // and then try to connect to wifi 121 | if ( (rc = p.load()) == PARAMS_OK) { 122 | setupWifi(d["ssid"].c_str(), d["pwd"].c_str()); 123 | wifiTimeout = waitForWifi(30 * BOOTSTRAP_SECOND); 124 | } 125 | 126 | // Bootstrap the device if paramters load was not successful 127 | // or if connection to wifi failed. 128 | if (rc != PARAMS_OK || wifiTimeout) { 129 | _PL("Device requires bootstrapping..."); 130 | _PP("Connect to WiFi AP: "); _PL(id); 131 | _PL("and navigate to http://10.1.1.1"); 132 | if ( ESPBootstrap.run(d) == BOOTSTRAP_OK ) p.save(); 133 | delay(1000); 134 | ESP.restart(); 135 | } 136 | 137 | // Try saving parameters into JSON file again 138 | // testing overwrite 139 | rc = p.save(); 140 | _PP("Param File Save finished. rc = "); _PL(rc); 141 | 142 | // Delete all entries from the dictionary 143 | _PL("Deleting all entries in the dictionary"); 144 | while (d.count()) { 145 | d.remove(d(0)); 146 | } 147 | printD(d); 148 | 149 | // Load all entries from the file back into the dictionaries 150 | _PL("Loading all entries back"); 151 | rc = p.load(); 152 | 153 | // and add one more line to test dictionary still works. 154 | d("new line", "new value"); 155 | printD(d); 156 | 157 | // Set up webserver to allow browing of the file system 158 | setupServer(); 159 | } 160 | 161 | void loop(void) { 162 | server.handleClient(); 163 | } 164 | 165 | 166 | // This method prepares for WiFi connection 167 | void setupWifi(const char* ssid, const char* pwd) { 168 | _PL("Setup_wifi()"); 169 | 170 | // We start by connecting to a WiFi network 171 | _PL("Connecting to WiFi..."); 172 | // clear wifi config 173 | WiFi.disconnect(); 174 | 175 | WiFi.mode(WIFI_STA); 176 | WiFi.begin(ssid, pwd); 177 | } 178 | 179 | // This method waits for a WiFi connection for aTimeout milliseconds. 180 | bool waitForWifi(unsigned long aTimeout) { 181 | _PL("WaitForWifi()"); 182 | 183 | unsigned long timeNow = millis(); 184 | 185 | while ( WiFi.status() != WL_CONNECTED ) { 186 | delay(1000); 187 | _PP("."); 188 | if ( millis() - timeNow > aTimeout ) { 189 | _PL(" WiFi connection timeout"); 190 | return true; 191 | } 192 | } 193 | 194 | _PL(" WiFi connected"); 195 | _PP("IP address: "); _PL(WiFi.localIP()); 196 | _PP("SSID: "); _PL(WiFi.SSID()); 197 | _PP("mac: "); _PL(WiFi.macAddress()); 198 | _PL(); 199 | _PP("Navigate to http://"); _PP(WiFi.localIP()); _PL("/edit"); 200 | return false; 201 | } 202 | -------------------------------------------------------------------------------- /examples/EBS_Example05_SPIFFSandWebServer/data/edit.htm.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arkhipenko/EspBootstrap/17a76786dd16bb863ae666644cace9a0bda14722/examples/EBS_Example05_SPIFFSandWebServer/data/edit.htm.gz -------------------------------------------------------------------------------- /examples/EBS_Example05_SPIFFSandWebServer/data/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arkhipenko/EspBootstrap/17a76786dd16bb863ae666644cace9a0bda14722/examples/EBS_Example05_SPIFFSandWebServer/data/favicon.ico -------------------------------------------------------------------------------- /examples/EBS_Example05_SPIFFSandWebServer/data/graphs.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arkhipenko/EspBootstrap/17a76786dd16bb863ae666644cace9a0bda14722/examples/EBS_Example05_SPIFFSandWebServer/data/graphs.js.gz -------------------------------------------------------------------------------- /examples/EBS_Example05_SPIFFSandWebServer/data/index.htm: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | 21 | 22 | ESP Monitor 23 | 24 | 85 | 86 | 87 |
88 | 89 | 90 | 91 | 92 |
93 |
94 |
95 |
96 | 97 | -------------------------------------------------------------------------------- /examples/EBS_Example05_SPIFFSandWebServer/server32.ino: -------------------------------------------------------------------------------- 1 | #if defined( ARDUINO_ARCH_ESP32 ) 2 | 3 | 4 | #define FILESYSTEM SPIFFS 5 | // You only need to format the filesystem once 6 | #define FORMAT_FILESYSTEM false 7 | #define Serial Serial 8 | 9 | #include 10 | 11 | 12 | //format bytes 13 | String formatBytes(size_t bytes) { 14 | if (bytes < 1024) { 15 | return String(bytes) + "B"; 16 | } else if (bytes < (1024 * 1024)) { 17 | return String(bytes / 1024.0) + "KB"; 18 | } else if (bytes < (1024 * 1024 * 1024)) { 19 | return String(bytes / 1024.0 / 1024.0) + "MB"; 20 | } else { 21 | return String(bytes / 1024.0 / 1024.0 / 1024.0) + "GB"; 22 | } 23 | } 24 | 25 | String getContentType(String filename) { 26 | if (server.hasArg("download")) { 27 | return "application/octet-stream"; 28 | } else if (filename.endsWith(".htm")) { 29 | return "text/html"; 30 | } else if (filename.endsWith(".html")) { 31 | return "text/html"; 32 | } else if (filename.endsWith(".css")) { 33 | return "text/css"; 34 | } else if (filename.endsWith(".js")) { 35 | return "application/javascript"; 36 | } else if (filename.endsWith(".png")) { 37 | return "image/png"; 38 | } else if (filename.endsWith(".gif")) { 39 | return "image/gif"; 40 | } else if (filename.endsWith(".jpg")) { 41 | return "image/jpeg"; 42 | } else if (filename.endsWith(".ico")) { 43 | return "image/x-icon"; 44 | } else if (filename.endsWith(".xml")) { 45 | return "text/xml"; 46 | } else if (filename.endsWith(".pdf")) { 47 | return "application/x-pdf"; 48 | } else if (filename.endsWith(".zip")) { 49 | return "application/x-zip"; 50 | } else if (filename.endsWith(".gz")) { 51 | return "application/x-gzip"; 52 | } 53 | return "text/plain"; 54 | } 55 | 56 | bool exists(String path){ 57 | bool yes = false; 58 | File file = FILESYSTEM.open(path, "r"); 59 | if(!file.isDirectory()){ 60 | yes = true; 61 | } 62 | file.close(); 63 | return yes; 64 | } 65 | 66 | bool handleFileRead(String path) { 67 | Serial.println("handleFileRead: " + path); 68 | if (path.endsWith("/")) { 69 | path += "index.htm"; 70 | } 71 | String contentType = getContentType(path); 72 | String pathWithGz = path + ".gz"; 73 | if (exists(pathWithGz) || exists(path)) { 74 | if (exists(pathWithGz)) { 75 | path += ".gz"; 76 | } 77 | File file = FILESYSTEM.open(path, "r"); 78 | server.streamFile(file, contentType); 79 | file.close(); 80 | return true; 81 | } 82 | return false; 83 | } 84 | 85 | void handleFileUpload() { 86 | if (server.uri() != "/edit") { 87 | return; 88 | } 89 | HTTPUpload& upload = server.upload(); 90 | if (upload.status == UPLOAD_FILE_START) { 91 | String filename = upload.filename; 92 | if (!filename.startsWith("/")) { 93 | filename = "/" + filename; 94 | } 95 | Serial.print("handleFileUpload Name: "); Serial.println(filename); 96 | fsUploadFile = FILESYSTEM.open(filename, "w"); 97 | filename = String(); 98 | } else if (upload.status == UPLOAD_FILE_WRITE) { 99 | //Serial.print("handleFileUpload Data: "); Serial.println(upload.currentSize); 100 | if (fsUploadFile) { 101 | fsUploadFile.write(upload.buf, upload.currentSize); 102 | } 103 | } else if (upload.status == UPLOAD_FILE_END) { 104 | if (fsUploadFile) { 105 | fsUploadFile.close(); 106 | } 107 | Serial.print("handleFileUpload Size: "); Serial.println(upload.totalSize); 108 | } 109 | } 110 | 111 | void handleFileDelete() { 112 | if (server.args() == 0) { 113 | return server.send(500, "text/plain", "BAD ARGS"); 114 | } 115 | String path = server.arg(0); 116 | Serial.println("handleFileDelete: " + path); 117 | if (path == "/") { 118 | return server.send(500, "text/plain", "BAD PATH"); 119 | } 120 | if (!exists(path)) { 121 | return server.send(404, "text/plain", "FileNotFound"); 122 | } 123 | FILESYSTEM.remove(path); 124 | server.send(200, "text/plain", ""); 125 | path = String(); 126 | } 127 | 128 | void handleFileCreate() { 129 | if (server.args() == 0) { 130 | return server.send(500, "text/plain", "BAD ARGS"); 131 | } 132 | String path = server.arg(0); 133 | Serial.println("handleFileCreate: " + path); 134 | if (path == "/") { 135 | return server.send(500, "text/plain", "BAD PATH"); 136 | } 137 | if (exists(path)) { 138 | return server.send(500, "text/plain", "FILE EXISTS"); 139 | } 140 | File file = FILESYSTEM.open(path, "w"); 141 | if (file) { 142 | file.close(); 143 | } else { 144 | return server.send(500, "text/plain", "CREATE FAILED"); 145 | } 146 | server.send(200, "text/plain", ""); 147 | path = String(); 148 | } 149 | 150 | void handleFileList() { 151 | if (!server.hasArg("dir")) { 152 | server.send(500, "text/plain", "BAD ARGS"); 153 | return; 154 | } 155 | 156 | String path = server.arg("dir"); 157 | Serial.println("handleFileList: " + path); 158 | 159 | 160 | File root = FILESYSTEM.open(path); 161 | path = String(); 162 | 163 | String output = "["; 164 | if(root.isDirectory()){ 165 | File file = root.openNextFile(); 166 | while(file){ 167 | if (output != "[") { 168 | output += ','; 169 | } 170 | output += "{\"type\":\""; 171 | output += (file.isDirectory()) ? "dir" : "file"; 172 | output += "\",\"name\":\""; 173 | output += String(file.name()).substring(1); 174 | output += "\"}"; 175 | file = root.openNextFile(); 176 | } 177 | } 178 | output += "]"; 179 | server.send(200, "text/json", output); 180 | } 181 | 182 | void setupServer(void) { 183 | FILESYSTEM.begin(); 184 | { 185 | File root = FILESYSTEM.open("/"); 186 | File file = root.openNextFile(); 187 | while(file){ 188 | String fileName = file.name(); 189 | size_t fileSize = file.size(); 190 | Serial.printf("FS File: %s, size: %s\n", fileName.c_str(), formatBytes(fileSize).c_str()); 191 | file = root.openNextFile(); 192 | } 193 | Serial.printf("\n"); 194 | } 195 | 196 | 197 | //SERVER INIT 198 | //list directory 199 | server.on("/list", HTTP_GET, handleFileList); 200 | //load editor 201 | server.on("/edit", HTTP_GET, []() { 202 | if (!handleFileRead("/edit.htm")) { 203 | server.send(404, "text/plain", "FileNotFound"); 204 | } 205 | }); 206 | //create file 207 | server.on("/edit", HTTP_PUT, handleFileCreate); 208 | //delete file 209 | server.on("/edit", HTTP_DELETE, handleFileDelete); 210 | //first callback is called after the request has ended with all parsed arguments 211 | //second callback handles file uploads at that location 212 | server.on("/edit", HTTP_POST, []() { 213 | server.send(200, "text/plain", ""); 214 | }, handleFileUpload); 215 | 216 | //called when the url is not defined here 217 | //use it to load content from FILESYSTEM 218 | server.onNotFound([]() { 219 | if (!handleFileRead(server.uri())) { 220 | server.send(404, "text/plain", "FileNotFound"); 221 | } 222 | }); 223 | 224 | //get heap status, analog input value and all GPIO statuses in one json call 225 | server.on("/all", HTTP_GET, []() { 226 | String json = "{"; 227 | json += "\"heap\":" + String(ESP.getFreeHeap()); 228 | json += ", \"analog\":" + String(analogRead(A0)); 229 | json += ", \"gpio\":" + String((uint32_t)(0)); 230 | json += "}"; 231 | server.send(200, "text/json", json); 232 | json = String(); 233 | }); 234 | server.begin(); 235 | } 236 | 237 | #endif 238 | -------------------------------------------------------------------------------- /examples/EBS_Example05_SPIFFSandWebServer/server8266.ino: -------------------------------------------------------------------------------- 1 | #if defined( ARDUINO_ARCH_ESP8266 ) 2 | 3 | //format bytes 4 | String formatBytes(size_t bytes) { 5 | if (bytes < 1024) { 6 | return String(bytes) + "B"; 7 | } else if (bytes < (1024 * 1024)) { 8 | return String(bytes / 1024.0) + "KB"; 9 | } else if (bytes < (1024 * 1024 * 1024)) { 10 | return String(bytes / 1024.0 / 1024.0) + "MB"; 11 | } else { 12 | return String(bytes / 1024.0 / 1024.0 / 1024.0) + "GB"; 13 | } 14 | } 15 | 16 | String getContentType(String filename) { 17 | if (server.hasArg("download")) { 18 | return "application/octet-stream"; 19 | } else if (filename.endsWith(".htm")) { 20 | return "text/html"; 21 | } else if (filename.endsWith(".html")) { 22 | return "text/html"; 23 | } else if (filename.endsWith(".css")) { 24 | return "text/css"; 25 | } else if (filename.endsWith(".js")) { 26 | return "application/javascript"; 27 | } else if (filename.endsWith(".png")) { 28 | return "image/png"; 29 | } else if (filename.endsWith(".gif")) { 30 | return "image/gif"; 31 | } else if (filename.endsWith(".jpg")) { 32 | return "image/jpeg"; 33 | } else if (filename.endsWith(".ico")) { 34 | return "image/x-icon"; 35 | } else if (filename.endsWith(".xml")) { 36 | return "text/xml"; 37 | } else if (filename.endsWith(".pdf")) { 38 | return "application/x-pdf"; 39 | } else if (filename.endsWith(".zip")) { 40 | return "application/x-zip"; 41 | } else if (filename.endsWith(".gz")) { 42 | return "application/x-gzip"; 43 | } 44 | return "text/plain"; 45 | } 46 | 47 | 48 | bool handleFileRead(String path) { 49 | Serial.println("handleFileRead: " + path); 50 | if (path.endsWith("/")) { 51 | path += "index.htm"; 52 | } 53 | String contentType = getContentType(path); 54 | String pathWithGz = path + ".gz"; 55 | if (filesystem->exists(pathWithGz) || filesystem->exists(path)) { 56 | if (filesystem->exists(pathWithGz)) { 57 | path += ".gz"; 58 | } 59 | File file = filesystem->open(path, "r"); 60 | server.streamFile(file, contentType); 61 | file.close(); 62 | return true; 63 | } 64 | return false; 65 | } 66 | 67 | void handleFileUpload() { 68 | if (server.uri() != "/edit") { 69 | return; 70 | } 71 | HTTPUpload& upload = server.upload(); 72 | if (upload.status == UPLOAD_FILE_START) { 73 | String filename = upload.filename; 74 | if (!filename.startsWith("/")) { 75 | filename = "/" + filename; 76 | } 77 | Serial.print("handleFileUpload Name: "); Serial.println(filename); 78 | fsUploadFile = filesystem->open(filename, "w"); 79 | filename = String(); 80 | } else if (upload.status == UPLOAD_FILE_WRITE) { 81 | //DBG_OUTPUT_PORT.print("handleFileUpload Data: "); DBG_OUTPUT_PORT.println(upload.currentSize); 82 | if (fsUploadFile) { 83 | fsUploadFile.write(upload.buf, upload.currentSize); 84 | } 85 | } else if (upload.status == UPLOAD_FILE_END) { 86 | if (fsUploadFile) { 87 | fsUploadFile.close(); 88 | } 89 | Serial.print("handleFileUpload Size: "); Serial.println(upload.totalSize); 90 | } 91 | } 92 | 93 | void handleFileDelete() { 94 | if (server.args() == 0) { 95 | return server.send(500, "text/plain", "BAD ARGS"); 96 | } 97 | String path = server.arg(0); 98 | Serial.println("handleFileDelete: " + path); 99 | if (path == "/") { 100 | return server.send(500, "text/plain", "BAD PATH"); 101 | } 102 | if (!filesystem->exists(path)) { 103 | return server.send(404, "text/plain", "FileNotFound"); 104 | } 105 | filesystem->remove(path); 106 | server.send(200, "text/plain", ""); 107 | path = String(); 108 | } 109 | 110 | void handleFileCreate() { 111 | if (server.args() == 0) { 112 | return server.send(500, "text/plain", "BAD ARGS"); 113 | } 114 | String path = server.arg(0); 115 | Serial.println("handleFileCreate: " + path); 116 | if (path == "/") { 117 | return server.send(500, "text/plain", "BAD PATH"); 118 | } 119 | if (filesystem->exists(path)) { 120 | return server.send(500, "text/plain", "FILE EXISTS"); 121 | } 122 | File file = filesystem->open(path, "w"); 123 | if (file) { 124 | file.close(); 125 | } else { 126 | return server.send(500, "text/plain", "CREATE FAILED"); 127 | } 128 | server.send(200, "text/plain", ""); 129 | path = String(); 130 | } 131 | 132 | void handleFileList() { 133 | if (!server.hasArg("dir")) { 134 | server.send(500, "text/plain", "BAD ARGS"); 135 | return; 136 | } 137 | 138 | String path = server.arg("dir"); 139 | Serial.println("handleFileList: " + path); 140 | Dir dir = filesystem->openDir(path); 141 | path = String(); 142 | 143 | String output = "["; 144 | while (dir.next()) { 145 | File entry = dir.openFile("r"); 146 | if (output != "[") { 147 | output += ','; 148 | } 149 | bool isDir = false; 150 | output += "{\"type\":\""; 151 | output += (isDir) ? "dir" : "file"; 152 | output += "\",\"name\":\""; 153 | if (entry.name()[0] == '/') { 154 | output += &(entry.name()[1]); 155 | } else { 156 | output += entry.name(); 157 | } 158 | output += "\"}"; 159 | entry.close(); 160 | } 161 | 162 | output += "]"; 163 | server.send(200, "text/json", output); 164 | } 165 | 166 | 167 | void setupServer() { 168 | filesystem->begin(); 169 | { 170 | Dir dir = filesystem->openDir("/"); 171 | while (dir.next()) { 172 | String fileName = dir.fileName(); 173 | size_t fileSize = dir.fileSize(); 174 | Serial.printf("FS File: %s, size: %s\n", fileName.c_str(), formatBytes(fileSize).c_str()); 175 | } 176 | Serial.printf("\n"); 177 | } 178 | 179 | server.on("/list", HTTP_GET, handleFileList); 180 | //load editor 181 | server.on("/edit", HTTP_GET, []() { 182 | if (!handleFileRead("/edit.htm")) { 183 | server.send(404, "text/plain", "FileNotFound"); 184 | } 185 | }); 186 | //create file 187 | server.on("/edit", HTTP_PUT, handleFileCreate); 188 | //delete file 189 | server.on("/edit", HTTP_DELETE, handleFileDelete); 190 | //first callback is called after the request has ended with all parsed arguments 191 | //second callback handles file uploads at that location 192 | server.on("/edit", HTTP_POST, []() { 193 | server.send(200, "text/plain", ""); 194 | }, handleFileUpload); 195 | 196 | //called when the url is not defined here 197 | //use it to load content from SPIFFS 198 | server.onNotFound([]() { 199 | if (!handleFileRead(server.uri())) { 200 | server.send(404, "text/plain", "FileNotFound"); 201 | } 202 | }); 203 | 204 | //get heap status, analog input value and all GPIO statuses in one json call 205 | server.on("/all", HTTP_GET, []() { 206 | String json = "{"; 207 | json += "\"heap\":" + String(ESP.getFreeHeap()); 208 | json += ", \"analog\":" + String(analogRead(A0)); 209 | json += ", \"gpio\":" + String((uint32_t)(((GPI | GPO) & 0xFFFF) | ((GP16I & 0x01) << 16))); 210 | json += "}"; 211 | server.send(200, "text/json", json); 212 | json = String(); 213 | }); 214 | server.begin(); 215 | 216 | } 217 | 218 | #endif 219 | -------------------------------------------------------------------------------- /examples/EBS_example01_ParametersEEPROMMap/EBS_example01_ParametersEEPROMMap.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020, Anatoli Arkhipenko 3 | All rights reserved. 4 | 5 | This example illustrates how to use EspBootstrap library with memory structure parameters. 6 | The OTA update is not included. 7 | 8 | 1. Compile and upload into the chip 9 | 2. Start Serial monitor 10 | 3. Boot up the chip for the first time 11 | 4. Chip will create an Access Point names ESPNNN-XXXXXX 12 | 5. Connect your phone/PC to that Access Point. There is no password 13 | 6. Connect your browser to http://10.1.1.1. You have 5 minutes to do steps 7-9 below 14 | 7. Populate the Wifi Seetings 15 | 8. Populate the path to a valid JSON configuration file 16 | (e.g.,: http://raw.githubusercontent.com/arkhipenko/EspBootstrap/master/examples/EBS_example01/config.json) 17 | 9. Press "Submit" button. 18 | 10. Device will reboot 19 | 11. Device should connect to your WiFi network, download, parse and save the configuration. 20 | 21 | */ 22 | 23 | #define _DEBUG_ 24 | //#define _TEST_ 25 | 26 | #ifdef _DEBUG_ 27 | #define _PP(a) Serial.print(a); 28 | #define _PL(a) Serial.println(a); 29 | #else 30 | #define _PP(a) 31 | #define _PL(a) 32 | #endif 33 | 34 | // ==== Includes =================================== 35 | #include // Parameters stored in EEPROM 36 | #include // ESP Bootstrap for memory structure 37 | #include // Json parsing from HTTP host into memory structure 38 | 39 | 40 | // ==== Parameters and BootStrap =================== 41 | 42 | // Every configration structure should start with a token 43 | // "Token" is a character string identifying set of parameters (and a version) 44 | // IMPORTANT: Token should be the fist element of the parameters structure. 45 | #define CTOKEN "EBS1" 46 | const String TOKEN(CTOKEN); 47 | 48 | // NPARS is a number of parameters in the structure 49 | // inclusive of the Token 50 | const int NPARS = 7; 51 | 52 | // The parameter structure itself defined as a new type 53 | // All parameters shoudl be of type 'char[]' 54 | // Take care to allocate enough space for your parameters 55 | // There is no buffer overrun checking done by this library 56 | // (for size and speed purposes) 57 | typedef struct { 58 | char token[5]; 59 | char ssid[32]; 60 | char pwd[32]; 61 | char cfg_url[128]; 62 | char ota_host[32]; 63 | char ota_port[6]; 64 | char ota_url[32]; 65 | } Params; 66 | 67 | // Define actual parameter variable 68 | Params eg; 69 | 70 | // You can supply default values for the parameters 71 | // Which will be loaded into the structure in case 72 | // of bad crc or token mismatch 73 | // You can provide NULL pointer instead of Defaults 74 | // In that case the sctructure will be filled with zeros 75 | Params defaults = { CTOKEN, 76 | "", 77 | "", 78 | "http://raw.githubusercontent.com/arkhipenko/EspBootstrap/master/examples/EBS_example01_ParametersEEPROMMap/config.json", 79 | "", 80 | "1234", 81 | "/esp/ota.php" 82 | }; 83 | 84 | // A very important part of the process is a map of parameter fields 85 | // This map is used by many library components to populate individual fields. 86 | // The map SHOULD NOT include reference to the TOKEN 87 | char* PARS[] = { eg.ssid, 88 | eg.pwd, 89 | eg.cfg_url, 90 | eg.ota_host, 91 | eg.ota_port, 92 | eg.ota_url 93 | }; 94 | 95 | // The next two variables define how many fields are displayed on the web form 96 | // when the form is constructed. You could include all fields, or just a subset 97 | // First line is used as a title, the rest are the field labels 98 | // This structure works in conjunction with PARS[] structure to identify 99 | // which fields to actually populate. 100 | const int NPARS_BTS = 3; 101 | const char* PAGE[] = { "EspBootstrap", 102 | "WiFi SSID", 103 | "WiFi PWD", 104 | "Config URL", 105 | }; 106 | 107 | // ==== CODE ============================== 108 | 109 | // This methods prints out all parameters for inspection 110 | void printConfig() { 111 | _PL(); 112 | _PL("Config dump"); 113 | _PP("\ttoken : "); _PL(eg.token); 114 | _PP("\tssid : "); _PL(eg.ssid); 115 | _PP("\tpasswd : "); _PL(eg.pwd); 116 | _PP("\tconfig : "); _PL(eg.cfg_url); 117 | _PP("\tota host : "); _PL(eg.ota_host); 118 | _PP("\tota port : "); _PL(eg.ota_port); 119 | _PP("\tota url : "); _PL(eg.ota_url); 120 | _PL(); 121 | } 122 | 123 | 124 | // Arduino SETUP method 125 | void setup(void) { 126 | bool wifiTimeout; 127 | int rc; 128 | 129 | // Setting up Serial console 130 | #ifdef _DEBUG_ 131 | Serial.begin(115200); 132 | delay(3000); 133 | { 134 | _PL("EspBootStrap Example"); _PL(); 135 | #if defined( ARDUINO_ARCH_ESP8266 ) 136 | String id = "ESP8266-" + String(ESP.getChipId(), HEX); 137 | #endif 138 | #if defined( ARDUINO_ARCH_ESP32 ) 139 | String id = "ESP32-" + String((uint32_t)( ESP.getEfuseMac() & 0xFFFFFFFFL ), HEX); 140 | #endif 141 | _PP("ESP Chip ID: "); _PL(id); 142 | _PP("Parameter structure size: "); _PL( sizeof(Params) ); 143 | _PL(); 144 | } 145 | #endif 146 | 147 | // **** PARAMETERS COMPONENT OF ESPBOOTSTRAP LIBRARY **** 148 | // ====================================================== 149 | // To work with Parameters we need to instanciate a Paramters object. 150 | // first parameter is a reference to the token, {String} 151 | // second parameter is a pointer to the parameters variable {Params} 152 | // third parameter is a pointer to the defaults {cahr **}, or NULL if no defaults 153 | // fourth parameter is the starting address in the EEPROM memory, {int} 154 | // fifth parameter is a size of parameters structure 155 | ParametersEEPROMMap *p_ptr = new ParametersEEPROMMap(TOKEN, &eg, &defaults, 0, sizeof(Params)); 156 | ParametersEEPROMMap& p = *p_ptr; 157 | 158 | // Define EEPROM_MAX to explicitly set maximum EEPROM capacity for your chip 159 | rc = p.begin(); 160 | _PP(": EspBootStrap ParametersEEPROMMap initialized. rc = "); _PL(rc); 161 | 162 | // load() methods attempts to load parameters from the EEPROM 163 | // It checks for a valid CRC and a match on the TOKENs. 164 | // If either CRC or token do not match, an error is set 165 | // load() can return OK, indicating that in the end the structure was populated with something, 166 | // however lastError() may indicate that values were overwritten with defaults due to CRC 167 | // or Token mismatch, and therefore return a non-zero value. Always check lastError()! 168 | rc = p.load(); 169 | 170 | _PP("Configuration loaded. rc = "); _PL(rc); 171 | printConfig(); 172 | 173 | // If Parameters were loaded successfully, the device will try to 174 | // connect to the WiFi network specified in the configuration for 30 seconds 175 | // It will time out after 30 seconds on an assumption that WiFi config is invalid 176 | // 30 seconds is arbitrary - you can set it for longer of shorter period of time 177 | if (rc == PARAMS_OK) { 178 | _PP("Connecting to WiFi for 30 sec:"); 179 | setupWifi(eg.ssid, eg.pwd); 180 | wifiTimeout = waitForWifi(30 * BOOTSTRAP_SECOND); 181 | } 182 | 183 | // If loading of parameters failed, or a WiFi connection timed out 184 | // we need to BootStrap the device. 185 | // ESPBootstrap is a static singleton object which will start up an Access Point (ESPXXX-NNNNNN), 186 | // and a webserver, and then display a simple web form as specified by the PAGE and PARS structures. 187 | // The webserver will be active for 5 minutes. This is done to prevent devices from going into 188 | // bootstrap mode forever due to intermittent WiFi issues. After 5 minutes, if the Submit button 189 | // was never pressed, the device will reboot and re-try to connect to current WiFi network. 190 | // This will continue until either the WiFi settings are changes on the web page, or WiFi network 191 | // issues with the current correct settings are resolved 192 | if (rc != PARAMS_OK || wifiTimeout) { 193 | _PL("Bootstrapping..."); 194 | 195 | // **** BOOTSTRAP COMPONENT OF ESPBOOTSTRAP LIBRARY **** 196 | // ===================================================== 197 | // ESPBootstrap.run() takes 4 paramters: 198 | // Pointer to the descriptions (title, and fields), {char **} 199 | // Pointer to the parameter map, {char **} 200 | // Number of parameters to display on the web form, {int} 201 | // Timeout in milliseconds. (can use helper constants BOOTSTRAP_MINUTE and BOOTSTRAP_SECOND) 202 | rc = ESPBootstrap.run(PAGE, PARS, NPARS_BTS, 5 * BOOTSTRAP_MINUTE); 203 | 204 | if (rc == BOOTSTRAP_OK) { 205 | // If bootstrap was successful, new set of parameters should be saved, 206 | // and processing continued 207 | p.save(); 208 | _PL("Bootstrapped OK. Rebooting."); 209 | } 210 | else { 211 | _PL("Bootstrap timed out. Rebooting."); 212 | } 213 | // or device should be restarted after a timeout 214 | printConfig(); 215 | delay(1000); 216 | ESP.restart(); 217 | } 218 | 219 | // **** JSONCONFIG COMPONENT OF ESPBOOTSTRAP LIBRARY **** 220 | // ====================================================== 221 | // The rest of the parameters could be lifted off a JSON configuration file 222 | // Example of the JSON file is provided on the github. 223 | // JSON file rules: 224 | // 1. No arrays - just a list of key-value pairs 225 | // 2. All keys and values are strings and should be in quotation marks 226 | // 3. The order of key-value pairs in the file should be EXACTLY the same as paramters structure, without leading TOKEN 227 | // 4. Keys and values can contain backslash-ed characters: e.g.; "key\"with a quote" : "\\path\\path2", 228 | // 5. Each line should end with a comma "," (except the last line) 229 | // 230 | // JSONConfig is a static singleton object that does HTTP call, parses the JSON file, and 231 | // populates respective confuration fields. 232 | // JSONConfig.parseHttp takes 3 parameters: 233 | // a fully quallified URL pointing to a JSON configuation file (including http://) {char *} 234 | // a pointer to the parameter map, {char **} 235 | // number of paramters to populate minus 1 for token, {int} 236 | rc = JSONConfig.parse(eg.cfg_url, PARS, NPARS - 1); 237 | 238 | // If successful, the "eg" structure should have a fresh set of paraeters from the JSON file. 239 | _PP("JSONConfig finished. rc = "); _PL(rc); 240 | printConfig(); 241 | if (rc == 0) p.save(); 242 | } 243 | 244 | void loop(void) { 245 | 246 | } 247 | 248 | // This method prepares for WiFi connection 249 | void setupWifi(const char* ssid, const char* pwd) { 250 | _PL("Setup_wifi()"); 251 | 252 | // We start by connecting to a WiFi network 253 | _PL("Connecting to WiFi..."); 254 | // clear wifi config 255 | WiFi.disconnect(); 256 | 257 | WiFi.mode(WIFI_STA); 258 | WiFi.begin(ssid, pwd); 259 | } 260 | 261 | // This method waits for a WiFi connection for aTimeout milliseconds. 262 | bool waitForWifi(unsigned long aTimeout) { 263 | _PL("WaitForWifi()"); 264 | 265 | unsigned long timeNow = millis(); 266 | 267 | while ( WiFi.status() != WL_CONNECTED ) { 268 | delay(1000); 269 | _PP("."); 270 | if ( millis() - timeNow > aTimeout ) { 271 | _PL(" WiFi connection timeout"); 272 | return true; 273 | } 274 | } 275 | 276 | _PL(" WiFi connected"); 277 | _PP("IP address: "); _PL(WiFi.localIP()); 278 | _PP("SSID: "); _PL(WiFi.SSID()); 279 | _PP("mac: "); _PL(WiFi.macAddress()); 280 | return false; 281 | } 282 | -------------------------------------------------------------------------------- /examples/EBS_example01_ParametersEEPROMMap/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ssid" : "your wifi ssid", 3 | "pwd" : "your wifi password", 4 | "cfg_url" : "http://raw.githubusercontent.com/arkhipenko/EspBootstrap/master/examples/EBS_example01_ParametersEEPROMMap/config.json", 5 | "ota_host" : "ota.home.lan", 6 | "ota_port" : "80", 7 | "ota_url" : "/esp/esp8266.php" 8 | } 9 | -------------------------------------------------------------------------------- /examples/EBS_example01a_ParametersEEPROMMap/EBS_example01a_ParametersEEPROMMap.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020, Anatoli Arkhipenko 3 | All rights reserved. 4 | 5 | This is example #1 without comments or debug messages 6 | */ 7 | 8 | #define CTOKEN "EBS1A" 9 | 10 | 11 | #include // Parameters stored in EEPROM 12 | #include // ESP Bootstrap for memory structure 13 | #include // Json parsing from HTTP host into memory structure 14 | 15 | 16 | const String TOKEN(CTOKEN); 17 | const int NPARS = 7; 18 | 19 | typedef struct { 20 | char token[6]; 21 | char ssid[32]; 22 | char pwd[32]; 23 | char cfg_url[128]; 24 | char ota_host[32]; 25 | char ota_port[6]; 26 | char ota_url[32]; 27 | } Params; 28 | Params eg; 29 | 30 | Params defaults = { CTOKEN, 31 | "", 32 | "", 33 | "http://raw.githubusercontent.com/arkhipenko/EspBootstrap/master/examples/EBS_example01_ParametersEEPROMMap/config.json", 34 | "", 35 | "1234", 36 | "/esp/ota.php" 37 | }; 38 | 39 | char* PARS[] = { eg.ssid, 40 | eg.pwd, 41 | eg.cfg_url, 42 | eg.ota_host, 43 | eg.ota_port, 44 | eg.ota_url 45 | }; 46 | 47 | const int NPARS_BTS = 3; 48 | const char* PAGE[] = { "EspBootstrap", 49 | "WiFi SSID", 50 | "WiFi PWD", 51 | "Config URL", 52 | }; 53 | 54 | void setup(void) { 55 | bool wifiTimeout; 56 | int rc; 57 | 58 | ParametersEEPROMMap *p_ptr = new ParametersEEPROMMap(TOKEN, &eg, &defaults, 0, sizeof(Params)); 59 | ParametersEEPROMMap& p = *p_ptr; 60 | 61 | rc = p.begin(); 62 | rc = p.load(); 63 | 64 | if (rc == PARAMS_OK) { 65 | setupWifi(eg.ssid, eg.pwd); 66 | wifiTimeout = waitForWifi(30 * BOOTSTRAP_SECOND); 67 | } 68 | 69 | if (rc != PARAMS_OK || wifiTimeout) { 70 | rc = ESPBootstrap.run(PAGE, PARS, NPARS_BTS, 5 * BOOTSTRAP_MINUTE); 71 | if (rc == BOOTSTRAP_OK) { 72 | p.save(); 73 | } 74 | delay(1000); 75 | ESP.restart(); 76 | } 77 | rc = JSONConfig.parse(eg.cfg_url, PARS, NPARS - 1); 78 | if (rc == 0) p.save(); 79 | } 80 | 81 | void loop(void) { 82 | } 83 | 84 | void setupWifi(const char* ssid, const char* pwd) { 85 | WiFi.disconnect(); 86 | WiFi.mode(WIFI_STA); 87 | WiFi.begin(ssid, pwd); 88 | } 89 | 90 | bool waitForWifi(unsigned long aTimeout) { 91 | unsigned long timeNow = millis(); 92 | 93 | while ( WiFi.status() != WL_CONNECTED ) { 94 | delay(1000); 95 | if ( millis() - timeNow > aTimeout ) { 96 | return true; 97 | } 98 | } 99 | return false; 100 | } 101 | -------------------------------------------------------------------------------- /examples/EBS_example01a_ParametersEEPROMMap/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ssid" : "your wifi ssid", 3 | "pwd" : "your wifi password", 4 | "cfg_url" : "http://raw.githubusercontent.com/arkhipenko/EspBootstrap/master/examples/EBS_example01_ParametersEEPROMMap/config.json", 5 | "ota_host" : "ota.home.lan", 6 | "ota_port" : "80", 7 | "ota_url" : "/esp/esp8266.php" 8 | } 9 | -------------------------------------------------------------------------------- /examples/EBS_example02_ParametersEEPROM/EBS_example02_ParametersEEPROM.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020, Anatoli Arkhipenko 3 | All rights reserved. 4 | 5 | This example illustrates how to use EspBootstrap library with Dictionry as parameters. 6 | The OTA update is not included. 7 | 8 | 1. Compile and upload into the chip 9 | 2. Start Serial monitor 10 | 3. Boot up the chip for the first time 11 | 4. Chip will create an Access Point names ESPNNN-XXXXXX 12 | 5. Connect your phone/PC to that Access Point. There is no password 13 | 6. Connect your browser to http://10.1.1.1. You have 5 minutes to do steps 7-9 below 14 | 7. Populate the Wifi Seetings 15 | 8. Populate the path to a valid JSON configuration file 16 | (e.g.,: http://raw.githubusercontent.com/arkhipenko/EspBootstrap/master/examples/EBS_example01/config.json) 17 | 9. Press "Submit" button. 18 | 10. Device will reboot 19 | 11. Device should connect to your WiFi network, download, parse and save the configuration. 20 | 21 | */ 22 | 23 | // These two defines MUST be placed before the header files 24 | // to be taken into account by the library 25 | 26 | #define EEPROM_MAX 4096 27 | #define CTOKEN "EBS2" 28 | 29 | #define _DEBUG_ 30 | //#define _TEST_ 31 | 32 | #ifdef _DEBUG_ 33 | #define _PP(a) Serial.print(a); 34 | #define _PL(a) Serial.println(a); 35 | #else 36 | #define _PP(a) 37 | #define _PL(a) 38 | #endif 39 | 40 | 41 | // ==== Includes =================================== 42 | #include 43 | #include 44 | #include 45 | 46 | 47 | // ==== Parameters and BootStrap =================== 48 | 49 | // Every configration structure should start with a token 50 | // "Token" is a character string identifying set of parameters (and a version) 51 | const String TOKEN(CTOKEN); 52 | 53 | // NPARS is a number of parameters in the structure 54 | // inclusive of the Token 55 | const int NPARS = 7; 56 | 57 | // The actual dictionary object to hold parameter values 58 | Dictionary d(NPARS); 59 | 60 | // The next two variables define how many fields are displayed on the web form 61 | // when the form is constructed. You could include all fields, or just a subset 62 | // First line is used as a title, the rest are the field labels 63 | // This structure works in conjunction with dictionary. Only the first NPARS_BTS 64 | // fields are displayed on the form. Fields are numbered in the order they inserted. 65 | const int NPARS_BTS = 3; 66 | 67 | 68 | // ==== CODE ============================== 69 | 70 | // This methods prints out parameters for inspection 71 | void printConfig() { 72 | _PL(); 73 | _PL("Config dump"); 74 | _PP("\ttoken : "); _PL(CTOKEN); 75 | _PP("\tssid : "); _PL(d["ssid"]); 76 | _PP("\tpasswd: "); _PL(d["pwd"]); 77 | _PP("\tconfig: "); _PL(d["cfg_url"]); 78 | _PP("\tota host: "); _PL(d["ota_host"]); 79 | _PP("\tota port: "); _PL(d["ota_port"]); 80 | _PP("\tota url : "); _PL(d["ota_url"]); 81 | _PL(); 82 | } 83 | 84 | 85 | 86 | // Arduino SETUP method 87 | void setup(void) { 88 | int rc; 89 | bool wifiTimeout; 90 | 91 | // Setting up Serial console 92 | #ifdef _DEBUG_ 93 | Serial.begin(115200); 94 | delay(500); 95 | { 96 | _PL("EspBootStrap Dict Example"); _PL(); 97 | #if defined( ARDUINO_ARCH_ESP8266 ) 98 | String id = "ESP8266-" + String(ESP.getChipId(), HEX); 99 | #endif 100 | #if defined( ARDUINO_ARCH_ESP32 ) 101 | String id = "ESP32-" + String((uint32_t)( ESP.getEfuseMac() & 0xFFFFFFFFL ), HEX); 102 | #endif 103 | _PP("ESP Chip ID: "); _PL(id); 104 | _PL(); 105 | } 106 | #endif 107 | 108 | 109 | // Pre-populate the dictionary with defaults for the webform 110 | // First entry is a Web Form Title (element #0) 111 | d("Title", "EspBootstrapD"); 112 | // The rest will be created as fields on the form: 113 | d("ssid", ""); 114 | d("pwd", ""); 115 | d("cfg_url", "http://raw.githubusercontent.com/arkhipenko/EspBootstrap/master/examples/EBS_example02_ParametersEEPROM/config.json"); 116 | 117 | // **** PARAMETERS COMPONENT OF ESPBOOTSTRAP LIBRARY **** 118 | // ====================================================== 119 | // To work with Parameters we need to instanciate a Paramters object. 120 | // first parameter is a reference to the token, {String} 121 | // second parameter is a reference to the dictionary object holding parameters {Dictionary} 122 | // third parameter is the starting address in the EEPROM memory, {int} 123 | // fourth parameter is a size of EEPROM allocated to parameters 124 | // since the combined size of all key-value pairs is not known, make sure you allocate 125 | // enough space. Otherwise the Parameters object will not be activated. 126 | ParametersEEPROM *p_ptr = new ParametersEEPROM(TOKEN, d, 0, 1024); 127 | ParametersEEPROM& p = *p_ptr; 128 | 129 | // Define EEPROM_MAX to explicitly set maximum EEPROM capacity for your chip 130 | rc = p.begin(); 131 | _PP("EspBootStrap ParametersEEPROM initialized. rc = "); _PL(rc); 132 | 133 | // load() methods attempts to load parameters from the EEPROM 134 | // It checks for a valid CRC and a match on the TOKENs. 135 | // If either CRC or token do not match, an error is set 136 | // load() can return OK, indicating that in the end the structure was populated with something, 137 | // however lastError() may indicate that values were overwritten with defaults due to CRC 138 | // or Token mismatch, and therefore return a non-zero value. Always check lastError()! 139 | // Upon success, the dictionary should be updated with key-value pairs from the EEPROM 140 | rc = p.load(); 141 | _PP("Configuration loaded. rc = "); _PL(rc); 142 | printConfig(); 143 | 144 | // If Parameters were loaded successfully, the device will try to 145 | // connect to the WiFi network (d["ssid"], d["pwd"]) specified in the configuration. 146 | // It will time out after 30 seconds on an assumption that WiFi config is invalid 147 | // 30 seconds is arbitrary - you can set it for longer of shorter period of time 148 | 149 | if (rc == PARAMS_OK) { 150 | _PP("Connecting to WiFi for 30 sec:"); 151 | setupWifi(d["ssid"].c_str(), d["pwd"].c_str()); 152 | wifiTimeout = waitForWifi(30 * BOOTSTRAP_SECOND); 153 | } 154 | 155 | // If loading of parameters failed, or a WiFi connection timed out 156 | // we need to BootStrap the device. 157 | // ESPBootstrap is a static singleton object which will start up an Access Point (ESPXXX-NNNNNN), 158 | // and a webserver, and then display a simple web form as specified by the PAGE and PARS structures. 159 | // The webserver will be active for 5 minutes. This is done to prevent devices from going into 160 | // bootstrap mode forever due to intermittent WiFi issues. After 5 minutes, if the Submit button 161 | // was never pressed, the device will reboot and re-try to connect to current WiFi network. 162 | // This will continue until either the WiFi settings are changes on the web page, or WiFi network 163 | // issues with the current correct settings are resolved 164 | if (rc != PARAMS_OK || wifiTimeout) { 165 | _PL("Device needs bootstrapping:"); 166 | 167 | 168 | // **** BOOTSTRAP COMPONENT OF ESPBOOTSTRAP LIBRARY **** 169 | // ===================================================== 170 | // ESPBootstrap.run() takes 4 paramters: 171 | // Reference to the dictionary object with title and fields, {Dictionary} 172 | // Number of parameters to display on the web form, {int} 173 | // Timeout in milliseconds. (can use helper constants BOOTSTRAP_MINUTE and BOOTSTRAP_SECOND) 174 | rc = ESPBootstrap.run(d, NPARS_BTS, 5 * BOOTSTRAP_MINUTE); 175 | 176 | if (rc == BOOTSTRAP_OK) { 177 | // If bootstrap was successful, new set of parameters should be saved, 178 | // and processing continued 179 | p.save(); 180 | _PL("Bootstrapped OK. Rebooting."); 181 | } 182 | else { 183 | _PL("Bootstrap timed out. Rebooting."); 184 | } 185 | // or device should be restarted after a timeout 186 | printConfig(); 187 | delay(5000); 188 | ESP.restart(); 189 | } 190 | 191 | // **** JSONCONFIG COMPONENT OF ESPBOOTSTRAP LIBRARY **** 192 | // ====================================================== 193 | // The rest of the parameters could be lifted off a JSON configuration file 194 | // Example of the JSON file is provided on the github. 195 | // JSON file rules: 196 | // 1. No arrays - just a list of key-value pairs 197 | // 2. All keys and values are strings and should be in quotation marks 198 | // 3. The order of key-value pairs in the file does not matter 199 | // 4. Keys and values can contain backslash-ed characters: e.g.; "key\"with a quote" : "\\path\\path2", 200 | // 5. Each line should end with a comma "," (except the last line) 201 | // 202 | // JSONConfig is a static singleton object that does HTTP call, parses the JSON file, and 203 | // populates respective configuration key-value pairs. 204 | // JSONConfig.parseHttp takes 3 parameters: 205 | // a fully quallified URL pointing to a JSON configuation file (including http://) {char *} 206 | // a reference to the parameter dictionary object, {Dictionary} 207 | // key-value pairs are ADDED or UPDATED in the dictionary, therefore JSON file may contain a subset of 208 | // parameters (e.g., the WiFi settings are always set by the Bootstrap web form, and the rest is 209 | // provided by the JSON file. 210 | rc = JSONConfig.parse(d["cfg_url"], d); 211 | 212 | // If successful, the "d" dictionary should have a refreshed set of parameters from the JSON file. 213 | _PP("JSONConfig finished. rc = "); _PL(rc); 214 | _PP("Current dictionary count = "); _PL(d.count()); 215 | _PP("Current dictionary size = "); _PL(d.size()); 216 | printConfig(); 217 | if (rc == 0) p.save(); 218 | } 219 | 220 | void loop(void) { 221 | 222 | } 223 | 224 | // This method prepares for WiFi connection 225 | void setupWifi(const char* ssid, const char* pwd) { 226 | _PL("Setup_wifi()"); 227 | 228 | // We start by connecting to a WiFi network 229 | _PL("Connecting to WiFi..."); 230 | // clear wifi config 231 | WiFi.disconnect(); 232 | 233 | WiFi.mode(WIFI_STA); 234 | WiFi.begin(ssid, pwd); 235 | } 236 | 237 | // This method waits for a WiFi connection for aTimeout milliseconds. 238 | bool waitForWifi(unsigned long aTimeout) { 239 | _PL("WaitForWifi()"); 240 | 241 | unsigned long timeNow = millis(); 242 | 243 | while ( WiFi.status() != WL_CONNECTED ) { 244 | delay(1000); 245 | _PP("."); 246 | if ( millis() - timeNow > aTimeout ) { 247 | _PL(" WiFi connection timeout"); 248 | return true; 249 | } 250 | } 251 | 252 | _PL(" WiFi connected"); 253 | _PP("IP address: "); _PL(WiFi.localIP()); 254 | _PP("SSID: "); _PL(WiFi.SSID()); 255 | _PP("mac: "); _PL(WiFi.macAddress()); 256 | return false; 257 | } 258 | -------------------------------------------------------------------------------- /examples/EBS_example02_ParametersEEPROM/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ssid" : "your wifi ssid", 3 | "pwd" : "your wifi password", 4 | "cfg_url" : "http://raw.githubusercontent.com/arkhipenko/EspBootstrap/master/examples/EBS_example02_ParametersEEPROM/config.json", 5 | "ota_host" : "ota.home.lan", 6 | "ota_port" : "80", 7 | "ota_url" : "/esp/esp8266.php" 8 | } 9 | -------------------------------------------------------------------------------- /examples/EBS_example02_ParametersEEPROM/config_comments.json: -------------------------------------------------------------------------------- 1 | # Comments are supported although they are not allowed by JSON standard 2 | { 3 | "ssid" : "your wifi ssid", # WiFi credentials 4 | "pwd" : "your wifi password", # WiFi credentials 5 | "cfg_url" : "http://raw.githubusercontent.com/arkhipenko/EspBootstrap/master/examples/EBS_example02_ParametersEEPROM/config.json", 6 | "ota_host" : "ota.home.lan", 7 | "ota_port" : "80", 8 | "ota_url" : "/esp/esp8266.php" 9 | } 10 | -------------------------------------------------------------------------------- /examples/EBS_example02a_ParametersEEPROM/EBS_example02a_ParametersEEPROM.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020, Anatoli Arkhipenko 3 | All rights reserved. 4 | 5 | This is example #2 without comments and debug messages 6 | */ 7 | 8 | #define EEPROM_MAX 4096 9 | #define CTOKEN "EBS2a" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | const String TOKEN(CTOKEN); 16 | const int NPARS = 7; 17 | 18 | Dictionary d(NPARS); 19 | const int NPARS_BTS = 3; 20 | 21 | 22 | // ==== CODE ============================== 23 | 24 | void setup(void) { 25 | int rc; 26 | bool wifiTimeout; 27 | 28 | d("Title", "EspBootstrapD"); 29 | d("ssid", ""); 30 | d("pwd", ""); 31 | d("cfg_url", "http://raw.githubusercontent.com/arkhipenko/EspBootstrap/master/examples/EBS_example02_ParametersEEPROM/config.json"); 32 | 33 | ParametersEEPROM *p_ptr = new ParametersEEPROM(TOKEN, d, 0, 1024); 34 | ParametersEEPROM& p = *p_ptr; 35 | 36 | rc = p.begin(); 37 | rc = p.load(); 38 | 39 | if (rc == PARAMS_OK) { 40 | setupWifi(d["ssid"].c_str(), d["pwd"].c_str()); 41 | wifiTimeout = waitForWifi(30 * BOOTSTRAP_SECOND); 42 | } 43 | if (rc != PARAMS_OK || wifiTimeout) { 44 | rc = ESPBootstrap.run(d, NPARS_BTS, 5 * BOOTSTRAP_MINUTE); 45 | if (rc == BOOTSTRAP_OK) { 46 | p.save(); 47 | } 48 | delay(5000); 49 | ESP.restart(); 50 | } 51 | 52 | rc = JSONConfig.parse(d["cfg_url"], d); 53 | if (rc == 0) p.save(); 54 | } 55 | 56 | void loop(void) { 57 | } 58 | 59 | void setupWifi(const char* ssid, const char* pwd) { 60 | WiFi.disconnect(); 61 | WiFi.mode(WIFI_STA); 62 | WiFi.begin(ssid, pwd); 63 | } 64 | 65 | bool waitForWifi(unsigned long aTimeout) { 66 | unsigned long timeNow = millis(); 67 | 68 | while ( WiFi.status() != WL_CONNECTED ) { 69 | delay(500); 70 | if ( millis() - timeNow > aTimeout ) { 71 | return true; 72 | } 73 | } 74 | return false; 75 | } 76 | -------------------------------------------------------------------------------- /examples/EBS_example02a_ParametersEEPROM/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ssid" : "your wifi ssid", 3 | "pwd" : "your wifi password", 4 | "cfg_url" : "http://raw.githubusercontent.com/arkhipenko/EspBootstrap/master/examples/EBS_example02_ParametersEEPROM/config.json", 5 | "ota_host" : "ota.home.lan", 6 | "ota_port" : "80", 7 | "ota_url" : "/esp/esp8266.php" 8 | } 9 | -------------------------------------------------------------------------------- /examples/EBS_example03_ParametersEEPROM_alt/EBS_example03_ParametersEEPROM_alt.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020, Anatoli Arkhipenko 3 | All rights reserved. 4 | 5 | This example illustrates how to use EspBootstrap library. 6 | The OTA update is not included. 7 | 8 | 1. Compile and upload into the chip 9 | 2. Start Serial monitor 10 | 3. Boot up the chip for the first time 11 | 4. Chip will attempt to load parameters and connect to WiFi 12 | 5. If not successful - chip will create an Access Point names ESPNNN-XXXXXX 13 | a. Connect your phone/PC to that Access Point. There is no password 14 | b. Connect your browser to http://10.1.1.1. You have 5 minutes to do steps 7-9 below 15 | c. Populate the Wifi Seetings 16 | d. Populate the path to a valid JSON configuration file 17 | (e.g.,: http://raw.githubusercontent.com/arkhipenko/EspBootstrap/master/examples/EBS_example01/config.json) 18 | e. Press "Submit" button. 19 | f. Device will reboot 20 | 6. Device should connect to your WiFi network, download, parse and save the configuration 21 | 7. If successful, device will remember that at least one successful attempt to download configuration was made, 22 | and going forward this set could be used offline if configuration server suddenly goes offline. 23 | If configurations were never downloaded successfully ata least once, then the 24 | 25 | */ 26 | 27 | // These two defines MUST be placed before the header files 28 | // to be taken into account by the library 29 | #define EEPROM_MAX 4096 30 | #define CTOKEN "EBS3" 31 | 32 | // ==================================== 33 | #define SSID1 "YOUR WIFI SSID HERE" 34 | #define PWD1 "YOUR WIFI PASSWORD HERE" 35 | 36 | #define _DEBUG_ 37 | //#define _TEST_ 38 | 39 | #ifdef _DEBUG_ 40 | #define _PP(a) Serial.print(a); 41 | #define _PL(a) Serial.println(a); 42 | #else 43 | #define _PP(a) 44 | #define _PL(a) 45 | #endif 46 | 47 | // ==== Includes =================================== 48 | #include 49 | #include 50 | #include 51 | 52 | // ==== Parameters and BootStrap =================== 53 | 54 | // Every configration structure should start with a token 55 | // "Token" is a character string identifying set of parameters (and a version) 56 | const String TOKEN(CTOKEN); 57 | 58 | // NPARS is a number of parameters in the structure 59 | // inclusive of the Token 60 | const int NPARS = 7; 61 | 62 | // The actual dictionary object to hold parameter values 63 | Dictionary d(NPARS); 64 | 65 | // The next two variables define how many fields are displayed on the web form 66 | // when the form is constructed. You could include all fields, or just a subset 67 | // First line is used as a title, the rest are the field labels 68 | // This structure works in conjunction with dictionary. Only the first NPARS_BTS 69 | // fields are displayed on the form. Fields are numbered in the order they inserted. 70 | const int NPARS_BTS = 3; 71 | 72 | // ==== CODE ============================== 73 | 74 | // This methods prints out parameters for inspection 75 | void printConfig() { 76 | _PL(); 77 | _PL("Config dump"); 78 | _PP("\ttoken : "); _PL(CTOKEN); 79 | _PP("\tssid : "); _PL(d["ssid"]); 80 | _PP("\tpasswd: "); _PL(d["pwd"]); 81 | _PP("\tconfig: "); _PL(d["cfg_url"]); 82 | _PP("\tota host: "); _PL(d["ota_host"]); 83 | _PP("\tota port: "); _PL(d["ota_port"]); 84 | _PP("\tota url : "); _PL(d["ota_url"]); 85 | _PL(); 86 | } 87 | 88 | 89 | 90 | // Arduino SETUP method 91 | void setup(void) { 92 | int rc; // Return code of the operations 93 | bool wifiTimeout; // Detect a WiFi connection timeout. 94 | 95 | // Setting up Serial console 96 | #ifdef _DEBUG_ 97 | Serial.begin(115200); 98 | delay(500); 99 | { 100 | _PL("EspBootStrap Alternative Dict Example"); _PL(); 101 | #if defined( ARDUINO_ARCH_ESP8266 ) 102 | String id = "ESP8266-" + String(ESP.getChipId(), HEX); 103 | #endif 104 | #if defined( ARDUINO_ARCH_ESP32 ) 105 | String id = "ESP32-" + String((uint32_t)( ESP.getEfuseMac() & 0xFFFFFFFFL ), HEX); 106 | #endif 107 | _PP("ESP Chip ID: "); _PL(id); 108 | _PL(); 109 | } 110 | #endif 111 | 112 | 113 | // ===================================================== 114 | // Alternative approach of bootstrapping for the case where 115 | // some of the key parameters' defaults maybe known at the 116 | // time of coding. 117 | // The benefit of doing it this way is - the device maybe able to 118 | // connect to a default wifi network and update its parameters to 119 | // the latest set in an unattended way without a need for manual 120 | // bootstrapping. 121 | 122 | // STEP 1: The default wifi connection is attempted, and device will 123 | // enter bootstrap mode if wifi connection could not be established 124 | 125 | // First entry is a Web Form "Title" (element #0) 126 | d("Title", "EspBootstrap Alt"); 127 | // The rest will be created as fields on the form: 128 | d("ssid", SSID1); 129 | d("password", PWD1); 130 | d("cfg_url", "http://raw.githubusercontent.com/arkhipenko/EspBootstrap/master/examples/EBS_example03_ParametersEEPROM_alt/config.json"); 131 | 132 | // **** PARAMETERS COMPONENT OF ESPBOOTSTRAP LIBRARY **** 133 | // ====================================================== 134 | // To work with Parameters we need to instanciate a Paramters object. 135 | // first parameter is a reference to the token, {String} 136 | // second parameter is a reference to the dictionary object holding parameters {Dictionary} 137 | // third parameter is the starting address in the EEPROM memory, {int} 138 | // fourth parameter is a size of EEPROM allocated to parameters 139 | // since the combined size of all key-value pairs is not known, make sure you allocate 140 | // enough space. Otherwise the Parameters object will not be activated. 141 | ParametersEEPROM *p_ptr = new ParametersEEPROM(TOKEN, d, 0, 1024); 142 | ParametersEEPROM& p = *p_ptr; 143 | 144 | // Define EEPROM_MAX to explicitly set maximum EEPROM capacity for your chip 145 | rc = p.begin(); 146 | _PP("EspBootStrap ParametersEEPROM initialized. rc = "); _PL(rc); 147 | 148 | // load() methods attempts to load parameters from the EEPROM 149 | // It checks for a valid CRC and a match on the TOKENs. 150 | // If either CRC or token do not match, an error is set 151 | // load() rc may indicate that values were overwritten with defaults due to CRC 152 | // or Token mismatch, and therefore return a non-zero value. Always check return code! 153 | // Upon success, the dictionary should be updated with key-value pairs from the EEPROM 154 | rc = p.load(); 155 | _PP("Configuration loaded. rc = "); _PL(rc); 156 | printConfig(); 157 | 158 | // The device will try to connect to the WiFi network (d["ssid"], d["password"]) 159 | // specified in the configuration, which could be part of the saved parameters or defaults. 160 | // It will time out after 30 seconds on an assumption that WiFi config is invalid 161 | // 30 seconds is arbitrary - you can set it for longer of shorter period of time 162 | 163 | _PP("Connecting to WiFi for 30 sec:"); 164 | setupWifi(d["ssid"].c_str(), d["password"].c_str()); 165 | wifiTimeout = waitForWifi(30 * BOOTSTRAP_SECOND); 166 | 167 | // STEP 2: 168 | // If connected to WiFi, device will attempt to load parameters from the configuration server 169 | // indicated by the "cfg_url" parameter. 170 | if ( !wifiTimeout ) { 171 | rc = JSONConfig.parse( d["cfg_url"], d); 172 | // If successful, the "d" dictionary should have a refreshed set of parameters from the JSON file. 173 | _PP("JSONConfig finished. rc = "); _PL(rc); 174 | _PP("Current dictionary count = "); _PL(d.count()); 175 | _PP("Current dictionary size = "); _PL(d.size()); 176 | 177 | // If reading configration JSON file ended successfully, a special key-value pair ("saved ok") 178 | // is inserted into the dictionary. This key indicated that the device was able to read configuration 179 | // successfully at least once, and can use this configuration for normal operations even if 180 | // subsequent configuration read is not successful for whatever reason. 181 | // This allows device to continue to operate even if, for instance, the configuration server is down. 182 | if (rc == JSON_OK) { 183 | p.save(); 184 | d("saved", "ok"); 185 | } 186 | } 187 | 188 | // STEP 3: 189 | // Now we are ready to assess what happened with our attempt to connect to WiFi, 190 | // load existing parameters, and parse JSON configuration from the config server. 191 | // The device will need bootstrapping in case of: 192 | // 1. WiFi timed out -OR- 193 | // 2. JSON config parsing ended up in error and a good set of parameters was never saved 194 | if ( wifiTimeout || !( rc == JSON_OK || d("saved") ) ) { 195 | _PL("Device needs bootstrapping:"); 196 | 197 | // **** BOOTSTRAP COMPONENT OF ESPBOOTSTRAP LIBRARY **** 198 | // ===================================================== 199 | // ESPBootstrap.run() takes 3 paramters: 200 | // Reference to the dictionary object with title and fields, {Dictionary} 201 | // Number of parameters to display on the web form, {int} 202 | // Timeout in milliseconds. (can use helper constants BOOTSTRAP_MINUTE and BOOTSTRAP_SECOND) 203 | rc = ESPBootstrap.run(d, NPARS_BTS, 5 * BOOTSTRAP_MINUTE); 204 | 205 | if (rc == BOOTSTRAP_OK) { 206 | // If bootstrap was successful, new set of parameters should be saved, 207 | // and processing continued 208 | p.save(); 209 | _PL("Bootstrapped OK. Rebooting."); 210 | } 211 | else { 212 | _PL("Bootstrap timed out. Rebooting."); 213 | } 214 | // or device should be restarted after a timeout 215 | printConfig(); 216 | delay(5000); 217 | ESP.restart(); 218 | } 219 | 220 | // If you got this far you should have configuration loaded and ready to go. 221 | _PP("Current dictionary count = "); _PL(d.count()); 222 | _PP("Current dictionary size = "); _PL(d.size()); 223 | printConfig(); 224 | 225 | // Continue with other setup() activities 226 | } 227 | 228 | void loop(void) { 229 | } 230 | 231 | // This method prepares for WiFi connection 232 | void setupWifi(const char* ssid, const char* pwd) { 233 | _PL("Setup_wifi()"); 234 | 235 | // We start by connecting to a WiFi network 236 | _PL("Connecting to WiFi..."); 237 | // clear wifi config 238 | WiFi.disconnect(); 239 | 240 | WiFi.mode(WIFI_STA); 241 | WiFi.begin(ssid, pwd); 242 | } 243 | 244 | // This method waits for a WiFi connection for aTimeout milliseconds. 245 | bool waitForWifi(unsigned long aTimeout) { 246 | _PL("WaitForWifi()"); 247 | 248 | unsigned long timeNow = millis(); 249 | 250 | while ( WiFi.status() != WL_CONNECTED ) { 251 | delay(1000); 252 | _PP("."); 253 | if ( millis() - timeNow > aTimeout ) { 254 | _PL(" WiFi connection timeout"); 255 | return true; 256 | } 257 | } 258 | 259 | _PL(" WiFi connected"); 260 | _PP("IP address: "); _PL(WiFi.localIP()); 261 | _PP("SSID: "); _PL(WiFi.SSID()); 262 | _PP("mac: "); _PL(WiFi.macAddress()); 263 | return false; 264 | } 265 | -------------------------------------------------------------------------------- /examples/EBS_example03_ParametersEEPROM_alt/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ssid" : "your wifi ssid", 3 | "pwd" : "your wifi password", 4 | "cfg_url" : "http://raw.githubusercontent.com/arkhipenko/EspBootstrap/master/examples/EBS_example03_ParametersEEPROM_alt/config.json", 5 | "ota_host" : "ota.home.lan", 6 | "ota_port" : "80", 7 | "ota_url" : "/esp/esp8266.php" 8 | } 9 | -------------------------------------------------------------------------------- /examples/EBS_example03a_ParametersEEPROM_alt/EBS_example03a_ParametersEEPROM_alt.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020, Anatoli Arkhipenko 3 | All rights reserved. 4 | 5 | This is exampe 3 without comments and debug messages 6 | 7 | */ 8 | 9 | #define EEPROM_MAX 4096 10 | #define CTOKEN "EBS3" 11 | 12 | // ==================================== 13 | #define SSID1 "YOUR WIFI SSID HERE" 14 | #define PWD1 "YOUR WIFI PASSWORD HERE" 15 | 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | const String TOKEN(CTOKEN); 22 | const int NPARS = 7; 23 | 24 | Dictionary d(NPARS); 25 | const int NPARS_BTS = 3; 26 | 27 | void setup(void) { 28 | int rc; // Return code of the operations 29 | bool wifiTimeout; // Detect a WiFi connection timeout. 30 | 31 | d("Title", "EspBootstrap Alt"); 32 | d("ssid", SSID1); 33 | d("password", PWD1); 34 | d("cfg_url", "http://raw.githubusercontent.com/arkhipenko/EspBootstrap/master/examples/EBS_example03_ParametersEEPROM_alt/config.json"); 35 | 36 | ParametersEEPROM *p_ptr = new ParametersEEPROM(TOKEN, d, 0, 1024); 37 | ParametersEEPROM& p = *p_ptr; 38 | 39 | rc = p.begin(); 40 | rc = p.load(); 41 | 42 | setupWifi(d["ssid"].c_str(), d["password"].c_str()); 43 | wifiTimeout = waitForWifi(30 * BOOTSTRAP_SECOND); 44 | 45 | if ( !wifiTimeout ) { 46 | rc = JSONConfig.parse( d["cfg_url"], d); 47 | if (rc == JSON_OK) { 48 | p.save(); 49 | d("saved", "ok"); 50 | } 51 | } 52 | 53 | if ( wifiTimeout || !( rc == JSON_OK || d("saved") ) ) { 54 | rc = ESPBootstrap.run(d, NPARS_BTS, 5 * BOOTSTRAP_MINUTE); 55 | 56 | if (rc == BOOTSTRAP_OK) { 57 | p.save(); 58 | } 59 | delay(5000); 60 | ESP.restart(); 61 | } 62 | 63 | // Continue with other setup() activities 64 | } 65 | 66 | void loop(void) { 67 | } 68 | 69 | void setupWifi(const char* ssid, const char* pwd) { 70 | WiFi.disconnect(); 71 | WiFi.mode(WIFI_STA); 72 | WiFi.begin(ssid, pwd); 73 | } 74 | 75 | bool waitForWifi(unsigned long aTimeout) { 76 | unsigned long timeNow = millis(); 77 | 78 | while ( WiFi.status() != WL_CONNECTED ) { 79 | delay(500); 80 | if ( millis() - timeNow > aTimeout ) { 81 | return true; 82 | } 83 | } 84 | return false; 85 | } 86 | -------------------------------------------------------------------------------- /examples/EBS_example03a_ParametersEEPROM_alt/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ssid" : "your wifi ssid", 3 | "pwd" : "your wifi password", 4 | "cfg_url" : "http://raw.githubusercontent.com/arkhipenko/EspBootstrap/master/examples/EBS_example03_ParametersEEPROM_alt/config.json", 5 | "ota_host" : "ota.home.lan", 6 | "ota_port" : "80", 7 | "ota_url" : "/esp/esp8266.php" 8 | } 9 | -------------------------------------------------------------------------------- /examples/EBS_example03b_ParametersEEPROM_alt/EBS_example03b_ParametersEEPROM_alt.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020, Anatoli Arkhipenko 3 | All rights reserved. 4 | 5 | This example illustrates how to use EspBootstrap library. 6 | The OTA update is not included. 7 | 8 | 1. Compile and upload into the chip 9 | 2. Start Serial monitor 10 | 3. Boot up the chip for the first time 11 | 4. Chip will attempt to load parameters and connect to WiFi 12 | 5. If not successful - chip will create an Access Point names ESPNNN-XXXXXX 13 | a. Connect your phone/PC to that Access Point. There is no password 14 | b. Connect your browser to http://10.1.1.1. You have 5 minutes to do steps 7-9 below 15 | c. Populate the Wifi Seetings 16 | d. Populate the host/port/url to a valid JSON configuration file 17 | (e.g.,: raw.githubusercontent.com, 80, /arkhipenko/EspBootstrap/master/examples/EBS_example01/config.json) 18 | e. Press "Submit" button. 19 | f. Device will reboot 20 | 6. Device should connect to your WiFi network, download, parse and save the configuration 21 | 7. If successful, device will remember that at least one successful attempt to download configuration was made, 22 | and going forward this set could be used offline if configuration server suddenly goes offline. 23 | If configurations were never downloaded successfully ata least once, then the 24 | 25 | */ 26 | 27 | // These two defines MUST be placed before the header files 28 | // to be taken into account by the library 29 | #define EEPROM_MAX 4096 30 | #define CTOKEN "EBS3b" 31 | 32 | // ==================================== 33 | #define SSID1 "YOUR WIFI SSID HERE" 34 | #define PWD1 "YOUR WIFI PASSWORD HERE" 35 | 36 | #define _DEBUG_ 37 | //#define _TEST_ 38 | 39 | #ifdef _DEBUG_ 40 | #define _PP(a) Serial.print(a); 41 | #define _PL(a) Serial.println(a); 42 | #else 43 | #define _PP(a) 44 | #define _PL(a) 45 | #endif 46 | 47 | 48 | // ==== INCLUDES ====================== 49 | #include 50 | #include 51 | #include 52 | 53 | 54 | // ==== Parameters and BootStrap =================== 55 | 56 | // Every configration structure should start with a token 57 | // "Token" is a character string identifying set of parameters (and a version) 58 | const String TOKEN(CTOKEN); 59 | 60 | // NPARS is a number of parameters in the structure 61 | // inclusive of the Token 62 | const int NPARS = 7; 63 | 64 | // The actual dictionary object to hold parameter values 65 | Dictionary d(NPARS); 66 | 67 | // The next two variables define how many fields are displayed on the web form 68 | // when the form is constructed. You could include all fields, or just a subset 69 | // First line is used as a title, the rest are the field labels 70 | // This structure works in conjunction with dictionary. Only the first NPARS_BTS 71 | // fields are displayed on the form. Fields are numbered in the order they inserted. 72 | const int NPARS_BTS = 5; 73 | 74 | // ==== CODE ============================== 75 | 76 | // This methods prints out parameters for inspection 77 | void printConfig() { 78 | _PL(); 79 | _PL("Config dump"); 80 | _PP("\ttoken : "); _PL(CTOKEN); 81 | _PP("\tssid : "); _PL(d["ssid"]); 82 | _PP("\tpasswd: "); _PL(d["pwd"]); 83 | _PP("\tcfg host: "); _PL(d["cfg_host"]); 84 | _PP("\tcfg port: "); _PL(d["cfg_port"]); 85 | _PP("\tcfg url: "); _PL(d["cfg_url"]); 86 | _PP("\tota host: "); _PL(d["ota_host"]); 87 | _PP("\tota port: "); _PL(d["ota_port"]); 88 | _PP("\tota url : "); _PL(d["ota_url"]); 89 | _PL(); 90 | } 91 | 92 | 93 | 94 | // Arduino SETUP method 95 | void setup(void) { 96 | int rc; // Return code of the operations 97 | bool wifiTimeout; // Detect a WiFi connection timeout. 98 | 99 | // Setting up Serial console 100 | #ifdef _DEBUG_ 101 | Serial.begin(115200); 102 | delay(500); 103 | { 104 | _PL("EspBootStrap Alternative Dict Example"); _PL(); 105 | #if defined( ARDUINO_ARCH_ESP8266 ) 106 | String id = "ESP8266-" + String(ESP.getChipId(), HEX); 107 | #endif 108 | #if defined( ARDUINO_ARCH_ESP32 ) 109 | String id = "ESP32-" + String((uint32_t)( ESP.getEfuseMac() & 0xFFFFFFFFL ), HEX); 110 | #endif 111 | _PP("ESP Chip ID: "); _PL(id); 112 | _PL(); 113 | } 114 | #endif 115 | 116 | 117 | // ===================================================== 118 | // Alternative approach of bootstrapping for the case where 119 | // some of the key parameters' defaults maybe known at the 120 | // time of coding. 121 | // The benefit of doing it this way is - the device maybe able to 122 | // connect to a default wifi network and update its parameters to 123 | // the latest set in an unattended way without a need for manual 124 | // bootstrapping. 125 | 126 | // STEP 1: The default wifi connection is attempted, and device will 127 | // enter bootstrap mode if wifi connection could not be established 128 | 129 | // First entry is a Web Form "Title" (element #0) 130 | d("Title", "EspBootstrap Alt"); 131 | // The rest will be created as fields on the form: 132 | d("ssid", SSID1); 133 | d("pwd", PWD1); 134 | d("cfg_host", "ota.home.lan"); 135 | d("cfg_port", "80"); 136 | d("cfg_url", "/esp/config/ebs03b_config.json"); 137 | 138 | // **** PARAMETERS COMPONENT OF ESPBOOTSTRAP LIBRARY **** 139 | // ====================================================== 140 | // To work with Parameters we need to instanciate a Paramters object. 141 | // first parameter is a reference to the token, {String} 142 | // second parameter is a reference to the dictionary object holding parameters {Dictionary} 143 | // third parameter is the starting address in the EEPROM memory, {int} 144 | // fourth parameter is a size of EEPROM allocated to parameters 145 | // since the combined size of all key-value pairs is not known, make sure you allocate 146 | // enough space. Otherwise the Parameters object will not be activated. 147 | ParametersEEPROM *p_ptr = new ParametersEEPROM(TOKEN, d, 0, 1024); 148 | ParametersEEPROM& p = *p_ptr; 149 | 150 | // Define EEPROM_MAX to explicitly set maximum EEPROM capacity for your chip 151 | rc = p.begin(); 152 | _PP("EspBootStrap ParametersEEPROM initialized. rc = "); _PL(rc); 153 | 154 | // load() methods attempts to load parameters from the EEPROM 155 | // It checks for a valid CRC and a match on the TOKENs. 156 | // If either CRC or token do not match, an error is set 157 | // load() rc may indicate that values were overwritten with defaults due to CRC 158 | // or Token mismatch, and therefore return a non-zero value. Always check return code! 159 | // Upon success, the dictionary should be updated with key-value pairs from the EEPROM 160 | rc = p.load(); 161 | _PP("Configuration loaded. rc = "); _PL(rc); 162 | printConfig(); 163 | 164 | // The device will try to connect to the WiFi network (d["ssid"], d["password"]) 165 | // specified in the configuration, which could be part of the saved parameters or defaults. 166 | // It will time out after 30 seconds on an assumption that WiFi config is invalid 167 | // 30 seconds is arbitrary - you can set it for longer of shorter period of time 168 | 169 | _PP("Connecting to WiFi for 30 sec:"); 170 | setupWifi(d["ssid"].c_str(), d["pwd"].c_str()); 171 | wifiTimeout = waitForWifi(30 * BOOTSTRAP_SECOND); 172 | 173 | // STEP 2: 174 | // If connected to WiFi, device will attempt to load parameters from the configuration server 175 | // This sketch uses the option with explicit host/port/url schema: 176 | // NOTE: host should not have 'http://' in front, just the domain name or ip address 177 | if ( !wifiTimeout ) { 178 | rc = JSONConfig.parse( d["cfg_host"], d["cfg_port"].toInt(), d["cfg_url"], d); 179 | // If successful, the "d" dictionary should have a refreshed set of parameters from the JSON file. 180 | _PP("JSONConfig finished. rc = "); _PL(rc); 181 | _PP("Current dictionary count = "); _PL(d.count()); 182 | _PP("Current dictionary size = "); _PL(d.size()); 183 | 184 | // If reading configration JSON file ended successfully, a special key-value pair ("saved ok") 185 | // is inserted into the dictionary. This key indicated that the device was able to read configuration 186 | // successfully at least once, and can use this configuration for normal operations even if 187 | // subsequent configuration read is not successful for whatever reason. 188 | // This allows device to continue to operate even if, for instance, the configuration server is down. 189 | if (rc == JSON_OK) { 190 | p.save(); 191 | d("saved", "ok"); 192 | } 193 | } 194 | 195 | // STEP 3: 196 | // Now we are ready to assess what happened with our attempt to connect to WiFi, 197 | // load existing parameters, and parse JSON configuration from the config server. 198 | // The device will need bootstrapping in case of: 199 | // 1. WiFi timed out -OR- 200 | // 2. JSON config parsing ended up in error and a good set of parameters was never saved 201 | if ( wifiTimeout || !( rc == JSON_OK || d("saved") ) ) { 202 | _PL("Device needs bootstrapping:"); 203 | 204 | // **** BOOTSTRAP COMPONENT OF ESPBOOTSTRAP LIBRARY **** 205 | // ===================================================== 206 | // ESPBootstrap.run() takes 3 paramters: 207 | // Reference to the dictionary object with title and fields, {Dictionary} 208 | // Number of parameters to display on the web form, {int} 209 | // Timeout in milliseconds. (can use helper constants BOOTSTRAP_MINUTE and BOOTSTRAP_SECOND) 210 | rc = ESPBootstrap.run(d, NPARS_BTS, 5 * BOOTSTRAP_MINUTE); 211 | 212 | if (rc == BOOTSTRAP_OK) { 213 | // If bootstrap was successful, new set of parameters should be saved, 214 | // and processing continued 215 | p.save(); 216 | _PL("Bootstrapped OK. Rebooting."); 217 | } 218 | else { 219 | _PL("Bootstrap timed out. Rebooting."); 220 | } 221 | // or device should be restarted after a timeout 222 | printConfig(); 223 | delay(5000); 224 | ESP.restart(); 225 | } 226 | 227 | // If you got this far you should have configuration loaded and ready to go. 228 | _PP("Current dictionary count = "); _PL(d.count()); 229 | _PP("Current dictionary size = "); _PL(d.size()); 230 | printConfig(); 231 | 232 | // Continue with other setup() activities 233 | } 234 | 235 | void loop(void) { 236 | } 237 | 238 | // This method prepares for WiFi connection 239 | void setupWifi(const char* ssid, const char* pwd) { 240 | _PL("Setup_wifi()"); 241 | 242 | // We start by connecting to a WiFi network 243 | _PL("Connecting to WiFi..."); 244 | // clear wifi config 245 | WiFi.disconnect(); 246 | 247 | WiFi.mode(WIFI_STA); 248 | WiFi.begin(ssid, pwd); 249 | } 250 | 251 | // This method waits for a WiFi connection for aTimeout milliseconds. 252 | bool waitForWifi(unsigned long aTimeout) { 253 | _PL("WaitForWifi()"); 254 | 255 | unsigned long timeNow = millis(); 256 | 257 | while ( WiFi.status() != WL_CONNECTED ) { 258 | delay(1000); 259 | _PP("."); 260 | if ( millis() - timeNow > aTimeout ) { 261 | _PL(" WiFi connection timeout"); 262 | return true; 263 | } 264 | } 265 | 266 | _PL(" WiFi connected"); 267 | _PP("IP address: "); _PL(WiFi.localIP()); 268 | _PP("SSID: "); _PL(WiFi.SSID()); 269 | _PP("mac: "); _PL(WiFi.macAddress()); 270 | return false; 271 | } 272 | -------------------------------------------------------------------------------- /examples/EBS_example04_ParametersSPIFFS/EBS_example04_ParametersSPIFFS.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020, Anatoli Arkhipenko 3 | All rights reserved. 4 | 5 | This example illustrates how to use EspBootstrap library with parameters saved on a filesystem. 6 | 7 | 1. Compile and upload into the chip 8 | 2. Start Serial monitor 9 | 3. Boot up the chip for the first time 10 | 4. Chip will attempt to load parameters and connect to WiFi 11 | 5. If not successful - chip will create an Access Point names ESPNNN-XXXXXX 12 | a. Connect your phone/PC to that Access Point. There is no password 13 | b. Connect your browser to http://10.1.1.1. You have 5 minutes to do steps 7-9 below 14 | c. Populate the Wifi Settings 15 | d. Press "Submit" button. 16 | e. Device will reboot 17 | 6. Device should connect to your WiFi network and read, parse and save the configuration to SPIFFS filesystem 18 | 19 | */ 20 | 21 | #define CTOKEN "EBS4" 22 | 23 | // ==================================== 24 | #define SSID1 "YOUR WIFI SSID HERE" 25 | #define PWD1 "YOUR WIFI PASSWORD HERE" 26 | 27 | #define _DEBUG_ 28 | //#define _TEST_ 29 | 30 | #ifdef _DEBUG_ 31 | #define _PP(a) Serial.print(a); 32 | #define _PL(a) Serial.println(a); 33 | #else 34 | #define _PP(a) 35 | #define _PL(a) 36 | #endif 37 | 38 | // ==== Includes =================================== 39 | #include 40 | #include 41 | 42 | // ==== Parameters and BootStrap =================== 43 | 44 | // Every configration structure should start with a token 45 | // "Token" is a character string identifying set of parameters (and a version) 46 | const String TOKEN(CTOKEN); 47 | 48 | // NPARS is a number of parameters in the structure 49 | // inclusive of the Token 50 | const int NPARS = 7; 51 | 52 | // The actual dictionary object to hold parameter values 53 | Dictionary d(NPARS); 54 | 55 | // The next two variables define how many fields are displayed on the web form 56 | // when the form is constructed. You could include all fields, or just a subset 57 | // First line is used as a title, the rest are the field labels 58 | // This structure works in conjunction with dictionary. Only the first NPARS_BTS 59 | // fields are displayed on the form. Fields are numbered in the order they inserted. 60 | const int NPARS_BTS = 3; 61 | 62 | // ==== CODE ============================== 63 | 64 | // This methods prints out parameters for inspection 65 | void printConfig() { 66 | _PL(); 67 | for (int i = 0; i < d.count(); i++) { 68 | _PP(d(i)); _PP(" : "); _PL(d[i]); 69 | } 70 | _PP("Current count = "); _PL(d.count()); 71 | _PP("Current size = "); _PL(d.size()); 72 | _PL(); 73 | } 74 | 75 | 76 | 77 | // Arduino SETUP method 78 | void setup(void) { 79 | int rc; // Return code of the operations 80 | bool wifiTimeout; // Detect a WiFi connection timeout. 81 | 82 | // Setting up Serial console 83 | #ifdef _DEBUG_ 84 | Serial.begin(115200); 85 | delay(500); 86 | { 87 | _PL("EspBootStrap SPIFFS Dict Example"); _PL(); 88 | #if defined( ARDUINO_ARCH_ESP8266 ) 89 | String id = "ESP8266-" + String(ESP.getChipId(), HEX); 90 | #endif 91 | #if defined( ARDUINO_ARCH_ESP32 ) 92 | String id = "ESP32-" + String((uint32_t)( ESP.getEfuseMac() & 0xFFFFFFFFL ), HEX); 93 | #endif 94 | _PP("ESP Chip ID: "); _PL(id); 95 | _PL(); 96 | } 97 | #endif 98 | 99 | 100 | // ===================================================== 101 | // Alternative approach of bootstrapping for the case where 102 | // some of the key parameters' defaults maybe known at the 103 | // time of coding. 104 | // The benefit of doing it this way is - the device maybe able to 105 | // connect to a default wifi network and update its parameters to 106 | // the latest set in an unattended way without a need for manual 107 | // bootstrapping. 108 | // Configuration is saved in the JSON file on the SPIFFS filesystem. 109 | 110 | // STEP 1: The default wifi connection is attempted, and device will 111 | // enter bootstrap mode if wifi connection could not be established 112 | 113 | // First entry is a Web Form "Title" (element #0) 114 | d("Title", "EspBootstrap Filesystem"); 115 | // The rest will be created as fields on the form: 116 | d("ssid", SSID1); 117 | d("password", PWD1); 118 | d("test4", "This is a test"); 119 | 120 | 121 | // VERY IMPORTANT: 122 | // Activation of the file system is the responsibility of the programmer 123 | // You may be using filesystem for other purposes, so the library should 124 | // not turn it on or off. 125 | SPIFFS.begin(); 126 | 127 | // **** PARAMETERS COMPONENT OF ESPBOOTSTRAP LIBRARY **** 128 | // ====================================================== 129 | // To work with Parameters we need to instanciate a Paramters object. 130 | // first parameter is a reference to the token, {String} 131 | // second parameter is a reference to the dictionary object holding parameters {Dictionary} 132 | // Since parameters are stored in a json file in the SPIFFS file system, the size of the file 133 | // does not matter. 134 | ParametersSPIFFS *p_ptr = new ParametersSPIFFS(TOKEN, d); 135 | ParametersSPIFFS& p = *p_ptr; 136 | 137 | rc = p.begin(); 138 | _PP("EspBootStrap ParametersSPIFFS initialized. rc = "); _PL(rc); 139 | 140 | // load() methods attempts to load parameters from the file on SPIFFS filesystem 141 | // The filename is same as the token. If the token is "EBS4", then the filename will be 142 | // "/EBS4.json". 143 | rc = p.load(); 144 | _PP("Configuration loaded. rc = "); _PL(rc); 145 | printConfig(); 146 | 147 | // The device will try to connect to the WiFi network (d["ssid"], d["password"]) 148 | // specified in the configuration, which could be part of the saved parameters or defaults. 149 | // It will time out after 30 seconds on an assumption that WiFi config is invalid 150 | // 30 seconds is arbitrary - you can set it for longer of shorter period of time 151 | 152 | _PP("Connecting to WiFi for 30 sec:"); 153 | setupWifi(d["ssid"].c_str(), d["password"].c_str()); 154 | wifiTimeout = waitForWifi(30 * BOOTSTRAP_SECOND); 155 | 156 | // STEP 2: 157 | // Now we are ready to assess what happened with our attempt to connect to WiFi, 158 | // load existing parameters, and parse JSON configuration from the config server. 159 | // The device will need bootstrapping in case of: 160 | // 1. WiFi timed out -OR- 161 | // 2. JSON config parsing ended up in error and a good set of parameters was never saved 162 | if ( wifiTimeout ) { 163 | _PL("Device needs bootstrapping:"); 164 | 165 | // **** BOOTSTRAP COMPONENT OF ESPBOOTSTRAP LIBRARY **** 166 | // ===================================================== 167 | // ESPBootstrap.run() takes 3 paramters: 168 | // Reference to the dictionary object with title and fields, {Dictionary} 169 | // Number of parameters to display on the web form, {int} 170 | // Timeout in milliseconds. (can use helper constants BOOTSTRAP_MINUTE and BOOTSTRAP_SECOND) 171 | rc = ESPBootstrap.run(d, NPARS_BTS, 5 * BOOTSTRAP_MINUTE); 172 | 173 | if (rc == BOOTSTRAP_OK) { 174 | // If bootstrap was successful, new set of parameters should be saved, 175 | // and processing continued 176 | rc = p.save(); 177 | _PL("Bootstrapped OK. Rebooting."); 178 | _PP("Parametes saved. rc = "); _PL(rc); 179 | } 180 | else { 181 | _PL("Bootstrap timed out. Rebooting."); 182 | } 183 | // or device should be restarted after a timeout 184 | printConfig(); 185 | delay(5000); 186 | ESP.restart(); 187 | } 188 | 189 | // If you got this far you should have configuration loaded and ready to go. 190 | printConfig(); 191 | 192 | // Continue with other setup() activities 193 | SPIFFS.end(); 194 | } 195 | 196 | void loop(void) { 197 | 198 | } 199 | 200 | // This method prepares for WiFi connection 201 | void setupWifi(const char* ssid, const char* pwd) { 202 | _PL("Setup_wifi()"); 203 | 204 | // We start by connecting to a WiFi network 205 | _PL("Connecting to WiFi..."); 206 | // clear wifi config 207 | WiFi.disconnect(); 208 | 209 | WiFi.mode(WIFI_STA); 210 | WiFi.begin(ssid, pwd); 211 | } 212 | 213 | // This method waits for a WiFi connection for aTimeout milliseconds. 214 | bool waitForWifi(unsigned long aTimeout) { 215 | _PL("WaitForWifi()"); 216 | 217 | unsigned long timeNow = millis(); 218 | 219 | while ( WiFi.status() != WL_CONNECTED ) { 220 | delay(1000); 221 | _PP("."); 222 | if ( millis() - timeNow > aTimeout ) { 223 | _PL(" WiFi connection timeout"); 224 | return true; 225 | } 226 | } 227 | 228 | _PL(" WiFi connected"); 229 | _PP("IP address: "); _PL(WiFi.localIP()); 230 | _PP("SSID: "); _PL(WiFi.SSID()); 231 | _PP("mac: "); _PL(WiFi.macAddress()); 232 | return false; 233 | } 234 | -------------------------------------------------------------------------------- /examples/EBS_example04_ParametersSPIFFS/data/EBS4.json: -------------------------------------------------------------------------------- 1 | {"Title":"EspBootstrap Filesystem", "ssid":"YOUR WIFI SSID HERE", "password":"YOUR WIFI PASSWORD HERE", "test4":"This is a test" } 2 | 3 | -------------------------------------------------------------------------------- /examples/EBS_example04a_ParametersSPIFFS/EBS_example04a_ParametersSPIFFS.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020, Anatoli Arkhipenko 3 | All rights reserved. 4 | 5 | This is exampe 4 without comments and debug messages 6 | 7 | */ 8 | 9 | #define CTOKEN "EBS4" 10 | 11 | // ==================================== 12 | #define SSID1 "YOUR WIFI SSID HERE" 13 | #define PWD1 "YOUR WIFI PASSWORD HERE" 14 | 15 | #include 16 | #include 17 | 18 | const String TOKEN(CTOKEN); 19 | const int NPARS = 7; 20 | 21 | Dictionary d(NPARS); 22 | const int NPARS_BTS = 3; 23 | 24 | void setup(void) { 25 | int rc; // Return code of the operations 26 | bool wifiTimeout; // Detect a WiFi connection timeout. 27 | 28 | d("Title", "EspBootstrap Alt"); 29 | d("ssid", SSID1); 30 | d("password", PWD1); 31 | d("test4", "This is a test"); 32 | 33 | SPIFFS.begin(); 34 | 35 | ParametersSPIFFS *p_ptr = new ParametersSPIFFS(TOKEN, d); 36 | ParametersSPIFFS& p = *p_ptr; 37 | 38 | rc = p.begin(); 39 | rc = p.load(); 40 | 41 | setupWifi(d["ssid"].c_str(), d["password"].c_str()); 42 | wifiTimeout = waitForWifi(30 * BOOTSTRAP_SECOND); 43 | 44 | if ( wifiTimeout ) { 45 | rc = ESPBootstrap.run(d, NPARS_BTS, 5 * BOOTSTRAP_MINUTE); 46 | 47 | if (rc == BOOTSTRAP_OK) { 48 | rc = p.save(); 49 | } 50 | delay(5000); 51 | ESP.restart(); 52 | } 53 | 54 | SPIFFS.end(); 55 | } 56 | 57 | void loop(void) { 58 | } 59 | 60 | void setupWifi(const char* ssid, const char* pwd) { 61 | WiFi.disconnect(); 62 | WiFi.mode(WIFI_STA); 63 | WiFi.begin(ssid, pwd); 64 | } 65 | 66 | bool waitForWifi(unsigned long aTimeout) { 67 | unsigned long timeNow = millis(); 68 | 69 | while ( WiFi.status() != WL_CONNECTED ) { 70 | delay(1000); 71 | if ( millis() - timeNow > aTimeout ) { 72 | return true; 73 | } 74 | } 75 | return false; 76 | } 77 | -------------------------------------------------------------------------------- /examples/EBS_example04a_ParametersSPIFFS/data/EBS4.json: -------------------------------------------------------------------------------- 1 | {"Title":"EspBootstrap Filesystem", "ssid":"YOUR WIFI SSID HERE", "password":"YOUR WIFI PASSWORD HERE", "test4":"This is a test" } 2 | 3 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For TaskManager 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | EspBootstrapDict KEYWORD1 10 | EspBootstrapMap KEYWORD1 11 | 12 | ParametersEEPROM KEYWORD1 13 | ParametersEEPROMMap KEYWORD1 14 | ParametersSPIFFS KEYWORD1 15 | ParametersSPIFFSMap KEYWORD1 16 | 17 | JsonConfigHttp KEYWORD1 18 | JsonConfigHttpMap KEYWORD1 19 | JsonConfigSPIFFS KEYWORD1 20 | JsonConfigSPIFFSMap KEYWORD1 21 | 22 | ####################################### 23 | # Methods and Functions (KEYWORD2) 24 | ####################################### 25 | 26 | run KEYWORD2 27 | 28 | clear KEYWORD2 29 | begin KEYWORD2 30 | loadDefaults KEYWORD2 31 | load KEYWORD2 32 | save KEYWORD2 33 | 34 | parse KEYWORD2 35 | 36 | ####################################### 37 | # Constants (LITERAL1) 38 | 39 | BOOTSTRAP_OK LITERAL1 40 | BOOTSTRAP_ERR LITERAL1 41 | BOOTSTRAP_TIMEOUT LITERAL1 42 | BOOTSTRAP_SECOND LITERAL1 43 | BOOTSTRAP_MINUTE LITERAL1 44 | ESPBootstrap LITERAL1 45 | 46 | PARAMS_OK LITERAL1 47 | PARAMS_ERR LITERAL1 48 | PARAMS_LEN LITERAL1 49 | PARAMS_CRC LITERAL1 50 | PARAMS_TOK LITERAL1 51 | PARAMS_FDE LITERAL1 52 | PARAMS_FER LITERAL1 53 | PARAMS_MEM LITERAL1 54 | PARAMS_ACT LITERAL1 55 | 56 | JSON_OK LITERAL1 57 | JSON_ERR LITERAL1 58 | JSON_COMMA LITERAL1 59 | JSON_COLON LITERAL1 60 | JSON_QUOTE LITERAL1 61 | JSON_BCKSL LITERAL1 62 | JSON_HTTPERR LITERAL1 63 | JSON_NOWIFI LITERAL1 64 | JSON_EOF LITERAL1 65 | JSON_FILERR LITERAL1 66 | JSON_FILENE LITERAL1 67 | JSONConfig LITERAL1 68 | 69 | _JSONCONFIG_NOSTATIC LITERAL1 70 | 71 | ####################################### 72 | 73 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"EspBootstrap", 3 | "description":"Library helps bring new esp devices online and connected into ecosystem. Read and parse config from json files on the web or SPIFFS.", 4 | "keywords":"eeprom,iot,wifi,config,json,parse", 5 | "authors": 6 | { 7 | "name": "Anatoli Arkhipenko", 8 | "maintainer": true 9 | }, 10 | "repository": 11 | { 12 | "type": "git", 13 | "url": "https://github.com/arkhipenko/EspBootstrap.git" 14 | }, 15 | "version": "2.3.2", 16 | "frameworks": "arduino", 17 | "platforms": ["espressif8266", "espressif32"] 18 | } 19 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=EspBootstrap 2 | version=2.3.2 3 | author=Anatoli Arkhipenko 4 | maintainer=Anatoli Arkhipenko 5 | sentence=Library helps bring new esp devices online and connected into ecosystem 6 | paragraph=Library helps bring new device online and connected into ecosystem: 1) Define runtime parameters 2) Get initial set of parameters from code or from user (via simple webform in AP mode or a file on SPIFFS) and 3) connect and download a simplified json config file + parse it. 4) Save all parameters in the EEPROM or file on SPIFFS for future use. Supports both parameter memory structures and Dictionary objects 7 | category=Other 8 | url=https://github.com/arkhipenko/EspBootstrap.git 9 | architectures=* 10 | -------------------------------------------------------------------------------- /src/EspBootstrapBase.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2020, Anatoli Arkhipenko. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 25 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef _ESPBOOTSTRAPBASE_H_ 32 | #define _ESPBOOTSTRAPBASE_H_ 33 | 34 | 35 | #include 36 | 37 | 38 | #if defined( ARDUINO_ARCH_ESP8266 ) 39 | #include 40 | #include 41 | #define WebServer ESP8266WebServer 42 | #endif 43 | 44 | #if defined( ARDUINO_ARCH_ESP32 ) 45 | #include 46 | #include 47 | #define WebServer WebServer 48 | #endif 49 | 50 | 51 | #define BOOTSTRAP_OK 0 52 | #define BOOTSTRAP_ERR (-1) 53 | #define BOOTSTRAP_CANCEL (-98) 54 | #define BOOTSTRAP_TIMEOUT (-99) 55 | 56 | #define BOOTSTRAP_SECOND 1000L 57 | #define BOOTSTRAP_MINUTE 60000L 58 | 59 | class EspBootstrapBase { 60 | public: 61 | EspBootstrapBase(); 62 | virtual ~EspBootstrapBase(); 63 | 64 | protected: 65 | int8_t iAllDone; 66 | WebServer* iServer; 67 | uint8_t iNum; 68 | uint32_t iTimeout; 69 | }; 70 | 71 | 72 | EspBootstrapBase::EspBootstrapBase () { 73 | iAllDone = false; 74 | } 75 | 76 | 77 | EspBootstrapBase::~EspBootstrapBase () { 78 | if (iServer) { 79 | iServer->stop(); 80 | iServer->close(); 81 | delete iServer; 82 | } 83 | } 84 | 85 | #ifndef SSID_PREFIX 86 | 87 | #if defined( ARDUINO_ARCH_ESP8266 ) 88 | #define SSID_PREFIX "esp8266-" 89 | #endif 90 | 91 | #if defined( ARDUINO_ARCH_ESP32 ) 92 | #define SSID_PREFIX "esp32-" 93 | #endif 94 | 95 | #ifndef SSID_PREFIX 96 | #define SSID_PREFIX "bootstrap-ap" 97 | #endif 98 | 99 | #endif 100 | 101 | #endif // _ESPBOOTSTRAPBASE_H_ -------------------------------------------------------------------------------- /src/EspBootstrapDict.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2020, Anatoli Arkhipenko. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 25 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef _ESPBOOTSTRAPDICT_H_ 32 | #define _ESPBOOTSTRAPDICT_H_ 33 | 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | class EspBootstrapDict : public EspBootstrapBase { 40 | public: 41 | EspBootstrapDict(); 42 | virtual ~EspBootstrapDict(); 43 | 44 | int8_t run(Dictionary &aDict, uint8_t aNum = 0, uint32_t aTimeout = 10 * BOOTSTRAP_MINUTE, bool aSecPass = true); 45 | void handleRoot (); 46 | void handleSubmit (); 47 | inline void cancel() { iCancelAP = true; } ; 48 | 49 | 50 | private: 51 | int8_t doRun(); 52 | 53 | bool iCancelAP; 54 | bool iSecurePassword; 55 | Dictionary* iDict; 56 | 57 | }; 58 | 59 | static EspBootstrapDict ESPBootstrap; 60 | 61 | EspBootstrapDict::EspBootstrapDict () { 62 | } 63 | 64 | 65 | EspBootstrapDict::~EspBootstrapDict () { 66 | } 67 | 68 | 69 | void __espbootstrap_handleroot() { 70 | ESPBootstrap.handleRoot(); 71 | } 72 | 73 | 74 | void __espbootstrap_handlesubmit() { 75 | ESPBootstrap.handleSubmit(); 76 | } 77 | 78 | 79 | int8_t EspBootstrapDict::run(Dictionary &aDict, uint8_t aNum, uint32_t aTimeout, bool aSecPass) { 80 | if (aNum == 0) { 81 | iNum = aDict.count() - 1; 82 | } 83 | else { 84 | iNum = aNum; 85 | } 86 | if (iNum > aDict.count() - 1) iNum = aDict.count() - 1; 87 | if (iNum == 0) iNum = 1; 88 | 89 | iDict = &aDict; 90 | iTimeout = aTimeout; 91 | iSecurePassword = aSecPass; 92 | 93 | iCancelAP = false; 94 | return doRun(); 95 | } 96 | 97 | 98 | int8_t EspBootstrapDict::doRun() { 99 | 100 | String ssid(SSID_PREFIX); 101 | const IPAddress APIP (10, 1, 1, 1); 102 | const IPAddress APMASK (255, 255, 255, 0); 103 | 104 | WiFi.disconnect(); 105 | WiFi.mode(WIFI_AP); 106 | 107 | ssid += WiFi.macAddress(); 108 | ssid.replace(":", ""); 109 | ssid.toLowerCase(); 110 | //#if defined( ARDUINO_ARCH_ESP8266 ) 111 | // ssid += String(ESP.getChipId(), HEX); 112 | //#endif 113 | #if defined( ARDUINO_ARCH_ESP32 ) 114 | // ssid += String((uint32_t)( ESP.getEfuseMac() & 0xFFFFFFFFL ), HEX); 115 | WiFi.softAP( ssid.c_str()); 116 | delay(50); 117 | #endif 118 | 119 | WiFi.softAPConfig(APIP, APIP, APMASK); 120 | delay(50); 121 | WiFi.softAP( ssid.c_str()); 122 | yield(); 123 | 124 | iServer = new WebServer(80); 125 | if (iServer == NULL) return BOOTSTRAP_ERR; 126 | 127 | iServer->on("/submit.html", __espbootstrap_handlesubmit); 128 | iServer->onNotFound(__espbootstrap_handleroot); 129 | 130 | iAllDone = false; 131 | iServer->begin(); 132 | 133 | uint32_t timeNow = millis(); 134 | while (!iAllDone && !iCancelAP) { 135 | iServer->handleClient(); 136 | if ( millis() - timeNow > iTimeout ) { 137 | iServer->stop(); 138 | iServer->close(); 139 | delete iServer; 140 | iServer = NULL; 141 | return BOOTSTRAP_TIMEOUT; 142 | } 143 | delay(10); 144 | // yield(); 145 | } 146 | 147 | iServer->stop(); 148 | iServer->close(); 149 | delete iServer; 150 | iServer = NULL; 151 | return (iCancelAP ? BOOTSTRAP_CANCEL: BOOTSTRAP_OK); 152 | } 153 | 154 | 155 | #define BUFLEN 256 156 | void EspBootstrapDict::handleRoot() { 157 | char buf[BUFLEN]; 158 | 159 | iServer->setContentLength(CONTENT_LENGTH_UNKNOWN); 160 | iServer->send(200, "text/html", "" ); 161 | iServer->sendContent(""); 162 | 163 | Dictionary& d = *iDict; 164 | snprintf(buf, BUFLEN, "

%s

", d[0].c_str() ); 165 | iServer->sendContent(buf); 166 | 167 | for (int i = 1; i <= iNum; i++) { 168 | String s = d(i); 169 | s.toUpperCase(); 170 | if ( iSecurePassword && (s.indexOf("PASSWORD") >= 0 || s.indexOf("PWD") >= 0) ) { 171 | snprintf(buf, BUFLEN, "

", i, d(i).c_str(), i, i, d[i].c_str() ); 172 | } 173 | else { 174 | snprintf(buf, BUFLEN, "

", i, d(i).c_str(), i, i, d[i].c_str() ); 175 | } 176 | iServer->sendContent(buf); 177 | } 178 | iServer->sendContent("
"); 179 | } 180 | 181 | 182 | void EspBootstrapDict::handleSubmit() { 183 | iServer->setContentLength(CONTENT_LENGTH_UNKNOWN); 184 | iServer->send(200, "text/html", "" ); 185 | iServer->sendContent("

Saved

"); 186 | iServer->sendContent("

Your changes are saved

"); 187 | 188 | Dictionary& d = *iDict; 189 | for (int i = 0; i < iServer->args() && i < iNum; i++) { 190 | d( d(i + 1), iServer->arg(i) ); 191 | } 192 | iAllDone = true; 193 | } 194 | 195 | 196 | 197 | #endif // _ESPBOOTSTRAPDICT_H_ -------------------------------------------------------------------------------- /src/EspBootstrapMap.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2020, Anatoli Arkhipenko. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 25 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef _ESPBOOTSTRAPMAP_H_ 32 | #define _ESPBOOTSTRAPMAP_H_ 33 | 34 | 35 | #include 36 | #include 37 | 38 | 39 | class EspBootstrapMap : public EspBootstrapBase { 40 | public: 41 | EspBootstrapMap(); 42 | virtual ~EspBootstrapMap(); 43 | 44 | int8_t run(const char** aTitles, char** aMap, uint8_t aNum, uint32_t aTimeout = 10 * BOOTSTRAP_MINUTE, bool aSecPass = true); 45 | void handleRoot (); 46 | void handleSubmit (); 47 | inline void cancel() { iCancelAP = true; } ; 48 | 49 | 50 | private: 51 | int8_t doRun(); 52 | 53 | bool iCancelAP; 54 | bool iSecurePassword; 55 | const char** iTitles; 56 | char** iMap; 57 | }; 58 | 59 | 60 | EspBootstrapMap::EspBootstrapMap () { 61 | iCancelAP = false; 62 | } 63 | 64 | 65 | EspBootstrapMap::~EspBootstrapMap () { 66 | } 67 | 68 | static EspBootstrapMap ESPBootstrap; 69 | 70 | void __espbootstrap_handleroot() { 71 | ESPBootstrap.handleRoot(); 72 | } 73 | 74 | 75 | void __espbootstrap_handlesubmit() { 76 | ESPBootstrap.handleSubmit(); 77 | } 78 | 79 | 80 | int8_t EspBootstrapMap::run(const char** aTitles, char** aMap, uint8_t aNum, uint32_t aTimeout, bool aSecPass) { 81 | 82 | iNum = aNum; 83 | iTitles = aTitles; 84 | iMap = aMap; 85 | iTimeout = aTimeout; 86 | iCancelAP = false; 87 | iSecurePassword = aSecPass; 88 | 89 | return doRun(); 90 | } 91 | 92 | 93 | int8_t EspBootstrapMap::doRun() { 94 | 95 | String ssid(SSID_PREFIX); 96 | const IPAddress APIP (10, 1, 1, 1); 97 | const IPAddress APMASK (255, 255, 255, 0); 98 | 99 | WiFi.disconnect(); 100 | WiFi.mode(WIFI_AP); 101 | 102 | ssid += WiFi.macAddress(); 103 | ssid.replace(":", ""); 104 | ssid.toLowerCase(); 105 | //#if defined( ARDUINO_ARCH_ESP8266 ) 106 | // ssid += String(ESP.getChipId(), HEX); 107 | //#endif 108 | #if defined( ARDUINO_ARCH_ESP32 ) 109 | // ssid += String((uint32_t)( ESP.getEfuseMac() & 0xFFFFFFFFL ), HEX); 110 | WiFi.softAP( ssid.c_str()); 111 | delay(50); 112 | #endif 113 | 114 | WiFi.softAPConfig(APIP, APIP, APMASK); 115 | delay(50); 116 | WiFi.softAP( ssid.c_str()); 117 | yield(); 118 | 119 | iServer = new WebServer(80); 120 | if (iServer == NULL) return BOOTSTRAP_ERR; 121 | 122 | iServer->on("/submit.html", __espbootstrap_handlesubmit); 123 | iServer->onNotFound(__espbootstrap_handleroot); 124 | 125 | iAllDone = false; 126 | iServer->begin(); 127 | 128 | uint32_t timeNow = millis(); 129 | while (!iAllDone && !iCancelAP) { 130 | iServer->handleClient(); 131 | if ( millis() - timeNow > iTimeout ) { 132 | iServer->stop(); 133 | iServer->close(); 134 | delete iServer; 135 | iServer = NULL; 136 | return BOOTSTRAP_TIMEOUT; 137 | } 138 | delay(10); 139 | // yield(); 140 | } 141 | 142 | iServer->stop(); 143 | iServer->close(); 144 | delete iServer; 145 | iServer = NULL; 146 | return (iCancelAP ? BOOTSTRAP_CANCEL: BOOTSTRAP_OK); 147 | } 148 | 149 | 150 | #define BUFLEN 256 151 | void EspBootstrapMap::handleRoot() { 152 | char buf[BUFLEN]; 153 | 154 | iServer->setContentLength(CONTENT_LENGTH_UNKNOWN); 155 | iServer->send(200, "text/html", "" ); 156 | iServer->sendContent(""); 157 | 158 | snprintf(buf, BUFLEN, "

%s

", iTitles[0] ); 159 | iServer->sendContent(buf); 160 | 161 | for (int i = 1; i <= iNum; i++) { 162 | if ( iSecurePassword && false ) { // fr future use 163 | snprintf(buf, BUFLEN, "

", i, iTitles[i], i, i, iMap[i - 1] ); 164 | } 165 | else { 166 | snprintf(buf, BUFLEN, "

", i, iTitles[i], i, i, iMap[i - 1] ); 167 | } 168 | iServer->sendContent(buf); 169 | } 170 | iServer->sendContent("
"); 171 | } 172 | 173 | 174 | void EspBootstrapMap::handleSubmit() { 175 | iServer->setContentLength(CONTENT_LENGTH_UNKNOWN); 176 | iServer->send(200, "text/html", "" ); 177 | iServer->sendContent("

Saved

"); 178 | iServer->sendContent("

Your changes are saved

"); 179 | 180 | for (int i = 0; i < iServer->args() && i < iNum; i++) { 181 | strcpy( iMap[i], iServer->arg(i).c_str() ); 182 | } 183 | iAllDone = true; 184 | } 185 | 186 | 187 | #endif // _ESPBOOTSTRAPMAP_H_ -------------------------------------------------------------------------------- /src/JsonConfigBase.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2020, Anatoli Arkhipenko. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 25 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef _JSONCONFIGBASE_H_ 32 | #define _JSONCONFIGBASE_H_ 33 | 34 | 35 | #include 36 | 37 | #define JSON_OK 0 38 | #define JSON_ERR (-1) 39 | #define JSON_COMMA (-20) 40 | #define JSON_COLON (-21) 41 | #define JSON_QUOTE (-22) 42 | #define JSON_BCKSL (-23) 43 | #define JSON_MEM (-24) 44 | #define JSON_FMT (-25) 45 | #define JSON_EOF (-99) 46 | 47 | class JsonConfigBase { 48 | public: 49 | JsonConfigBase(); 50 | virtual ~JsonConfigBase(); 51 | 52 | protected: 53 | virtual int8_t _doParse(Stream& aJson, uint16_t aNum); 54 | virtual int8_t _storeKeyValue(const char* aKey, const char* aValue) { return JSON_MEM; }; 55 | }; 56 | 57 | JsonConfigBase::JsonConfigBase() {} 58 | JsonConfigBase::~JsonConfigBase() {} 59 | 60 | int8_t JsonConfigBase::_doParse(Stream& aJson, uint16_t aNum) { 61 | bool insideQoute = false; 62 | bool nextVerbatim = false; 63 | bool isValue = false; 64 | bool isComment = false; 65 | int p = 0; 66 | int8_t rc; 67 | String currentKey; 68 | String currentValue; 69 | 70 | while ( aJson.peek() >= 0 ) { 71 | char c = aJson.read(); 72 | 73 | //#ifdef _LIBDEBUG_ 74 | //Serial.print((uint8_t)c); 75 | //Serial.print(" ("); 76 | //Serial.print(c); 77 | //Serial.println(")"); 78 | //#endif 79 | 80 | if ( isComment ) { 81 | if ( c == '\n' ) { 82 | isComment = false; 83 | isValue = false; 84 | } 85 | continue; 86 | } 87 | if (nextVerbatim) { 88 | nextVerbatim = false; 89 | } 90 | 91 | // not a comment and not a verbatim char 92 | else { 93 | // process all special cases: '\', '"', ':', and ',' 94 | if (c == '\\' ) { 95 | nextVerbatim = true; 96 | continue; 97 | } 98 | 99 | if ( c == '\"' ) { 100 | if (!insideQoute) { 101 | if ( isValue ) { 102 | if ( currentValue.length() > 0 ) return JSON_FMT; 103 | } 104 | else { 105 | if ( currentKey.length() > 0 ) return JSON_FMT; 106 | } 107 | insideQoute = true; 108 | continue; 109 | } 110 | else { 111 | insideQoute = false; 112 | continue; 113 | } 114 | } 115 | 116 | if (c == '\n') { 117 | if ( insideQoute ) { 118 | return JSON_QUOTE; 119 | } 120 | if ( nextVerbatim ) { 121 | return JSON_BCKSL; 122 | } 123 | } 124 | 125 | #ifdef _JSON_ASCII_ONLY 126 | if ( c > 127 ) continue; // ignore non-ascii characters 127 | #endif 128 | 129 | if (!insideQoute) { 130 | if ( c == '#' ) { 131 | isComment = true; 132 | continue; 133 | } 134 | 135 | if (c == ':') { 136 | if ( isValue ) { 137 | return JSON_COMMA; //missing comma probably 138 | } 139 | isValue = true; 140 | continue; 141 | } 142 | 143 | if ( c == '{' || c == ' ' || c == '\t' || c == '\r' ) continue; 144 | 145 | if ( c == ',' || c == '\n' || c == '}') { 146 | if ( isValue ) { 147 | if ( currentValue.length() == 0 ) return JSON_FMT; 148 | isValue = false; 149 | rc = _storeKeyValue( currentKey.c_str(), currentValue.c_str() ); 150 | if (rc) return JSON_MEM; // if error - exit with an error code 151 | currentValue = String(); 152 | currentKey = String(); 153 | p++; 154 | if (aNum > 0 && p >= aNum) break; 155 | } 156 | else { 157 | if ( c == ',' ) return JSON_FMT; 158 | } 159 | continue; 160 | } 161 | } 162 | } 163 | if (isValue) currentValue.concat(c); 164 | else currentKey.concat(c); 165 | } 166 | if (insideQoute || nextVerbatim || (aNum > 0 && p < aNum )) return JSON_EOF; 167 | #ifdef _LIBDEBUG_ 168 | Serial.printf("Dictionary::jload: DICTIONARY_OK\n"); 169 | #endif 170 | return JSON_OK; 171 | } 172 | 173 | /* int8_t JsonConfigBase::_doParse(size_t aLen, uint16_t aNum) { 174 | 175 | bool insideQoute = false; 176 | bool nextVerbatim = false; 177 | bool isValue = false; 178 | bool isComment = false; 179 | int p = 0; 180 | int8_t rc; 181 | String currentKey = String(); 182 | String currentValue = String(); 183 | 184 | for (int i = 0; i < aLen; i++) { 185 | char c; 186 | int16_t nrc = _nextChar(); 187 | if ( nrc < 0 ) break; //EOF 188 | c = (char) nrc; 189 | #ifdef _LIBDEBUG_ 190 | Serial.print(c); Serial.print("("); Serial.print((int)c); Serial.print(")"); 191 | #endif 192 | if ( isComment ) { 193 | if ( c == '\n' ) { 194 | isComment = false; 195 | isValue = false; 196 | } 197 | continue; 198 | } 199 | if (nextVerbatim) { 200 | nextVerbatim = false; 201 | } 202 | else { 203 | // process all special cases: '\', '"', ':', and ',' 204 | if (c == '\\' ) { 205 | nextVerbatim = true; 206 | continue; 207 | } 208 | if ( c == '#' ) { 209 | if ( !insideQoute ) { 210 | isComment = true; 211 | continue; 212 | } 213 | } 214 | if ( c == '\"' ) { 215 | if (!insideQoute) { 216 | insideQoute = true; 217 | continue; 218 | } 219 | else { 220 | insideQoute = false; 221 | if (isValue) { 222 | rc = _storeKeyValue( currentKey.c_str(), currentValue.c_str() ); 223 | if (rc) return JSON_MEM; // if error - exit with an error code 224 | currentValue = String(); 225 | currentKey = String(); 226 | p++; 227 | if (aNum > 0 && p >= aNum) break; 228 | } 229 | continue; 230 | } 231 | } 232 | if (c == '\n') { 233 | if ( insideQoute ) { 234 | return JSON_QUOTE; 235 | } 236 | if ( nextVerbatim ) { 237 | return JSON_BCKSL; 238 | } 239 | isValue = false; // missing comma, but let's forgive that 240 | continue; 241 | } 242 | if (!insideQoute) { 243 | if (c == ':') { 244 | if ( isValue ) { 245 | return JSON_COMMA; //missing comma probably 246 | } 247 | isValue = true; 248 | continue; 249 | } 250 | if (c == ',') { 251 | if ( !isValue ) { 252 | return JSON_COLON; //missing colon probably 253 | } 254 | isValue = false; 255 | continue; 256 | } 257 | if ( c == '{' || c == ' ' || c == '\t' || c == '\n' || c == '\r' ) continue; 258 | #ifdef _JSON_ASCII_ONLY 259 | if ( c > 127 ) continue; // ignore non-ascii characters 260 | #endif 261 | if ( c == '}' ) break; 262 | return JSON_FMT; 263 | } 264 | } 265 | if (insideQoute) { 266 | if (isValue) currentValue.concat(c); 267 | else currentKey.concat(c); 268 | } 269 | } 270 | if (insideQoute || nextVerbatim || (aNum > 0 && p < aNum )) return JSON_EOF; 271 | #ifdef _LIBDEBUG_ 272 | Serial.printf("JsonConfigBase::_doParse: JSON_OK\n"); 273 | #endif 274 | return JSON_OK; 275 | } */ 276 | 277 | 278 | 279 | #endif // _JSONCONFIGBASE_H_ -------------------------------------------------------------------------------- /src/JsonConfigHttp.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2020, Anatoli Arkhipenko. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 25 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef _JSONCONFIGHTTP_H_ 32 | #define _JSONCONFIGHTTP_H_ 33 | 34 | 35 | #include 36 | #include 37 | 38 | #if defined( ARDUINO_ARCH_ESP8266 ) 39 | #include 40 | #include 41 | #endif 42 | 43 | #if defined( ARDUINO_ARCH_ESP32 ) 44 | #include 45 | #include 46 | #endif 47 | 48 | 49 | #define JSON_HTTPERR (-97) 50 | #define JSON_NOWIFI (-98) 51 | 52 | 53 | class JsonConfigHttp : public JsonConfigBase { 54 | public: 55 | JsonConfigHttp(); 56 | virtual ~JsonConfigHttp(); 57 | 58 | int8_t parse(const String aUrl, Dictionary& aDict, int aNum = 0); 59 | int8_t parse(const String aHost, uint16_t aPort, String aUrl, Dictionary& aDict, int aNum = 0); 60 | 61 | protected: 62 | virtual int8_t _storeKeyValue(const char* aKey, const char* aValue); 63 | virtual int8_t _doParse(Stream& aJson, uint16_t aNum) { return JsonConfigBase::_doParse(aJson, aNum); }; 64 | 65 | private: 66 | int8_t parseCommon(int aHttpResult, int aNum); 67 | 68 | Dictionary* iDict; 69 | WiFiClient iClient; 70 | HTTPClient iHttp; 71 | // String iPayload; 72 | // size_t iIndex; 73 | }; 74 | 75 | #ifndef _JSONCONFIG_NOSTATIC 76 | static JsonConfigHttp JSONConfig; 77 | #endif 78 | 79 | JsonConfigHttp::JsonConfigHttp() {} 80 | JsonConfigHttp::~JsonConfigHttp() {} 81 | 82 | 83 | int8_t JsonConfigHttp::parse(const String aHost, uint16_t aPort, const String aUrl, Dictionary& aDict, int aNum) { 84 | int8_t rc; 85 | WiFiClient client; 86 | 87 | if (WiFi.status() != WL_CONNECTED) return JSON_NOWIFI; 88 | #ifdef _LIBDEBUG_ 89 | Serial.printf("JsonConfig: Connecting to: %s\n", aUrl.c_str()); 90 | #endif 91 | iDict = &aDict; 92 | rc = parseCommon( iHttp.begin(client, aHost, aPort, aUrl), aNum ); 93 | iHttp.end(); 94 | return rc; 95 | } 96 | 97 | 98 | int8_t JsonConfigHttp::parse(const String aUrl, Dictionary& aDict, int aNum) { 99 | int8_t rc; 100 | WiFiClient client; 101 | 102 | if (WiFi.status() != WL_CONNECTED) return JSON_NOWIFI; 103 | #ifdef _LIBDEBUG_ 104 | Serial.printf("JsonConfig parse: Connecting to: %s\n", aUrl.c_str()); 105 | #endif 106 | iDict = &aDict; 107 | rc = parseCommon( iHttp.begin(client, aUrl), aNum ); 108 | iHttp.end(); 109 | return rc; 110 | } 111 | 112 | 113 | int8_t JsonConfigHttp::parseCommon(int aHttpResult, int aNum) { 114 | int8_t rc; 115 | 116 | if ( aHttpResult ) { 117 | int httpCode = iHttp.GET(); 118 | // httpCode will be negative on error 119 | #ifdef _LIBDEBUG_ 120 | Serial.printf("JsonConfig parseCommon: httpCode = %d\n", httpCode); 121 | #endif 122 | if (httpCode > 0) { 123 | if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) { 124 | // iPayload = iHttp.getString(); 125 | // iIndex = 0; 126 | rc = _doParse(iHttp.getStream(), aNum); 127 | return rc; 128 | } 129 | } 130 | else { 131 | return httpCode; 132 | } 133 | return JSON_ERR; // should never get here anyway - but stupid compiler complains. 134 | } 135 | else { 136 | return JSON_HTTPERR; 137 | } 138 | return JSON_ERR; // should never get here anyway - but stupid compiler complains. 139 | } 140 | 141 | 142 | /* int16_t JsonConfigHttp::_nextChar() { 143 | if (iIndex < iPayload.length() ) { 144 | return (int16_t) iPayload[iIndex++]; 145 | } 146 | else { 147 | return JSON_EOF; 148 | } 149 | } */ 150 | 151 | 152 | int8_t JsonConfigHttp::_storeKeyValue(const char* aKey, const char* aValue){ 153 | return iDict->insert(aKey, aValue); 154 | } 155 | 156 | #endif // _JSONCONFIGHTTP_H_ -------------------------------------------------------------------------------- /src/JsonConfigHttpMap.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2020, Anatoli Arkhipenko. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 25 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef _JSONCONFIGHTTPMAP_H_ 32 | #define _JSONCONFIGHTTPMAP_H_ 33 | 34 | 35 | #include 36 | 37 | #if defined( ARDUINO_ARCH_ESP8266 ) 38 | #include 39 | #include 40 | #endif 41 | 42 | #if defined( ARDUINO_ARCH_ESP32 ) 43 | #include 44 | #include 45 | #endif 46 | 47 | 48 | #define JSON_HTTPERR (-97) 49 | #define JSON_NOWIFI (-98) 50 | 51 | 52 | class JsonConfigHttpMap : public JsonConfigBase { 53 | public: 54 | JsonConfigHttpMap(); 55 | virtual ~JsonConfigHttpMap(); 56 | 57 | int8_t parse(const String aUrl, char** aMap, int aNum); 58 | int8_t parse(const String aHost, uint16_t aPort, String aUrl, char** aMap, int aNum); 59 | 60 | protected: 61 | virtual int8_t _storeKeyValue(const char* aKey, const char* aValue); 62 | virtual int8_t _doParse(Stream& aJson, uint16_t aNum) { return JsonConfigBase::_doParse(aJson, aNum); }; 63 | 64 | private: 65 | int8_t parseCommon(int aHttpResult, int aNum); 66 | 67 | char** iMap; 68 | HTTPClient iHttp; 69 | // String iPayload; 70 | // size_t iIndex; 71 | size_t iParamIndex; 72 | }; 73 | 74 | #ifndef _JSONCONFIG_NOSTATIC 75 | static JsonConfigHttpMap JSONConfig; 76 | #endif 77 | 78 | JsonConfigHttpMap::JsonConfigHttpMap() {} 79 | JsonConfigHttpMap::~JsonConfigHttpMap() {} 80 | 81 | 82 | int8_t JsonConfigHttpMap::parse(const String aHost, uint16_t aPort, const String aUrl, char** aMap, int aNum) { 83 | int8_t rc; 84 | WiFiClient client; 85 | 86 | if (WiFi.status() != WL_CONNECTED) return JSON_NOWIFI; 87 | #ifdef _LIBDEBUG_ 88 | Serial.printf("JsonConfig: Connecting to: %s\n", aUrl.c_str()); 89 | #endif 90 | iMap = aMap; 91 | rc = parseCommon( iHttp.begin(client, aHost, aPort, aUrl), aNum ); 92 | iHttp.end(); 93 | return rc; 94 | } 95 | 96 | 97 | 98 | int8_t JsonConfigHttpMap::parse(const String aUrl, char** aMap, int aNum) { 99 | int8_t rc; 100 | WiFiClient client; 101 | 102 | if (WiFi.status() != WL_CONNECTED) return JSON_NOWIFI; 103 | #ifdef _LIBDEBUG_ 104 | Serial.printf("JsonConfig: Connecting to: %s\n", aUrl.c_str()); 105 | #endif 106 | iMap = aMap; 107 | rc = parseCommon( iHttp.begin(client, aUrl), aNum ); 108 | iHttp.end(); 109 | #ifdef _LIBDEBUG_ 110 | Serial.printf("JsonConfigHttpMap::parse rc %d\n", rc ); 111 | #endif 112 | return rc; 113 | } 114 | 115 | 116 | int8_t JsonConfigHttpMap::parseCommon(int aHttpResult, int aNum) { 117 | int8_t rc; 118 | 119 | if ( aHttpResult ) { 120 | int httpCode = iHttp.GET(); 121 | #ifdef _LIBDEBUG_ 122 | Serial.printf("JsonConfig: Connected httpCode= %d\n", httpCode ); 123 | #endif 124 | if (httpCode > 0) { 125 | if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) { 126 | // iPayload = iHttp.getString(); 127 | // iIndex = 0; 128 | iParamIndex = 0; 129 | rc = _doParse(iHttp.getStream(), aNum); 130 | #ifdef _LIBDEBUG_ 131 | Serial.printf("JsonConfigHttpMap::parseCommon rc %d\n", rc ); 132 | #endif 133 | return rc; 134 | } 135 | } 136 | else { 137 | return httpCode; 138 | } 139 | return JSON_ERR; // should never get here anyway - but stupid compiler complains. 140 | } 141 | else { 142 | return JSON_HTTPERR; 143 | } 144 | return JSON_ERR; // should never get here anyway - but stupid compiler complains. 145 | } 146 | 147 | /* 148 | int16_t JsonConfigHttpMap::_nextChar() { 149 | if (iIndex < iPayload.length() ) { 150 | return (int16_t) iPayload[iIndex++]; 151 | } 152 | else { 153 | return JSON_EOF; 154 | } 155 | } 156 | */ 157 | 158 | int8_t JsonConfigHttpMap::_storeKeyValue(const char* aKey, const char* aValue){ 159 | #ifdef _LIBDEBUG_ 160 | Serial.printf("JsonConfigHttpMap::_storeKeyValue: %s:%s\n", aKey, aValue ); 161 | // Serial.printf("iMap base address: %u, iMap[iParamIndex] address: %u\n", (uint32_t)iMap, (uint32_t)iMap[iParamIndex]); 162 | #endif 163 | 164 | strcpy(iMap[iParamIndex++], aValue); 165 | // memcpy(iMap[iParamIndex++], aValue, strlen(aValue)+1); 166 | return JSON_OK; 167 | } 168 | 169 | #endif // _JSONCONFIGHTTPMAP_H_ -------------------------------------------------------------------------------- /src/JsonConfigSPIFFS.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2020, Anatoli Arkhipenko. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 25 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef _JSONCONFIGSPIFFS_H_ 32 | #define _JSONCONFIGSPIFFS_H_ 33 | 34 | 35 | #include 36 | #include 37 | 38 | #if defined( ARDUINO_ARCH_ESP8266 ) 39 | #include 40 | #endif 41 | 42 | #if defined( ARDUINO_ARCH_ESP32 ) 43 | #include 44 | #include 45 | #endif 46 | 47 | #define JSON_FILERR (-95) 48 | #define JSON_FILENE (-96) 49 | 50 | class JsonConfigSPIFFS : public JsonConfigBase { 51 | public: 52 | JsonConfigSPIFFS(); 53 | virtual ~JsonConfigSPIFFS(); 54 | 55 | int8_t parse(const String aUrl, Dictionary& aDict, int aNum = 0); 56 | 57 | protected: 58 | virtual int8_t _storeKeyValue(const char* aKey, const char* aValue); 59 | virtual int8_t _doParse(Stream& aJson, uint16_t aNum) { return JsonConfigBase::_doParse(aJson, aNum); }; 60 | 61 | private: 62 | Dictionary* iDict; 63 | File iF; 64 | }; 65 | 66 | #ifndef _JSONCONFIG_NOSTATIC 67 | static JsonConfigSPIFFS JSONConfig; 68 | #endif 69 | 70 | JsonConfigSPIFFS::JsonConfigSPIFFS() { 71 | // SPIFFS.begin(); 72 | } 73 | 74 | JsonConfigSPIFFS::~JsonConfigSPIFFS() { 75 | // SPIFFS.end(); 76 | } 77 | 78 | int8_t JsonConfigSPIFFS::parse(const String aUrl, Dictionary& aDict, int aNum) { 79 | int8_t rc; 80 | 81 | if ( !SPIFFS.exists(aUrl) ) { // || !SPIFFS.isFile(aUrl) ) { 82 | return JSON_FILENE; 83 | } 84 | 85 | iF = SPIFFS.open(aUrl, "r"); 86 | if ( !iF ) { 87 | return JSON_FILERR; 88 | } 89 | 90 | iDict = &aDict; 91 | rc = _doParse ( iF, aNum ); 92 | 93 | iF.close(); 94 | return rc; 95 | } 96 | 97 | 98 | // int16_t JsonConfigSPIFFS::_nextChar() { 99 | // return (int16_t) iF.read(); 100 | // } 101 | 102 | 103 | int8_t JsonConfigSPIFFS::_storeKeyValue(const char* aKey, const char* aValue){ 104 | #ifdef _LIBDEBUG_ 105 | Serial.printf("JsonConfigSPIFFS::_storeKeyValue: %s:%s\n", aKey, aValue ); 106 | #endif 107 | return iDict->insert(aKey, aValue); 108 | } 109 | 110 | #endif // _JSONCONFIGSPIFFS_H_ -------------------------------------------------------------------------------- /src/JsonConfigSPIFFSMap.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2020, Anatoli Arkhipenko. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 25 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef _JSONCONFIGSPIFFSMAP_H_ 32 | #define _JSONCONFIGSPIFFSMAP_H_ 33 | 34 | 35 | #include 36 | 37 | #if defined( ARDUINO_ARCH_ESP8266 ) 38 | #include 39 | #endif 40 | 41 | #if defined( ARDUINO_ARCH_ESP32 ) 42 | #include 43 | #include 44 | #endif 45 | 46 | 47 | #define JSON_FILERR (-95) 48 | #define JSON_FILENE (-96) 49 | 50 | 51 | class JsonConfigSPIFFSMap : public JsonConfigBase { 52 | public: 53 | JsonConfigSPIFFSMap(); 54 | virtual ~JsonConfigSPIFFSMap(); 55 | 56 | int8_t parse(const String aUrl, char** aMap, int aNum); 57 | 58 | protected: 59 | virtual int8_t _storeKeyValue(const char* aKey, const char* aValue); 60 | virtual int8_t _doParse(Stream& aJson, uint16_t aNum) { return JsonConfigBase::_doParse(aJson, aNum); }; 61 | 62 | private: 63 | char** iMap; 64 | File iF; 65 | size_t iParamIndex; 66 | }; 67 | 68 | #ifndef _JSONCONFIG_NOSTATIC 69 | static JsonConfigSPIFFSMap JSONConfig; 70 | #endif 71 | 72 | JsonConfigSPIFFSMap::JsonConfigSPIFFSMap() {} 73 | JsonConfigSPIFFSMap::~JsonConfigSPIFFSMap() {} 74 | 75 | int8_t JsonConfigSPIFFSMap::parse(const String aUrl, char** aMap, int aNum) { 76 | int8_t rc; 77 | 78 | if ( !SPIFFS.exists(aUrl) ) { // || !SPIFFS.isFile(aUrl) ) { 79 | return JSON_FILENE; 80 | } 81 | 82 | iF = SPIFFS.open(aUrl, "r"); 83 | if ( !iF ) { 84 | return JSON_FILERR; 85 | } 86 | 87 | iMap = aMap; 88 | iParamIndex = 0; 89 | rc = _doParse ( iF, aNum ); 90 | 91 | iF.close(); 92 | return rc; 93 | } 94 | 95 | 96 | // char JsonConfigSPIFFSMap::_nextChar() { 97 | // return (int16_t) iF.read(); 98 | // } 99 | 100 | 101 | int8_t JsonConfigSPIFFSMap::_storeKeyValue(const char* aKey, const char* aValue){ 102 | strcpy(iMap[iParamIndex++], aValue); 103 | return JSON_OK; 104 | } 105 | 106 | #endif // _JSONCONFIGSPIFFSMAP_H_ -------------------------------------------------------------------------------- /src/ParametersBase.h: -------------------------------------------------------------------------------- 1 | #ifndef _PARAMETERSBASE_H_ 2 | #define _PARAMETERSBASE_H_ 3 | 4 | /* 5 | Copyright (c) 2015-2020, Anatoli Arkhipenko. 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, 12 | this list of conditions and the following disclaimer. 13 | 14 | 2. Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | 18 | 3. Neither the name of the copyright holder nor the names of its contributors 19 | may be used to endorse or promote products derived from this software without 20 | specific prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 23 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 26 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 28 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include 35 | 36 | // Error codes: 37 | #define PARAMS_OK 0 38 | #define PARAMS_ERR (-1) 39 | #define PARAMS_LEN (-2) 40 | #define PARAMS_CRC (-3) 41 | #define PARAMS_TOK (-4) 42 | #define PARAMS_FDE (-5) 43 | #define PARAMS_MEM (-98) 44 | #define PARAMS_ACT (-99) 45 | 46 | 47 | class ParametersBase { 48 | public: 49 | ParametersBase(const String& aToken); 50 | virtual ~ParametersBase(); 51 | 52 | virtual int8_t begin() = 0; 53 | virtual int8_t load() = 0; 54 | virtual int8_t save() = 0; 55 | 56 | virtual inline int8_t isActive() { 57 | return iActive; 58 | }; 59 | 60 | protected: 61 | int8_t iActive; 62 | const String& iToken; 63 | }; 64 | 65 | ParametersBase::ParametersBase(const String& aToken) : iToken(aToken) { 66 | iActive = false; 67 | } 68 | 69 | ParametersBase::~ParametersBase () {} 70 | 71 | #endif // _PARAMETERSBASE_H_ -------------------------------------------------------------------------------- /src/ParametersEEPROM.h: -------------------------------------------------------------------------------- 1 | #ifndef _PARAMETERSEEPROM_H_ 2 | #define _PARAMETERSEEPROM_H_ 3 | 4 | /* 5 | Copyright (c) 2015-2020, Anatoli Arkhipenko. 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, 12 | this list of conditions and the following disclaimer. 13 | 14 | 2. Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | 18 | 3. Neither the name of the copyright holder nor the names of its contributors 19 | may be used to endorse or promote products derived from this software without 20 | specific prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 23 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 26 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 28 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include 35 | #include 36 | #include 37 | 38 | #ifndef EEPROM_MAX 39 | 40 | #if defined( ARDUINO_ARCH_AVR ) 41 | #define EEPROM_MAX 512 42 | #endif 43 | 44 | #if defined( ARDUINO_ARCH_ESP8266 ) || defined( ARDUINO_ARCH_ESP32 ) 45 | #define EEPROM_MAX 4096 46 | #endif 47 | 48 | #ifndef EEPROM_MAX 49 | #define EEPROM_MAX 256 // safe default 50 | #endif 51 | 52 | #endif // #ifndef EEPROM_MAX 53 | 54 | class ParametersEEPROM : public ParametersBase { 55 | public: 56 | ParametersEEPROM(const String & aToken, Dictionary & aDict, uint16_t aAddress, uint16_t aSize ) ; 57 | virtual ~ParametersEEPROM(); 58 | 59 | virtual int8_t begin(); 60 | virtual int8_t load(); 61 | virtual int8_t save(); 62 | 63 | void clear(); 64 | 65 | private: 66 | uint8_t checksum (); 67 | 68 | Dictionary& iDict; 69 | uint16_t iAddress; 70 | uint8_t* iData; 71 | uint16_t iSize; 72 | }; 73 | 74 | ParametersEEPROM::ParametersEEPROM(const String& aToken, Dictionary& aDict, uint16_t aAddress, uint16_t aSize ) : ParametersBase(aToken), iDict(aDict) { 75 | iActive = false; 76 | iAddress = aAddress; 77 | iSize = aSize; 78 | iData = NULL; 79 | } 80 | 81 | 82 | ParametersEEPROM::~ParametersEEPROM() { 83 | if (iActive) { 84 | save(); 85 | #if defined( ARDUINO_ARCH_ESP8266 ) || defined( ARDUINO_ARCH_ESP32 ) 86 | EEPROM.end(); 87 | #endif 88 | iActive = false; 89 | } 90 | } 91 | 92 | 93 | int8_t ParametersEEPROM::begin() { 94 | uint16_t maxLen = iToken.length() + iDict.esize() + 4; // 4: 1 null for token, 1 crc8, 2 bytes for count 95 | if ( iSize < EEPROM_MAX && maxLen <= iSize) { 96 | #if defined( ARDUINO_ARCH_ESP8266 ) || defined( ARDUINO_ARCH_ESP32 ) 97 | EEPROM.begin(4096); // allocate all memory 98 | #endif 99 | iActive = true; 100 | return PARAMS_OK; 101 | } 102 | else { 103 | return PARAMS_LEN; 104 | } 105 | } 106 | 107 | 108 | int8_t ParametersEEPROM::load() { 109 | uint16_t iTl = iToken.length(); 110 | 111 | if (!iActive) { 112 | return PARAMS_ACT; 113 | } 114 | 115 | iData = (uint8_t * ) malloc(iSize); 116 | if (iData == NULL) { 117 | // iRc = PARAMS_MEM; 118 | return PARAMS_MEM; 119 | } 120 | uint8_t* p = iData; 121 | 122 | for (uint16_t i = 0; i < iSize - 1; i++, p++) { 123 | *p = EEPROM.read(iAddress + i); 124 | } 125 | uint8_t crc = EEPROM.read( iAddress + iSize - 1); 126 | 127 | // Check CRC 128 | if (crc != checksum () ) { 129 | free(iData); 130 | iData = NULL; 131 | return PARAMS_CRC; 132 | } 133 | 134 | // Check Token 135 | if ( strncmp( (const char *) iToken.c_str(), (const char *) iData, iSize ) != 0 ) { 136 | free(iData); 137 | iData = NULL; 138 | return PARAMS_TOK; 139 | } 140 | 141 | // Populate the dictionary 142 | p = iData + (iTl + 1); 143 | 144 | uint16_t cnt = *p | ((((uint16_t) * (p + 1)) << 8) & 0xff00); 145 | p += 2; 146 | 147 | for (uint16_t i = 0; i < cnt; i++) { 148 | String k = String((char* )p); 149 | p += (k.length() + 1); 150 | String v = String((char* )p); 151 | p += (v.length() + 1); 152 | iDict(k, v); 153 | } 154 | 155 | free(iData); 156 | iData = NULL; 157 | return PARAMS_OK; 158 | // if ( iMode == PARAMS_FILE ) { 159 | // String file = "/" + iToken + ".json"; 160 | // if ( !SPIFFS.exists(file) ) { 161 | // return PARAMS_FDE; 162 | // } 163 | // } 164 | } 165 | 166 | 167 | int8_t ParametersEEPROM::save() { 168 | uint8_t changed = 0; 169 | int8_t rc = PARAMS_OK; 170 | 171 | if (!iActive) { 172 | return PARAMS_ACT; 173 | } 174 | 175 | uint16_t iTl = iToken.length(); 176 | uint16_t iDs = iDict.esize(); 177 | uint16_t iDc = iDict.count(); 178 | uint16_t maxLen = iTl + iDs + 4; 179 | 180 | if ( maxLen >= iSize ) { 181 | return PARAMS_LEN; 182 | } 183 | iData = (uint8_t * ) malloc(iSize); 184 | if (iData == NULL) { 185 | return PARAMS_MEM; 186 | } 187 | clear(); 188 | uint8_t* p = iData; 189 | 190 | #ifdef _LIBDEBUG_ 191 | Serial.println ("Parameters save: memory allocated and cleared"); 192 | Serial.print("Token value: "); 193 | Serial.println(iToken); 194 | Serial.println(iToken.c_str()); 195 | #endif 196 | 197 | strcpy((char*)p, iToken.c_str()); 198 | p += (iTl + 1); 199 | 200 | #ifdef _LIBDEBUG_ 201 | Serial.println ("Parameters save: token copied"); 202 | #endif 203 | 204 | *p++ = iDc & 0xff; 205 | *p++ = (iDc >> 8) & 0xff; 206 | 207 | for (uint16_t i = 0; i < iDc; i++) { 208 | strcpy((char*)p, iDict(i).c_str()); 209 | p += (iDict(i).length() + 1); 210 | strcpy((char*)p, iDict[i].c_str()); 211 | p += (iDict[i].length() + 1); 212 | 213 | #ifdef _LIBDEBUG_ 214 | Serial.printf ("Parameters save: k-v pair #%d copies: %s : %s\n", i, iDict(i).c_str(), iDict[i].c_str()); 215 | #endif 216 | 217 | } 218 | 219 | #ifdef _LIBDEBUG_ 220 | Serial.println ("Parameters save: Key-value pairs copied"); 221 | #endif 222 | 223 | 224 | p = iData; 225 | 226 | for (uint16_t i = 0; i < iSize - 1; i++, p++) { 227 | #if defined( ARDUINO_ARCH_AVR ) 228 | EEPROM.update(iAddress + i, *p); 229 | #else 230 | // protect memory from excessive writes 231 | uint8_t b = EEPROM.read(iAddress + i); 232 | if ( b != *p) { 233 | EEPROM.write(iAddress + i, *p); 234 | changed = 1; 235 | } 236 | #endif 237 | } 238 | 239 | #ifdef _LIBDEBUG_ 240 | Serial.println ("Parameters save: EEPROM updated"); 241 | #endif 242 | 243 | 244 | #if defined( ARDUINO_ARCH_AVR ) 245 | EEPROM.update( iAddress + iSize - 1, checksum () ); 246 | #else 247 | { 248 | uint8_t b = EEPROM.read(iAddress + iSize - 1); 249 | uint8_t c = checksum (); 250 | if ( b != c ) { 251 | EEPROM.write( iAddress + iSize - 1, c ); 252 | changed = 1; 253 | } 254 | } 255 | #endif 256 | #if defined( ARDUINO_ARCH_ESP8266 ) || defined( ARDUINO_ARCH_ESP32 ) 257 | if ( changed ) { 258 | if ( !EEPROM.commit() ) rc = PARAMS_ERR; 259 | } 260 | #endif 261 | 262 | #ifdef _LIBDEBUG_ 263 | Serial.println ("Parameters save: checksum saved"); 264 | #endif 265 | 266 | 267 | free(iData); 268 | iData = NULL; 269 | 270 | #ifdef _LIBDEBUG_ 271 | Serial.println ("Parameters save: memory freed"); 272 | #endif 273 | 274 | return rc; 275 | 276 | } 277 | 278 | 279 | void ParametersEEPROM::clear () { 280 | if (iData) { 281 | memset((void *) iData, 0, iSize - 1); 282 | } 283 | } 284 | 285 | 286 | #define CRCMASK 0x1d 287 | uint8_t ParametersEEPROM::checksum () { 288 | uint8_t crc = 0; 289 | uint8_t *ptr = (uint8_t *) iData; 290 | 291 | 292 | for (int j = 0; j < iSize - 1; j++, ptr++) { 293 | crc ^= *ptr; 294 | for (int i = 0; i < 8; i++) { 295 | if ( crc & 0x80 ) 296 | crc = (uint8_t)((crc << 1) ^ CRCMASK); 297 | else 298 | crc <<= 1; 299 | } 300 | } 301 | return crc; 302 | } 303 | 304 | #endif // _PARAMETERSEEPROM_H_ -------------------------------------------------------------------------------- /src/ParametersEEPROMMap.h: -------------------------------------------------------------------------------- 1 | #ifndef _PARAMETERSEEPROMMAP_H_ 2 | #define _PARAMETERSEEPROMMAP_H_ 3 | 4 | /* 5 | Copyright (c) 2015-2020, Anatoli Arkhipenko. 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, 12 | this list of conditions and the following disclaimer. 13 | 14 | 2. Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | 18 | 3. Neither the name of the copyright holder nor the names of its contributors 19 | may be used to endorse or promote products derived from this software without 20 | specific prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 23 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 26 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 28 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include 35 | #include 36 | 37 | #ifndef EEPROM_MAX 38 | 39 | #define EEPROM_MAX 256 // safe default 40 | 41 | #if defined( ARDUINO_ARCH_AVR ) 42 | #define EEPROM_MAX 512 43 | #endif 44 | 45 | #if defined( ARDUINO_ARCH_ESP8266 ) || defined( ARDUINO_ARCH_ESP32 ) 46 | #define EEPROM_MAX 4096 47 | #endif 48 | 49 | #endif // #ifndef EEPROM_MAX 50 | 51 | class ParametersEEPROMMap : public ParametersBase { 52 | public: 53 | ParametersEEPROMMap( const String& aToken, void* aPtr, void* aDeflt = NULL, uint16_t aAddress = 0, uint16_t aLength = (EEPROM_MAX-1) ); 54 | virtual ~ParametersEEPROMMap(); 55 | 56 | virtual int8_t begin(); 57 | virtual int8_t load(); 58 | virtual int8_t save(); 59 | 60 | void loadDefaults(); 61 | void clear(); 62 | 63 | private: 64 | uint8_t checksum (); 65 | 66 | void* iData; 67 | void* iDefault; 68 | uint16_t iAddress; 69 | uint16_t iLen; 70 | uint16_t iMaxLen; 71 | 72 | }; 73 | 74 | ParametersEEPROMMap::ParametersEEPROMMap(const String& aToken, void* aPtr, void* aDeflt, uint16_t aAddress, uint16_t aLength ) : ParametersBase(aToken) { 75 | iActive = false; 76 | iAddress = aAddress; 77 | 78 | iData = aPtr; 79 | iDefault = aDeflt; 80 | iLen = aLength; 81 | iMaxLen = iLen + 1; // to account for 1 additional crc byte 82 | } 83 | 84 | 85 | ParametersEEPROMMap::~ParametersEEPROMMap() { 86 | if (iActive) { 87 | save(); 88 | #if defined( ARDUINO_ARCH_ESP8266 ) || defined( ARDUINO_ARCH_ESP32 ) 89 | EEPROM.end(); 90 | #endif 91 | iActive = false; 92 | } 93 | } 94 | 95 | 96 | int8_t ParametersEEPROMMap::begin() { 97 | 98 | if ( iMaxLen < 4 ) iMaxLen = 4; 99 | if ( iMaxLen <= EEPROM_MAX ) { 100 | #if defined( ARDUINO_ARCH_ESP8266 ) || defined( ARDUINO_ARCH_ESP32 ) 101 | EEPROM.begin(4096); 102 | #endif 103 | iActive = true; 104 | return PARAMS_OK; 105 | } 106 | else { 107 | return PARAMS_LEN; 108 | } 109 | } 110 | 111 | 112 | int8_t ParametersEEPROMMap::load() { 113 | uint8_t *ptr = (uint8_t *) iData; 114 | 115 | if (!iActive) { 116 | return PARAMS_ACT; 117 | } 118 | 119 | for (uint16_t i = 0; i < iLen; i++, ptr++) { 120 | *ptr = EEPROM.read(iAddress + i); 121 | } 122 | uint8_t crc = EEPROM.read( iAddress + iLen); 123 | 124 | if (crc != checksum () ) { 125 | loadDefaults(); 126 | return PARAMS_CRC; 127 | } 128 | 129 | if ( strncmp( (const char *) iToken.c_str(), (const char *) iData, iMaxLen ) != 0 ) { 130 | loadDefaults(); 131 | return PARAMS_TOK; 132 | } 133 | return PARAMS_OK; 134 | } 135 | 136 | 137 | int8_t ParametersEEPROMMap::save() { 138 | uint8_t *ptr = (uint8_t *) iData; 139 | uint8_t changed = 0; 140 | int8_t rc = PARAMS_OK; 141 | 142 | if (!iActive) { 143 | return PARAMS_ACT; 144 | } 145 | 146 | // if ( iLen >= iMaxLen ) { 147 | // return PARAMS_LEN; 148 | // } 149 | for (uint16_t i = 0; i < iLen; i++, ptr++) { 150 | #if defined( ARDUINO_ARCH_AVR ) 151 | EEPROM.update(iAddress + i, *ptr); 152 | #else 153 | // protect memory from excessive writes 154 | uint8_t b = EEPROM.read(iAddress + i); 155 | if ( b != *ptr) { 156 | EEPROM.write(iAddress + i, *ptr); 157 | changed = 1; 158 | } 159 | #endif 160 | } 161 | #if defined( ARDUINO_ARCH_AVR ) 162 | EEPROM.update( iAddress + iLen, checksum () ); 163 | #else 164 | { 165 | uint8_t b = EEPROM.read(iAddress + iLen); 166 | uint8_t c = checksum (); 167 | if ( b != c ) { 168 | EEPROM.write( iAddress + iLen, c ); 169 | changed = 1; 170 | } 171 | } 172 | // EEPROM.write( iAddress + iLen, checksum () ); 173 | #endif 174 | #if defined( ARDUINO_ARCH_ESP8266 ) || defined( ARDUINO_ARCH_ESP32 ) 175 | if ( changed ) { 176 | if ( !EEPROM.commit() ) rc = PARAMS_ERR; 177 | } 178 | #endif 179 | 180 | return rc; 181 | } 182 | 183 | 184 | void ParametersEEPROMMap::loadDefaults () { 185 | if ( iDefault ) { 186 | memcpy(iData, iDefault, iLen); 187 | } 188 | else { 189 | clear(); 190 | } 191 | strcpy((char *) iData, iToken.c_str()); 192 | } 193 | 194 | 195 | void ParametersEEPROMMap::clear () { 196 | memset( iData, 0, iLen ); 197 | } 198 | 199 | 200 | #define CRCMASK 0x1d 201 | uint8_t ParametersEEPROMMap::checksum () { 202 | uint8_t crc = 0; 203 | uint8_t *ptr = (uint8_t *) iData; 204 | 205 | 206 | for (int j = 0; j < iLen; j++, ptr++) { 207 | crc ^= *ptr; 208 | for (int i = 0; i < 8; i++) { 209 | if ( crc & 0x80 ) 210 | crc = (uint8_t)((crc << 1) ^ CRCMASK); 211 | else 212 | crc <<= 1; 213 | } 214 | } 215 | return crc; 216 | } 217 | 218 | #endif // _PARAMETERSEEPROMMAP_H_ 219 | -------------------------------------------------------------------------------- /src/ParametersSPIFFS.h: -------------------------------------------------------------------------------- 1 | #ifndef _PARAMETERSSPIFFS_H_ 2 | #define _PARAMETERSSPIFFS_H_ 3 | 4 | /* 5 | Copyright (c) 2015-2020, Anatoli Arkhipenko. 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, 12 | this list of conditions and the following disclaimer. 13 | 14 | 2. Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | 18 | 3. Neither the name of the copyright holder nor the names of its contributors 19 | may be used to endorse or promote products derived from this software without 20 | specific prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 23 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 26 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 28 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include 35 | #include 36 | #include 37 | 38 | #define PARAMS_FER (-6) 39 | 40 | class ParametersSPIFFS : public ParametersBase { 41 | public: 42 | ParametersSPIFFS(const String& aToken, Dictionary& aDict ) ; 43 | virtual ~ParametersSPIFFS(); 44 | 45 | virtual int8_t begin(); 46 | virtual int8_t load(); 47 | virtual int8_t save(); 48 | 49 | void clear(); 50 | 51 | private: 52 | 53 | Dictionary& iDict; 54 | String iFile; 55 | }; 56 | 57 | ParametersSPIFFS::ParametersSPIFFS(const String& aToken, Dictionary& aDict ) : ParametersBase(aToken), iDict(aDict) { 58 | iActive = false; 59 | } 60 | 61 | 62 | ParametersSPIFFS::~ParametersSPIFFS() { 63 | if (iActive) { 64 | save(); 65 | iActive = false; 66 | } 67 | } 68 | 69 | 70 | int8_t ParametersSPIFFS::begin() { 71 | iActive = true; 72 | iFile = String("/") + iToken + ".json"; 73 | #ifdef _LIBDEBUG_ 74 | Serial.printf("ParametersSPIFFS: config file = %s\n", iFile.c_str()); 75 | #endif 76 | return PARAMS_OK; 77 | } 78 | 79 | 80 | int8_t ParametersSPIFFS::load() { 81 | // uint16_t iTl = iToken.length(); 82 | 83 | if (!iActive) { 84 | return PARAMS_ACT; 85 | } 86 | return JSONConfig.parse(iFile, iDict); 87 | } 88 | 89 | 90 | int8_t ParametersSPIFFS::save() { 91 | if (!iActive) { 92 | return PARAMS_ACT; 93 | } 94 | 95 | File f = SPIFFS.open(iFile, "w"); 96 | if ( !f ) { 97 | return PARAMS_FER; 98 | } 99 | 100 | f.print(iDict.json()); 101 | // int l = iDict.count(); 102 | // f.print('{'); 103 | // for (int i = 0; i < l; i++) { 104 | // f.print('\"'); 105 | // f.print(iDict(i)); 106 | // f.print("\":\""); 107 | // f.print(iDict[i]); 108 | // if ( i < l - 1 ) f.print("\", "); 109 | // } 110 | // f.print("\"}"); 111 | f.close(); 112 | 113 | return PARAMS_OK; 114 | } 115 | 116 | 117 | void ParametersSPIFFS::clear () { 118 | SPIFFS.remove(iFile); 119 | } 120 | 121 | #endif // _PARAMETERSSPIFFS_H_ --------------------------------------------------------------------------------