├── .gitignore ├── CHANGES.txt ├── LICENSE ├── README.md ├── docs ├── config.png └── config_colapsed.png ├── examples ├── ConfigAssist-Dynamic │ └── ConfigAssist-Dynamic.ino ├── ConfigAssist-ESP32-ESP8266 │ └── ConfigAssist-ESP32-ESP8266.ino ├── ConfigAssist-FirmwareCheck │ ├── ConfigAssist-FirmwareCheck.ino │ ├── README.txt │ ├── firmCheckESP32PMem.h │ ├── firmCheckESP8266PMem.h │ └── firmware │ │ ├── ESP32 │ │ ├── FirmwareCheck1.0.1.bin │ │ └── lastest.json │ │ └── ESP8266 │ │ ├── FirmwareCheck1.0.1.bin │ │ └── lastest.json ├── ConfigAssist-LargeTextArea │ ├── ConfigAssist-LargeTextArea.ino │ └── appPMem.h ├── ConfigAssist-LogExternal │ └── ConfigAssist-LogExternal.ino ├── ConfigAssist-LogToFile │ └── ConfigAssist-LogToFile.ino ├── ConfigAssist-Minimal │ └── ConfigAssist-Minimal.ino ├── ConfigAssist-NtpTimeSync │ ├── ConfigAssist-NtpTimeSync.ino │ └── appPMem.h ├── ConfigAssist-Simple │ └── ConfigAssist-Simple.ino ├── ConfigAssist-Test-Wifi │ └── ConfigAssist-Test-Wifi.ino ├── ConfigAssist-VarAttributes │ ├── ConfigAssist-VarAttrtibutes.ino │ └── appPMem.h └── ConfigAssist-WiFiAsync │ └── ConfigAssist-WiFiAsync.ino ├── keywords.txt ├── library.json ├── library.properties └── src ├── ConfigAssist.cpp ├── ConfigAssist.h ├── ConfigAssistHelper.cpp ├── ConfigAssistHelper.h ├── configAssistPMem.h ├── dYaml.cpp ├── dYaml.h └── espLogger.h /.gitignore: -------------------------------------------------------------------------------- 1 | /debug.cfg 2 | /debug_custom.json 3 | /esp32.svd 4 | .pio/ 5 | partitions/ 6 | test/ 7 | .vscode/ 8 | /platformio.ini 9 | -------------------------------------------------------------------------------- /CHANGES.txt: -------------------------------------------------------------------------------- 1 | 2.8.7 (2024-12-17) 2 | * Added connection failover 3 | * Fixed callback with no async connection 4 | * Fix start storage called once 5 | 6 | 2.8.6 (2024-12-02) 7 | * Added WiFi async connection with WiFiResultCallback. Added example 8 | * Check connection and auto reconnect on failure 9 | * Added forced and async time synchronization 10 | 11 | 2.8.5 (2024-11-20) 12 | * Added force time synchronization. 13 | * Added read operator () and asign [] operators 14 | 15 | 2.8.4 (2024-10-19) 16 | * Load default dictionary only when setup AP 17 | * Fixed retain connections 18 | 19 | 2.8.3 (2024-04-23) 20 | * Generate config files on first run with default values. 21 | * Stream html controls to handle big text areas. 22 | 23 | 2.8.2 (2024-02-14) 24 | * Added attributes to variables (javascript, style, etc.). 25 | * Added a function to append a javascript to main page 26 | 27 | 2.8.1 (2024-02-01) 28 | * Removed regex to reduce size. 29 | 30 | 2.8.0 (2024-01-25) 31 | * Added accordion open/closed button. Fixed errors 32 | 33 | 2.7.9 (2024-01-12) 34 | * Removed Json. Fixed yaml errors 35 | 36 | 2.7.8 (2024-01-04) 37 | * Moved to yaml for definitions. 38 | 39 | 2.7.7 (2023-12-21) 40 | * Added callback function on portal updates. 41 | 42 | 2.7.6 (2023-12-15) 43 | * Added ntp time synchronization 44 | 45 | 2.7.5 (2023-11-07) 46 | * Added didplay types for seperators. Fixed errors. 47 | 48 | 2.7.3 (2023-10-24) 49 | * Added headers inside class 50 | 51 | 2.7.2 (2023-10-15) 52 | * Firmware updates over the internet. 53 | 54 | 2.7.1 (2023-10-10) 55 | * Dump to web server 56 | 57 | 2.7.0 (2023-10-10) 58 | * Enable saving wifi credentials to nvs 59 | 60 | 2.6.9 (2023-10-10) 61 | * Changed constructors, Improved log functions. Hide passwords in browser. 62 | 63 | 2.6.8 (2023-10-5) 64 | * Disable wifi-scan.time-sync,ota-update to save getMessageHtml 65 | 66 | 2.6.7 (2023-09-29) 67 | * Support OTA firmware updates with progress bar 68 | 69 | 2.6.6 (2023-09-09) 70 | * Backup and restore configuration 71 | 72 | 2.6.5 (2023-07-19) 73 | * Added set static ip to ConfigAssist-Test-Wifi.ino 74 | * Add a not found key to ini file if defined in json. 75 | 76 | 2.6.4 (2023-07-08) 77 | * Reboot confirmation using timestamp 78 | 79 | 2.6.3 (2023-06-21) 80 | * Test multiple station network connections from AP portal and show ip and signal strength 81 | after connection 82 | 83 | 2.6.2 (2023-05-15) 84 | * Allow application to logging into a file with logLevel 85 | 86 | 2.6.1 (2023-05-11) 87 | * Added function getCSS() to get ConfigAssist css on application main page 88 | * Added function getTimeSyncScript() to get ConfigAssist time sync script 89 | * Added function getMessageHtml() to get ConfigAssist simple message page 90 | 91 | 2.6.0 (2023-05-10) 92 | * Created ConfigAssist library 93 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 gemi254 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ConfigAssist 2 | 3 | A lightweight library allowing quick configuration of **esp32/esp8266** devices. **Define** application variables using a yaml text and **edit** them with a **responsive** configuration portal. Variables are updated instantly using **async get requests** and saved automatically on a ini file in local storage. 4 | 5 | ## Description 6 | 7 | **ConfigAssist** is a configuration management system for ESP-based devices (ESP32/ESP8266). It provides functionality to manage configuration files, web server operations, and handle user input on the configuration page. The class is versatile and includes support for managing network settings, reading and writing configuration files, and serving HTML forms over a web interface. 8 | 9 | **ConfigAssist** will help to automate definition of variables used in a typical **esp32/esp8266** application. It will automatically generate a web portal with edit html elements for each variable based on **yaml definition** text, allowing quick editing is value from a html page. 10 | 11 | Application variables like **Wifi ssid**, **Wifi password**, **Host Name** can be quick edited there and will be ready for use in your application. Every time a variable is changed in the web page, it will automatically updated by ConfigAssist using an **async** JavaScript request. As the user leaves or closes the page at end the data are auto saved to the ini file. 12 | 13 | ## Features 14 | 15 | * Automate **variables** definition in a typical ``ESP32/ESP8266`` project using a yaml definition description. 16 | * Configuration **portal** with various **html controls** for editing these variables from a ``web browser``. 17 | * Automatically generate an **ini** file in internal storage and auto save. 18 | * On the fly **update** values in ini file using **ajax** requests. 19 | * **Backup & Restore** device configurations. 20 | * **Wi-Fi scan** support for auto fill nearby Wi-Fi station connections. 21 | * Save **WiFi credentials** to **nvs** to retain ST connections. 22 | * **Validate** Wi-Fi station connections when connecting from AP. 23 | * Auto **synchronize** ESP32/ESP8266 internal **clock** with browser clock. 24 | * Support on the fly **firmware upgrades** (OTA). 25 | * Support automatic firmware **upgrades** over the **internet**. 26 | 27 |

28 | 29 |

30 | 31 | 32 | ## Features description 33 | 34 | **ConfigAssist** can perform a **Wifi Scan** on setup and attach a **drop down list** on the field **st_ssid** with nearby available wifi **access points**. The list will be sorted by **signal strength**, with the strongest signals to be placed first and will be refreshed every 15 seconds. Users can choose a valid nearby **ssid** from the wifi list. 35 | 36 | **Station** Wifi **connections** can be **validated** during setup with ``Test connection`` link available on each **st_ssid** field. The device will be switched to **WIFI_AP_STA** and **ConfigAssist** will try to test the Station connection with **Wifi ssid**, **Wifi password** entered without disconnecting from the Access Point. If the connection is successful the Station ip address and signal strength will be displayed. 37 | 38 | **WiFi credentials** can also be saved to **nvs** to retain ST connections. On **factory defaults** the nvs will be not cleared and the ST connection will be still available. 39 | You can use the command ``http://ip/cfg?_CLEAR=1`` to clear nvs. 40 | Set **CA_USE_PERSIST_CON** to 1 in ``ConfigAssist.h`` to use this feature. 41 | 42 | **ConfigAssist** can also check and synchronize the internal **clock** of ESP device with the browser time if needed. So even if no internet connection (AP mode) and no **npt** server is available the device will get the correct time. If **CA_TIMEZONE_KEY** string exists in variables it will be used to set the device time zone string. If not it will use browser offset. 43 | 44 | **ConfigAssist** can add web based **OTA** updates to your ESP32/ESP8266 projects. With the button **Upgrade** you can upload a firmware file (*.bin) from you pc and perform a **firmware upgrade** to the device. 45 | 46 | **ConfigAssist** can also pefrom **Firmware upgrades** over **internet**. It will compare the currnet firmware version of the device with a remote firmware stored in a **web site** location. If there is a new firmware it will automatically download it and pefrom the upgrade. 47 | 48 | Check the FirmwareCheck example for more details. 49 | 50 | These features can be disabled to save memory by setting 1 / 0 the lines **CA_USE_WIFISCAN**, **CA_USE_TESTWIFI**, **CA_USE_TIMESYNC**, **CA_USE_OTAUPLOAD**, and **CA_USE_FIMRMCHECK** in ``ConfigAssist.h``. 51 | 52 | You can also use compiler flags to enable / disable theese features 53 | ``` 54 | build_flags = -DCA_USE_LITTLEFS ; Use littlefs remove for spiffs 55 | -DLOGGER_LOG_LEVEL=5 56 | -DCA_USE_WIFISCAN=1 57 | -DCA_USE_TESTWIFI=1 58 | -DCA_USE_TIMESYNC=1 59 | -DCA_USE_OTAUPLOAD=1 60 | -DCA_USE_FIMRMCHECK=1 61 | ``` 62 | Device's configuration ``(*.ini files)`` can be downloaded with the **Backup** button and can be restored later with the **Restore** button. 63 | 64 | You can use the command ``http://ip/cfg?_RST=1`` to manually clear the ini file and load defaults. 65 | 66 | ## Configuration variables 67 | 68 | Variables **descriptions** and **default values** are based on a **text description** in yaml format that acts as a template defining the **type**, **label** and extra **info** of each variable. 69 | 70 | All config variables must be defined there describing the **variable type**, **default value** and the **label** that will be displayed to the user. It can also include and some **attributes** in case of special variables like **datalist** for list box, min, max, step for **input number** etc. 71 | ``` 72 | Wifi settings: 73 | - st_ssid: 74 | label: Name for WLAN 75 | default: '' 76 | ``` 77 | A simple html page will be generated by **ConfigAssist** allowing quick editing these variables from a web Browser with a connection to the device. While editing the variables they will be **instantly** available to the application and will be automatically saved. 78 | 79 | Application variables can be used in code with operator **()** i.e. ```conf('variable')```. The configuration data will be stored in the **SPIFFS** as an **ini file** (Plain Text) and will be automatically loaded on each reboot. 80 | 81 | ## How it works 82 | 83 | On first run when no data (**ini file**) is present in local storage, **ConfigAssist** will start an **Access Point** and load the default yaml dictionary with variable definitions that will be edited. It will then generate an html page with html controls allowing data to be edited from the connected devices. 84 | 85 | On each **change** in the html page data will be **updated** and will be available immediately to the application. The data will be saved automatically on local storage when the user finishes editing. If the configuration file is valid during next boot, **yaml dictionary** will not be loaded reducing memory consumption and speeding up the whole process. Note that if data is not changed **ConfigAssist** will not re-save the file. 86 | 87 | ConfigAssist uses **c++ vectors** to dynamically allocate and store variables and **binary search** for speeding the access process. 88 | 89 | ## How to use variables 90 | 91 | **ConfigAssist** consists of single file "ConfigAssist.h" that must be included in your application 92 | The application variables can be used directly by accessing the **class** itself by operator **())** 93 | i.e. 94 | 95 | + `String ssid = conf("st_ssid");` 96 | + `bool debug = conf("debug").toInt();` 97 | + `int pinNo = conf("st_ssid").toInt();` 98 | + `digitalWrite(conf("led_buildin").toInt(), 0)`; 99 | + `float float_value = atof(conf("float_value").c_str());` 100 | 101 | Also values can be assigned dynamically with operator **[]** 102 | 103 | + `conf["st_ssid"] = "TestWifi";` 104 | + `conf["debug"] = 1;` 105 | + `conf["float_value"] = 12.3;` 106 | 107 | 108 | ## Variables definition with YAML dictionary 109 | 110 | In your application sketch file you must define a yaml dictionary that includes all the information needed for the html edit form to be generated. Each variable will be displayed on edit page with the order defined in the yaml file. See example below... 111 | 112 | 113 | + If you use keywords `name, default` an **edit box** will be generated to edit the variable. Add `attribs` keywords to specify min, max, step for a numeric field. 114 | + If you keyword name ends with ``_pass`` (or ``_pass`` and a number i.e``_pass1``) a **password field** will be used. See **CA_ST_PASSWD_KEY** definition. 115 | + If you use keyword `checked` instead of `default` a Boolean value will be used that will be edited by a **check box** 116 | + You can combine keywords `default` with `options` in order to use a select list that will be edited by a **drop list**. 117 | - The `options` field must contain a comma separated list of values and can be enclosed by single quotes. 118 | - The `options` field can contains **option names**, **values pairs** delimited with ':' for example 'AllOpen':'0', 'AllClosed':'1' etc. 119 | 120 | + You can combine keywords `default` with `range` in order to use a value that will be edited by a **input range**. 121 | - The `range` field must contain a comma separated list of `min, max, step` and can be enclosed by single quotes. 122 | + You can combine keywords `default` with `datalist` in order to use a value that will be edited by a **combo box**. 123 | - The `datalist` field must contain a comma or line feed separated list of default values for drop down list. 124 | + You can combine keywords `default` with `file` in order to use a small text be edited by a **text area**. 125 | - The `file` field must contain a valid file path that the text will be saved to. The `default` keyword can also be used to define a default value. 126 | 127 | + All vars can have the keyword `attribs` to specify special attributes like javaScript, style for this field. 128 | - For example `attribs: onChange = "this.style.color = 'red'"; ` 129 | - You can use `setSubScript` function to add an init java script to main page. 130 | 131 | Check ConfigAssist-VarAttributes in ``examples/`` folder for more details. 132 | 133 | 134 | A **separator title** can also be used to group configuration values under a specific title. 135 |

136 | 137 |

138 | 139 | Seperators can be opened and closed when click on their title. You can define the initial state by using **setDisplayType** function. 140 | 141 | Use **AllOpen** to display opened tabs, **AllClosed** to display seperator titles only and **Accordion** to close all but not current tab. 142 | ``` 143 | // Set the display type 144 | conf.setDisplayType(ConfigAssistDisplayType::Accordion); 145 | ``` 146 | 147 | Version 2.7.8 now uses yaml instead of json descriptions to save space and increase speed 148 | See direct-yaml for details. 149 | 150 | If you want to convert your existing configuration from JSON to YAML 151 | download version 152 | 153 | ConfigAssist v2.7.8 154 | 155 | comment at ConfigAssist.h line 7 156 | ``` 157 | #define CA_USE_YAML // Comment to use JSON 158 | ``` 159 | Compile the JSON version and add a yaml dump handler. 160 | ``` 161 | server.on("/yaml", []() { // Append yaml dump handler 162 | conf.dumpYaml(&server); 163 | }); 164 | ``` 165 | Upload and visit ``http://ip/yaml`` to copy your configuration in yaml format. 166 | 167 | ## Sample variables definition text 168 | ``` 169 | const char* VARIABLES_DEF_YAML PROGMEM = R"~( 170 | Wifi settings: 171 | - st_ssid: 172 | label: Name for WLAN 173 | default: '' 174 | - st_pass: 175 | label: Password for WLAN 176 | default: '' 177 | - host_name: 178 | label: >- 179 | Host name to use for MDNS and AP
{mac} will be replaced with device's mac 180 | id 181 | default: configAssist_{mac} 182 | 183 | Application settings: 184 | - app_name: 185 | label: Name your application 186 | default: ConfigAssistDemo 187 | - led_buildin: 188 | label: Enter the pin that the build in led is connected. Leave blank for auto. 189 | attribs: "min='2' max='23' step='1'" 190 | default: 2 191 | 192 | ConfigAssist settings: 193 | - display_style: 194 | label: Choose how the config sections are displayed. Must reboot to apply 195 | options: 196 | - AllOpen: 0 197 | - AllClosed: 1 198 | - Accordion : 2 199 | - AccordionToggleClosed : 3 200 | default: AccordionToggleClosed 201 | 202 | Other settings: 203 | - float_val: 204 | label: Enter a float val 205 | default: 3.14159 206 | attribs: min="2.0" max="5" step=".001" 207 | - debug: 208 | label: Check to enable debug 209 | checked: False 210 | - sensor_type: 211 | label: Enter the sensor type 212 | options: 'BMP280', 'DHT12', 'DHT21', 'DHT22' 213 | default: DHT22 214 | - refresh_rate: 215 | label: Enter the sensor update refresh rate 216 | range: 10, 50, 1 217 | default: 30 218 | - time_zone: 219 | label: Needs to be a valid time zone string 220 | default: EET-2EEST,M3.5.0/3,M10.5.0/4 221 | datalist: 222 | - Etc/GMT,GMT0 223 | - Etc/GMT-0,GMT0 224 | - Etc/GMT-1,<+01>-1 225 | - cal_data: 226 | label: Enter data for 2 Point calibration.
Data will be saved to /calibration.ini 227 | file: "/calibration.ini" 228 | default: 229 | X1=222, Y1=1.22 230 | X2=900, Y2=3.24 231 | )~"; 232 | ``` 233 | 234 | ## Project definitions in your main app 235 | 236 | + include the **ConfigAssist** class 237 | - `#include //ConfigAssist class` 238 | 239 | + Define your static instance with **defaults** 240 | - `ConfigAssist conf;` 241 | 242 | + if you want to use a different external **ini file name** 243 | - `ConfigAssist conf(INI_FILE);` 244 | 245 | + if you want to use a different external **ini file name** and **yaml description** 246 | - `ConfigAssist conf(INI_FILE, VARIABLES_DEF_YAML); // ConfigAssist with custom name & dictionary` 247 | 248 | + if you want to use a different external **ini file name** and **yaml description** disabled 249 | - `ConfigAssist conf(INI_FILE, NULL); // ConfigAssist with custom ini name & dictionary disabled` 250 | 251 | ## Call back functions 252 | You can define a call back function wich will be called when the portal changes a value. 253 | This function will be called each time a value is changed from the portal with the value name as a parameter. 254 | ``` 255 | 256 | // Will be called when portal is updating a key 257 | void onDataChanged(String key){ 258 | LOG_I("Data changed, key:%s, val: %s \n", key.c_str(), conf(key).c_str()); 259 | } 260 | 261 | // Setup call back function 262 | conf.setRemotUpdateCallback(onDataChanged); 263 | 264 | ``` 265 | 266 | ## WIFI Access point handlers 267 | 268 | **ConfigAssist** must be initialized with a pointer to a web server to automatically handle AP form requests. 269 | Setup will add web handlers /cfg, /scan, to the server and if apEnable = true will enable Access Point. 270 | ``` 271 | conf.setup(server, /*Start AP*/ true); 272 | or 273 | conf.setupConfigPortal(server, true); 274 | ``` 275 | You can add /cfg handler to your application after connecting the device to the internet. 276 | Editing config will be enabled for station users. 277 | 278 | ``` 279 | // ConfigAssist will register handlers to the web server 280 | // so config can be edited. 281 | conf.setupConfigPortalHandlers(server); 282 | ``` 283 | ## Connect the WiFi using **ConfigAsistHelper** 284 | In order to simplify the proccess of connect to WiFi and set static ip address use the **ConfigAsistHelper class**. It will connect the WiFi using credentials contained in a ConfigAssist class. It will search for variables ending with **CA_ST_SSID_KEY** for ssid and **CA_ST_PASSWD_KEY** for passwords. 285 | For example variables like.. 286 | ``` 287 | st_ssid, st_pass, 288 | station_ssid, station_pass 289 | ``` 290 | It can also use variables like.. 291 | ``` 292 | st_ssid1, st_pass1, 293 | st_ssid2, st_pass2 294 | ``` 295 | for failover connections. 296 | 297 | If a variable found ending with **CA_ST_STATICIP_KEY** it will automatically set static ip. 298 | 299 | To use **ConfigAsistHelper** 300 | ``` 301 | // Define a ConfigAssist helper class with a ConfigAssist conf 302 | // containig credentials 303 | ConfigAssistHelper confHelper(conf); 304 | ``` 305 | 306 | ``` 307 | // Connect to any available network 308 | // Wait for connection or TimeOut 309 | bool bConn = confHelper.connectToNetwork(15000 /*Timeout ms*/, conf("led_buildin").toInt() /*Key containig internal led*/); 310 | ``` 311 | 312 | ``` 313 | // Connect to any available network async 314 | // It will not wait to connect but will send a connection result 315 | // Set Wi-Fi credentials from config and start connection process 316 | confHelper.connectToNetworkAsync(15000, 13, [](ConfigAssistHelper::WiFiResult result, const String& msg) { 317 | switch (result) { 318 | case ConfigAssistHelper::WiFiResult::SUCCESS: 319 | LOG_D("Connected to Wi-Fi! IP: %s\n", msg.c_str()); 320 | break; 321 | 322 | case ConfigAssistHelper::WiFiResult::INVALID_CREDENTIALS: 323 | LOG_D("Invalid credentials: %s\n", msg.c_str()); 324 | break; 325 | 326 | case ConfigAssistHelper::WiFiResult::CONNECTION_TIMEOUT: 327 | LOG_D("Connection fail: %s\n", msg.c_str()); 328 | break; 329 | 330 | case ConfigAssistHelper::WiFiResult::DISCONNECTION_ERROR: 331 | LOG_D("Disconnect: %s\n", msg.c_str()); 332 | break; 333 | 334 | default: 335 | LOG_D("Unknown result: %s\n", msg.c_str()); 336 | break; 337 | } 338 | }); 339 | ``` 340 | 341 | ## Synchronize time using **ConfigAsistHelper** 342 | ConfigAssist can synchronize device's time with a ntp server using **ConfigAsistHelper**. 343 | Variables ending with **CA_NTPSYNC_KEY** for example **ntp_server1**, **ntp_server2**, **ntp_server3** 344 | will be used as ntp servers. 345 | The current timezone for the device will be set automatically using **CA_TIMEZONE_KEY** for example **time_zone** 346 | 347 | Just define **ntp_server1**, **ntp_server2**, **ntp_server3** and **time_zone** settings in your config and call **syncTime** with timeout ms to wait until time is synchronized. Use force = true to reset clock and wait the time to be synchronized. 348 | ``` 349 | - ntp_server1: 350 | label: Time server to sync time1 351 | default: "europe.pool.ntp.org" 352 | - ntp_server2: 353 | label: Time server to sync time2 354 | default: "time.windows.com" 355 | - ntp_server3: 356 | label: Time server to sync time3 357 | default: "pool.ntp.org" 358 | 359 | // Setup time synchronization 360 | // Wait max 10 sec for sync 361 | confHelper.syncTime(10000); 362 | 363 | // Force time synchronization, 364 | // Clock will be reseted and wait for max 20 sec to sync 365 | // If fail clock will be restored 366 | confHelper.syncTime(20000, true); 367 | or 368 | // Sync time, force to reset clock, async to not wait for sync 369 | // In asynchronous mode, if time synchronization fails, no restoration of the clock occurs. 370 | confHelper.syncTimeAsync(20000, true); 371 | 372 | ``` 373 | 374 | ## Setup function 375 | ``` 376 | void setup() 377 | 378 | // Setup led on empty string 379 | if(conf("led_buildin")=="") conf.put("led_buildin", LED_BUILTIN); 380 | // Or 381 | if(conf("led_buildin")=="") conf["led_buildin"] = LED_BUILTIN; 382 | 383 | // Connect to any available network with timeout 1500 and led pin on led_buildin variable 384 | bool bConn = confHelper.connectToNetwork(15000, conf("led_buildin").toInt()); 385 | 386 | // Append config assist handlers to web server, setup ap on no connection 387 | conf.setup(server, !bConn); 388 | if(!bConn) LOG_E("Connect failed.\n"); 389 | 390 | ``` 391 | 392 | ## Ini files 393 | **ConfigAssist** can also used to quick generate and store ini files. 394 | Just call the class constructor with a filename to be saved and null to disable dictionary. 395 | ``` 396 | ConfigAssist info("/info.ini", NULL); 397 | ``` 398 | and add the parameters to be stored with 399 | ``` 400 | info.put("var1", "test1", true); 401 | or 402 | info["var1"] = "test1"; 403 | 404 | info.saveConfigFile(); 405 | ``` 406 | 407 | ## Logging to a file 408 | **ConfigAssist** can redirect serial print functions to a file in a spiffs and a **Debug log** can be generated. 409 | In your application you use **LOG_E**, **LOG_W**, **LOG_I**, **LOG_D** macros instead of **Serial.prinf** 410 | to print your messages. **ConfigAssist** can record these messages with **timestamps** to a file. 411 | 412 | + define **ConfigAssist** log mode 413 | Define the log level (Error = 1 .. Verbose = 5) 414 | ``` 415 | #define LOGGER_LOG_LEVEL 5 // Errors & Warnings & Info & Debug & Verbose 416 | ``` 417 | 418 | + if you want to enable serial print to a log file use.. 419 | ``` 420 | //Enable ConfigAssist logging to file 421 | #define LOGGER_LOG_MODE 2 // Log to file 422 | 423 | //Define the log filename 424 | #define LOGGER_LOG_FILENAME "/log1" 425 | ``` 426 | 427 | + You can also use your own log_printf function by setting log mode to 3 428 | ``` 429 | #define LOGGER_LOG_MODE 3 // External log_printf function 430 | ``` 431 | 432 | Check ConfigAssist-LogExternal in ``examples/`` folder. 433 | 434 | ## Compile 435 | Download library files and place them on ./libraries directory under ArduinoProjects 436 | Then include the **ConfigAssist.h** in your application and compile.. 437 | 438 | + compile for arduino-esp3 or arduino-esp8266. 439 | + To use Persistent ST connections On ESP8266 devices you must install **Preferences** library to provide ESP32-compatible Preferences API using LittleFS 440 | + if your variables exceed **CA_MAX_PARAMS** increase this value in class header. 441 | 442 | You can remove old **ini** config file by calling `conf.deleteConfig();` in your setup function. 443 | See **ConfigAssist-ESP32-ESP8266.ino** line:210 444 | 445 | ###### If you get compilation errors on arduino-esp32 you need to update your arduino-esp32 library in the IDE using Boards Manager 446 | 447 | ## Other examples 448 | You can see an advanced example of **ConfigAssist** usage on **PlantStatus** a plant monitoring and logging application 449 | -------------------------------------------------------------------------------- /docs/config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gemi254/ConfigAssist-ESP32-ESP8266/f7830b0e43629fceadb76a4bd6e2f7e38cb363a1/docs/config.png -------------------------------------------------------------------------------- /docs/config_colapsed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gemi254/ConfigAssist-ESP32-ESP8266/f7830b0e43629fceadb76a4bd6e2f7e38cb363a1/docs/config_colapsed.png -------------------------------------------------------------------------------- /examples/ConfigAssist-Dynamic/ConfigAssist-Dynamic.ino: -------------------------------------------------------------------------------- 1 | #include // Config assist class definition 2 | 3 | // Create a config class with an ini filename for storage 4 | //Start storage and load config 5 | ConfigAssist config("/info1.ini"); 6 | bool reset = false; 7 | void setup() { 8 | // put your setup code here, to run once: 9 | 10 | Serial.begin(115200); 11 | Serial.print("\n\n\n\n"); 12 | Serial.flush(); 13 | 14 | LOG_I("Starting..\n"); 15 | 16 | if(reset){ 17 | config.deleteConfig(); // Remove ini file and re-built 18 | config.clear(); // Clear loaded keys 19 | } 20 | // Dict is disabled, Ini file not found 21 | if(!config.confExists()){ 22 | String textDict="YAML DICT: \n"; 23 | for(int i=0; i<10; ++i){ 24 | textDict += " - var_"+ String(i) +":\n"; 25 | textDict += " default: "+ String(i) +"\n"; 26 | } 27 | LOG_I("Generated yaml:\n %s\n",textDict.c_str()); 28 | 29 | // Build ini file from textDict 30 | config.setDictStr(textDict.c_str(), true); 31 | LOG_I("Config valid: %i\n",config.valid()); 32 | // Save keys & values into ini file 33 | config.saveConfigFile(); 34 | config.dump(); 35 | }else{ // Ini file is valid, display the values 36 | config.dump(); 37 | } 38 | } 39 | 40 | void loop() { 41 | // put your main code here, to run repeatedly: 42 | } 43 | -------------------------------------------------------------------------------- /examples/ConfigAssist-ESP32-ESP8266/ConfigAssist-ESP32-ESP8266.ino: -------------------------------------------------------------------------------- 1 | #include // Config assist class 2 | #include // Config assist helper class 3 | 4 | #if defined(ESP32) 5 | WebServer server(80); 6 | #else 7 | ESP8266WebServer server(80); 8 | #endif 9 | 10 | #ifndef LED_BUILTIN 11 | #define LED_BUILTIN 22 12 | #endif 13 | 14 | #define INI_FILE "/ConfigAssistDemo.ini" // Define SPIFFS storage file 15 | 16 | unsigned long pingMillis = millis(); // Ping 17 | 18 | // Default application config dictionary 19 | // Modify the file with the params for you application 20 | // Then you can use then then by val = config[name]; 21 | const char* VARIABLES_DEF_YAML PROGMEM = R"~( 22 | Wifi settings: 23 | - st_ssid: 24 | label: Name for WLAN 25 | - st_pass: 26 | label: Password for WLAN 27 | - host_name: 28 | label: Host name to use for MDNS and AP 29 | {mac} will be replaced with device's mac id 30 | default: configAssist_{mac} 31 | 32 | Application settings: 33 | - app_name: 34 | label: Name your application 35 | default: ConfigAssistDemo 36 | - led_buildin: 37 | label: Enter the pin that the build in led is connected. 38 | Leave blank for auto. 39 | attribs: "min='2' max='23' step='1'" 40 | 41 | ConfigAssist settings: 42 | - display_style: 43 | label: Choose how the config sections are displayed. 44 | Must reboot to apply 45 | options: 46 | - AllOpen: 0 47 | - AllClosed: 1 48 | - Accordion : 2 49 | - AccordionToggleClosed : 3 50 | default: AccordionToggleClosed 51 | - work_mode: 52 | label: Application Work mode. Must reboot to apply 53 | options: "'MeasureUpload': '0' ,'MeasureBatchUpload':'1', 'MeasureUploadSleep':'2', 'MeasureBatchUploadSleep':'3', 'MeasureTimeoutUploadSleep':'4'" 54 | default: 2 55 | 56 | Other settings: 57 | - float_val: 58 | label: Enter a float val 59 | default: 3.14159 60 | attribs: min="2.0" max="5" step=".001" 61 | - debug: 62 | label: Check to enable debug 63 | checked: False 64 | - sensor_type: 65 | label: Enter the sensor type 66 | options: 'BMP280', 'DHT12', 'DHT21', 'DHT22' 67 | default: DHT22 68 | - refresh_rate: 69 | label: Enter the sensor update refresh rate 70 | range: 10, 50, 1 71 | default: 30 72 | - time_zone: 73 | label: Needs to be a valid time zone string 74 | default: EET-2EEST,M3.5.0/3,M10.5.0/4 75 | datalist: 76 | - Etc/GMT,GMT0 77 | - Etc/GMT-0,GMT0 78 | - Etc/GMT-1,<+01>-1 79 | - Etc/GMT-2,<+02>-2 80 | - Etc/GMT-3,<+03>-3 81 | - Etc/GMT-4,<+04>-4 82 | - Etc/GMT-5,<+05>-5 83 | - Etc/GMT-6,<+06>-6 84 | - Etc/GMT-7,<+07>-7 85 | - Etc/GMT-8,<+08>-8 86 | - Etc/GMT-9,<+09>-9 87 | - Etc/GMT-10,<+10>-10 88 | - Etc/GMT-11,<+11>-11 89 | - Etc/GMT-12,<+12>-12 90 | - Etc/GMT-13,<+13>-13 91 | - Etc/GMT-14,<+14>-14 92 | - Etc/GMT0,GMT0 93 | - Etc/GMT+0,GMT0 94 | - Etc/GMT+1,<-01>1 95 | - Etc/GMT+2,<-02>2 96 | - Etc/GMT+3,<-03>3 97 | - Etc/GMT+4,<-04>4 98 | - Etc/GMT+5,<-05>5 99 | - Etc/GMT+6,<-06>6 100 | - Etc/GMT+7,<-07>7 101 | - Etc/GMT+8,<-08>8 102 | - Etc/GMT+9,<-09>9 103 | - Etc/GMT+10,<-10>10 104 | - Etc/GMT+11,<-11>11 105 | - Etc/GMT+12,<-12>12 106 | - cal_data: 107 | label: Enter data for 2 Point calibration. 108 | Data will be saved to /calibration.ini 109 | file: "/calibration.ini" 110 | default: 111 | X1=222, Y1=1.22 112 | X2=900, Y2=3.24 113 | )~"; 114 | 115 | ConfigAssist conf(INI_FILE, VARIABLES_DEF_YAML); // Config assist class 116 | ConfigAssistHelper confHelper(conf); // Define a ConfigAssist helper for managing connection 117 | // Setup led 118 | String s = (conf("led_buildin") == "") ? conf["led_buildin"] = LED_BUILTIN : ""; 119 | 120 | // Print memory info 121 | void debugMemory(const char* caller) { 122 | #if defined(ESP32) 123 | LOG_I("%s > Free: heap %u, block: %u, pSRAM %u\n", caller, ESP.getFreeHeap(), heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL), ESP.getFreePsram()); 124 | #else 125 | LOG_I("%s > Free: heap %u\n", caller, ESP.getFreeHeap()); 126 | #endif 127 | } 128 | 129 | // List the storage file system 130 | void ListDir(const char * dirname) { 131 | LOG_I("Listing directory: %s\n", dirname); 132 | // List details of files on file system 133 | File root = STORAGE.open(dirname, "r"); 134 | File file = root.openNextFile(); 135 | while (file) { 136 | #if defined(ESP32) 137 | LOG_I("File: %s, size: %u B\n", file.path(), file.size()); 138 | #else 139 | LOG_I("File: %s, size: %u B\n", file.fullName(), file.size()); 140 | #endif 141 | file = root.openNextFile(); 142 | } 143 | Serial.println(""); 144 | } 145 | 146 | // Handler function for Home page 147 | void handleRoot() { 148 | String out("

Hello from {name}

"); 149 | out += "

Device time: " + conf.getLocalTime() +"

"; 150 | out += "Edit config"; 151 | conf["refresh_rate"] = conf("refresh_rate").toInt() + 1; 152 | #if defined(ESP32) 153 | out.replace("{name}", "ESP32"); 154 | #else 155 | out.replace("{name}", "ESP8266!"); 156 | #endif 157 | #if (CA_USE_TIMESYNC) 158 | out += ""; 159 | #endif 160 | server.send(200, "text/html", out); 161 | } 162 | 163 | // Handler for page not found 164 | void handleNotFound() { 165 | String message = "File Not Found\n\n"; 166 | message += "URI: "; 167 | message += server.uri(); 168 | message += "\nMethod: "; 169 | message += (server.method() == HTTP_GET) ? "GET" : "POST"; 170 | message += "\nArguments: "; 171 | message += server.args(); 172 | message += "\n"; 173 | for (uint8_t i = 0; i < server.args(); i++) { 174 | message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; 175 | } 176 | server.send(404, "text/plain", message); 177 | } 178 | 179 | // CallBack function when conf remotely changes 180 | void onDataChanged(String key){ 181 | LOG_I("Data changed: %s = %s \n", key.c_str(), conf(key).c_str()); 182 | if(key == "display_style") 183 | conf.setDisplayType((ConfigAssistDisplayType)conf("display_style").toInt()); 184 | 185 | } 186 | // Setup function 187 | void setup(void) { 188 | Serial.begin(115200); 189 | Serial.print("\n\n\n\n"); 190 | Serial.flush(); 191 | 192 | LOG_I("Starting..\n"); 193 | debugMemory("setup"); 194 | 195 | //ListDir("/"); 196 | 197 | //conf.deleteConfig(); // Uncomment to remove old ini file and re-built it fron dictionary 198 | 199 | // Register handlers for web server 200 | server.on("/", handleRoot); // Add root handler 201 | server.on("/d", []() { // Append dump handler 202 | conf.dump(&server); 203 | }); 204 | 205 | server.onNotFound(handleNotFound); // Append not found handler 206 | 207 | debugMemory("Loaded config"); 208 | 209 | // Will be called when portal is updating a key 210 | conf.setRemotUpdateCallback(onDataChanged); 211 | 212 | WiFi.setAutoConnect(false); 213 | WiFi.setAutoReconnect(false); 214 | 215 | // Connect to any available network and connect or timeout 216 | bool bConn = confHelper.connectToNetwork(15000, conf("led_buildin").toInt()); 217 | 218 | // Append config assist handlers to web server, setup ap on no connection 219 | conf.setup(server, !bConn); 220 | if(!bConn) LOG_E("Connect failed.\n"); 221 | else confHelper.startMDNS(); 222 | 223 | // Get int/bool value 224 | bool debug = conf("debug").toInt(); 225 | LOG_I("Boolean value: %i\n", debug); 226 | 227 | // Get float value 228 | float float_value = conf("float_val").toFloat(); 229 | LOG_I("Float value: %1.5f\n", float_value); 230 | 231 | // Set the defined display type 232 | conf.setDisplayType((ConfigAssistDisplayType)conf("display_style").toInt()); 233 | 234 | server.begin(); 235 | LOG_I("HTTP server started, display type: %s\n", conf("display_style").c_str()); 236 | 237 | // On the fly generate an ini info file on SPIFFS 238 | { 239 | if(debug) STORAGE.remove("/info.ini"); 240 | ConfigAssist info("/info.ini"); 241 | // Add a key even if not exists. It will be not editable 242 | if(!info.valid()){ 243 | LOG_D("Info file not exists\n"); 244 | info["bootCnt"] = 1; 245 | info["lastRSSI"] = WiFi.RSSI(); 246 | //info.put("bootCnt", 1, true); 247 | //info.put("lastRSSI", WiFi.RSSI(), true); 248 | }else{ 249 | LOG_I("Info file: bootCnt: %s, lastRSSI: %s\n", info("bootCnt").c_str(), info("lastRSSI").c_str() ); 250 | info["bootCnt"] = info("bootCnt").toInt() + 1; 251 | info["lastRSSI"] = WiFi.RSSI(); 252 | //info.put("bootCnt", info["bootCnt"].toInt() + 1, true); 253 | //info.put("lastRSSI", WiFi.RSSI(), true); 254 | } 255 | info.saveConfigFile(); 256 | info.dump(); 257 | } 258 | } 259 | 260 | // App main loop 261 | void loop(void) { 262 | server.handleClient(); 263 | confHelper.loop(); 264 | 265 | // Display info 266 | if (millis() - pingMillis >= 10000){ 267 | // if debug is enabled in config display memory debug messages 268 | if(conf("debug").toInt()) debugMemory("Loop"); 269 | pingMillis = millis(); 270 | } 271 | 272 | // Allow the cpu to switch to other tasks 273 | delay(2); 274 | } 275 | -------------------------------------------------------------------------------- /examples/ConfigAssist-FirmwareCheck/ConfigAssist-FirmwareCheck.ino: -------------------------------------------------------------------------------- 1 | #include // Config assist class 2 | #include // Config assist helper class 3 | 4 | #if defined(ESP32) 5 | #include "firmCheckESP32PMem.h" 6 | WebServer server(80); 7 | #else 8 | #include "firmCheckESP8266PMem.h" 9 | ESP8266WebServer server(80); 10 | #endif 11 | 12 | #ifndef LED_BUILTIN 13 | #define LED_BUILTIN 22 14 | #endif 15 | #define APP_VER "1.1" // Application version 16 | #define INI_FILE "/FirmwareCheck.ini" // Define SPIFFS storage file 17 | 18 | // Config class 19 | ConfigAssist conf(INI_FILE, VARIABLES_DEF_YAML); 20 | // Define a ConfigAssist helper 21 | ConfigAssistHelper confHelper(conf); 22 | 23 | 24 | String hostName; // Default Host name 25 | 26 | // Handler function for Home page 27 | void handleRoot() { 28 | 29 | String out("

Hello from {name}

"); 30 | out += "

Device time: " + conf.getLocalTime() +"

"; 31 | out += "Edit config"; 32 | 33 | #if defined(ESP32) 34 | out.replace("{name}", "ESP32"); 35 | #else 36 | out.replace("{name}", "ESP8266!"); 37 | #endif 38 | #if (CA_USE_TIMESYNC) 39 | out += ""; 40 | #endif 41 | server.send(200, "text/html", out); 42 | } 43 | 44 | // Handler for page not found 45 | void handleNotFound() { 46 | String message = "File Not Found\n\n"; 47 | message += "URI: "; 48 | message += server.uri(); 49 | message += "\nMethod: "; 50 | message += (server.method() == HTTP_GET) ? "GET" : "POST"; 51 | message += "\nArguments: "; 52 | message += server.args(); 53 | message += "\n"; 54 | for (uint8_t i = 0; i < server.args(); i++) { 55 | message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; 56 | } 57 | server.send(404, "text/plain", message); 58 | } 59 | 60 | // Setup function 61 | void setup(void) { 62 | 63 | Serial.begin(115200); 64 | Serial.print("\n\n\n\n"); 65 | Serial.flush(); 66 | 67 | LOG_I("Starting.. ver: %s\n", APP_VER); 68 | LOG_I("WiFi mode: %i\n", (int)WiFi.getMode()); 69 | 70 | if(false) conf.deleteConfig(); // Set to true ro remove old ini file and re-built it fron dictionary 71 | 72 | // Register handlers for web server 73 | server.on("/", handleRoot); 74 | server.on("/d", []() { // Append dump handler 75 | conf.dump(&server); 76 | }); 77 | server.onNotFound(handleNotFound); // Append not found handler 78 | 79 | // Connect to any available network 80 | bool bConn = confHelper.connectToNetwork(15000, LED_BUILTIN); 81 | 82 | // Append config assist handlers to web server, setup ap on no connection 83 | conf.setup(server, !bConn); 84 | if(!bConn){ 85 | LOG_E("Connect failed.\n"); 86 | }else{ 87 | confHelper.startMDNS(); 88 | } 89 | 90 | // Start web server 91 | server.begin(); 92 | LOG_I("HTTP server started\n"); 93 | } 94 | 95 | // App main loop 96 | void loop(void) { 97 | server.handleClient(); 98 | confHelper.loop(); 99 | // Allow the cpu to switch to other tasks 100 | delay(2); 101 | } 102 | -------------------------------------------------------------------------------- /examples/ConfigAssist-FirmwareCheck/README.txt: -------------------------------------------------------------------------------- 1 | This is an Example hosting firmware updates of an esp application on a remote web server (like github.com) and 2 | perform automatic check and upgrade when the version is changed. 3 | 4 | The firmware version of the device running ConfigAssist is compared with the latest version stored in the remote web server. 5 | If there is a change an `Upgrade firmware` button will be displayed in order to perform an automatic firware upgrade to 6 | the newest version. 7 | 8 | How to use this feature.. 9 | Bellow is the steps needed to use firmware check and upgrade using ConfigAssist. This example 10 | is using github.com to host the new firmware. 11 | 12 | !. Create a repo in github.com with your project. 13 | * For example `https://github.com/gemi254/ControlAssist-ESP32-ESP8266` 14 | 15 | 2. Create a sub folder to store your firmware information and the binary file. 16 | * For example `https://github.com/gemi254/ControlAssist-ESP32-ESP8266/firmware` 17 | 18 | 3. Create e file named "latest.json" inside your firmware folder with firmware information in json format. 19 | * Inside latest.json put the latest version number, a short description, and a link to the firmware file. 20 | * For example `https://github.com/gemi254/ControlAssist-ESP32-ESP8266/firmware/latest.json` 21 | contents: 22 | { 23 | "ver" : "1.0.1", 24 | "url" : "https://raw.githubusercontent.com/gemi254/ConfigAssist-ESP32-ESP8266/main/examples/ConfigAssist-FirmwareCheck/firmware/FirmwareCheck1.0.1.bin", 25 | "descr": "A new firmware v1.0.1 with bug fixes and improvements." 26 | } 27 | 28 | 4. Set the default value of variable ``firmware_url``. 29 | * In you project at your VARIABLES_DEF_YAML (ConfigAssist-FirmwareCheck.ino line 55). 30 | 31 | 6. In you project, define your current firmware version. 32 | * char FIRMWARE_VERSION[] = "1.0.0"; 33 | 34 | 7. Compile and rename the firmware file. 35 | * firmware.bin -> FirmwareCheck1.1.1.bin 36 | 37 | 8. Upload the firmware to github at url defined in latest.json 38 | 39 | 9. By pressing `Firmware` button in config page, ConfigAssist will compare current version with the version stored in github.com 40 | If newer version exists it will show a button `Upgrade Firmware` that will automatically perform a firmware upgrade with 41 | the latest firmware stored in github.com 42 | -------------------------------------------------------------------------------- /examples/ConfigAssist-FirmwareCheck/firmCheckESP32PMem.h: -------------------------------------------------------------------------------- 1 | const char* VARIABLES_DEF_YAML PROGMEM = R"~( 2 | Wifi settings: 3 | - st_ssid: 4 | label: Name for WLAN 5 | - st_pass: 6 | label: Password for WLAN 7 | - host_name: 8 | label: >- 9 | Host name to use for MDNS and AP 10 | {mac} will be replaced with device's mac id 11 | default: configAssist_{mac} 12 | 13 | Application settings: 14 | - app_name: 15 | label: Name your application 16 | default: FirmwareCheck 17 | - firmware_url: 18 | label: Firmware upgrade url with version and info 19 | default: >- 20 | https://raw.githubusercontent.com/gemi254/ConfigAssist-ESP32-ESP8266/main/examples/ConfigAssist-FirmwareCheck/firmware/ESP32/lastest.json 21 | - time_zone: 22 | label: Needs to be a valid time zone string 23 | default: EET-2EEST,M3.5.0/3,M10.5.0/4 24 | datalist: 25 | - Etc/GMT,GMT0 26 | - Etc/GMT-0,GMT0 27 | - Etc/GMT-1,<+01>-1 28 | - Etc/GMT-2,<+02>-2 29 | - Etc/GMT-3,<+03>-3 30 | - Etc/GMT-4,<+04>-4 31 | - Etc/GMT-5,<+05>-5 32 | - Etc/GMT-6,<+06>-6 33 | - Etc/GMT-7,<+07>-7 34 | - Etc/GMT-8,<+08>-8 35 | - Etc/GMT-9,<+09>-9 36 | - Etc/GMT-10,<+10>-10 37 | - Etc/GMT-11,<+11>-11 38 | - Etc/GMT-12,<+12>-12 39 | - Etc/GMT-13,<+13>-13 40 | - Etc/GMT-14,<+14>-14 41 | - Etc/GMT0,GMT0 42 | - Etc/GMT+0,GMT0 43 | - Etc/GMT+1,<-01>1 44 | - Etc/GMT+2,<-02>2 45 | - Etc/GMT+3,<-03>3 46 | - Etc/GMT+4,<-04>4 47 | - Etc/GMT+5,<-05>5 48 | - Etc/GMT+6,<-06>6 49 | - Etc/GMT+7,<-07>7 50 | - Etc/GMT+8,<-08>8 51 | - Etc/GMT+9,<-09>9 52 | - Etc/GMT+10,<-10>10 53 | - Etc/GMT+11,<-11>11 54 | - Etc/GMT+12,<-12>12 55 | )~"; -------------------------------------------------------------------------------- /examples/ConfigAssist-FirmwareCheck/firmCheckESP8266PMem.h: -------------------------------------------------------------------------------- 1 | const char* VARIABLES_DEF_YAML PROGMEM = R"~( 2 | Wifi settings: 3 | - st_ssid: 4 | label: Name for WLAN 5 | default: 6 | - st_pass: 7 | label: Password for WLAN 8 | default: 9 | - host_name: 10 | label: >- 11 | Host name to use for MDNS and AP
{mac} will be replaced with device's mac id 12 | default: configAssist_{mac} 13 | 14 | Application settings: 15 | - app_name: 16 | label: Name your application 17 | default: FirmwareCheck 18 | - firmware_url: 19 | label: Firmware upgrade url with version and info 20 | default: >- 21 | https://raw.githubusercontent.com/gemi254/ConfigAssist-ESP32-ESP8266/main/examples/ConfigAssist-FirmwareCheck/firmware/ESP8266/lastest.json 22 | - time_zone: 23 | label: Needs to be a valid time zone string 24 | default: EET-2EEST,M3.5.0/3,M10.5.0/4 25 | datalist: 26 | - Etc/GMT,GMT0 27 | - Etc/GMT-0,GMT0 28 | - Etc/GMT-1,<+01>-1 29 | - Etc/GMT-2,<+02>-2 30 | - Etc/GMT-3,<+03>-3 31 | - Etc/GMT-4,<+04>-4 32 | - Etc/GMT-5,<+05>-5 33 | - Etc/GMT-6,<+06>-6 34 | - Etc/GMT-7,<+07>-7 35 | - Etc/GMT-8,<+08>-8 36 | - Etc/GMT-9,<+09>-9 37 | - Etc/GMT-10,<+10>-10 38 | - Etc/GMT-11,<+11>-11 39 | - Etc/GMT-12,<+12>-12 40 | - Etc/GMT-13,<+13>-13 41 | - Etc/GMT-14,<+14>-14 42 | - Etc/GMT0,GMT0 43 | - Etc/GMT+0,GMT0 44 | - Etc/GMT+1,<-01>1 45 | - Etc/GMT+2,<-02>2 46 | - Etc/GMT+3,<-03>3 47 | - Etc/GMT+4,<-04>4 48 | - Etc/GMT+5,<-05>5 49 | - Etc/GMT+6,<-06>6 50 | - Etc/GMT+7,<-07>7 51 | - Etc/GMT+8,<-08>8 52 | - Etc/GMT+9,<-09>9 53 | - Etc/GMT+10,<-10>10 54 | - Etc/GMT+11,<-11>11 55 | - Etc/GMT+12,<-12>12 56 | )~"; -------------------------------------------------------------------------------- /examples/ConfigAssist-FirmwareCheck/firmware/ESP32/FirmwareCheck1.0.1.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gemi254/ConfigAssist-ESP32-ESP8266/f7830b0e43629fceadb76a4bd6e2f7e38cb363a1/examples/ConfigAssist-FirmwareCheck/firmware/ESP32/FirmwareCheck1.0.1.bin -------------------------------------------------------------------------------- /examples/ConfigAssist-FirmwareCheck/firmware/ESP32/lastest.json: -------------------------------------------------------------------------------- 1 | { 2 | "ver" : "1.0.1", 3 | "url" : "https://raw.githubusercontent.com/gemi254/ConfigAssist-ESP32-ESP8266/main/examples/ConfigAssist-FirmwareCheck/firmware/ESP32/FirmwareCheck1.0.1.bin", 4 | "descr": "A new firmware for ESP32 v1.0.1 with bug fixes and improvements." 5 | } -------------------------------------------------------------------------------- /examples/ConfigAssist-FirmwareCheck/firmware/ESP8266/FirmwareCheck1.0.1.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gemi254/ConfigAssist-ESP32-ESP8266/f7830b0e43629fceadb76a4bd6e2f7e38cb363a1/examples/ConfigAssist-FirmwareCheck/firmware/ESP8266/FirmwareCheck1.0.1.bin -------------------------------------------------------------------------------- /examples/ConfigAssist-FirmwareCheck/firmware/ESP8266/lastest.json: -------------------------------------------------------------------------------- 1 | { 2 | "ver" : "1.0.1", 3 | "url" : "https://raw.githubusercontent.com/gemi254/ConfigAssist-ESP32-ESP8266/main/examples/ConfigAssist-FirmwareCheck/firmware/ESP8266/FirmwareCheck1.0.1.bin", 4 | "descr": "A new firmware for ESP8266 v1.0.1 with bug fixes and improvements." 5 | } -------------------------------------------------------------------------------- /examples/ConfigAssist-LargeTextArea/ConfigAssist-LargeTextArea.ino: -------------------------------------------------------------------------------- 1 | #include // Config assist class 2 | #include // Config assist helper class 3 | 4 | #include "appPMem.h" 5 | 6 | #ifndef LED_BUILTIN 7 | #define LED_BUILTIN 22 8 | #endif 9 | #if defined(ESP32) 10 | WebServer server(80); 11 | #else 12 | ESP8266WebServer server(80); 13 | #endif 14 | 15 | #define INI_FILE "/LargeTextArea.ini" // Define SPIFFS storage file 16 | 17 | // Config class 18 | ConfigAssist conf(INI_FILE, VARIABLES_DEF_YAML); 19 | 20 | // Define a ConfigAssist helper class 21 | ConfigAssistHelper confHelper(conf); 22 | 23 | unsigned long pingMillis = millis(); // Ping 24 | 25 | // Handler function for Home page 26 | void handleRoot() { 27 | 28 | String out("

Hello from {name}

"); 29 | out += "

Device time: " + conf.getLocalTime() +"

"; 30 | out += "Edit config"; 31 | 32 | #if defined(ESP32) 33 | out.replace("{name}", "ESP32"); 34 | #else 35 | out.replace("{name}", "ESP8266"); 36 | #endif 37 | // Setup time sync script in home page 38 | // When page opened by browser if esp time 39 | // is not set will be synchronized wih remote host time 40 | #if (CA_USE_TIMESYNC) 41 | out += ""; 42 | #endif 43 | server.send(200, "text/html", out); 44 | } 45 | 46 | // Handler for page not found 47 | void handleNotFound() { 48 | String message = "File Not Found\n\n"; 49 | message += "URI: "; 50 | message += server.uri(); 51 | message += "\nMethod: "; 52 | message += (server.method() == HTTP_GET) ? "GET" : "POST"; 53 | message += "\nArguments: "; 54 | message += server.args(); 55 | message += "\n"; 56 | for (uint8_t i = 0; i < server.args(); i++) { 57 | message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; 58 | } 59 | server.send(404, "text/plain", message); 60 | } 61 | 62 | // Setup function 63 | void setup(void) { 64 | 65 | // Setup internal led variable if not set 66 | if (conf("led_buildin") == "") conf["led_buildin"] = LED_BUILTIN; 67 | 68 | Serial.begin(115200); 69 | Serial.print("\n\n\n\n"); 70 | Serial.flush(); 71 | 72 | LOG_I("Starting.. \n"); 73 | 74 | //conf.deleteConfig(); // Uncomment to remove old ini file and re-built it fron dictionary 75 | 76 | //Active seperator open/close, All others closed 77 | conf.setDisplayType(ConfigAssistDisplayType::AccordionToggleClosed); 78 | 79 | // Register handlers for web server 80 | server.on("/", handleRoot); 81 | server.on("/d", []() { // Append dump handler 82 | conf.dump(&server); 83 | }); 84 | server.onNotFound(handleNotFound); // Append not found handler 85 | 86 | // Connect to any available network 87 | bool bConn = confHelper.connectToNetwork(15000, conf("led_buildin").toInt()); 88 | 89 | // Append config assist handlers to web server, setup ap on no connection 90 | conf.setup(server, !bConn); 91 | if(!bConn) LOG_E("Connect failed.\n"); 92 | else confHelper.startMDNS(); 93 | 94 | // Start web server 95 | server.begin(); 96 | LOG_I("HTTP server started\n"); 97 | } 98 | 99 | // App main loop 100 | void loop(void) { 101 | server.handleClient(); 102 | confHelper.loop(); 103 | } 104 | -------------------------------------------------------------------------------- /examples/ConfigAssist-LargeTextArea/appPMem.h: -------------------------------------------------------------------------------- 1 | const char* VARIABLES_DEF_YAML PROGMEM = R"~( 2 | Wifi settings: 3 | - st_ssid: 4 | label: Name for WLAN 5 | default: 6 | - st_pass: 7 | label: Password for WLAN 8 | default: 9 | - host_name: 10 | label: >- 11 | Host name to use for MDNS and AP
{mac} will be replaced with device's mac id 12 | default: configAssist_{mac} 13 | 14 | Server Security: 15 | - login_usr: admin 16 | label: User name for login authentication 17 | default: admin 18 | - login_pass: 19 | label: Password for login authentication 20 | default: admin 21 | 22 | - server_cert: 23 | label: >- 24 | Encryption certificate. 25 | These can generated with the bash script available in the ESP8266 Arduino repository 26 | file: "/cert.ini" 27 | attribs: style="width: 254px; height:190px;" 28 | default: >- 29 | -----BEGIN CERTIFICATE----- 30 | MIIDSzCCAjMCCQD2ahcfZAwXxDANBgkqhkiG9w0BAQsFADCBiTELMAkGA1UEBhMC 31 | VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU9yYW5nZSBDb3VudHkx 32 | EDAOBgNVBAoMB1ByaXZhZG8xGjAYBgNVBAMMEXNlcnZlci56bGFiZWwuY29tMR8w 33 | HQYJKoZIhvcNAQkBFhBlYXJsZUB6bGFiZWwuY29tMB4XDTE4MDMwNjA1NDg0NFoX 34 | DTE5MDMwNjA1NDg0NFowRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3Rh 35 | dGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZI 36 | hvcNAQEBBQADggEPADCCAQoCggEBAPVKBwbZ+KDSl40YCDkP6y8Sv4iNGvEOZg8Y 37 | X7sGvf/xZH7UiCBWPFIRpNmDSaZ3yjsmFqm6sLiYSGSdrBCFqdt9NTp2r7hga6Sj 38 | oASSZY4B9pf+GblDy5m10KDx90BFKXdPMCLT+o76Nx9PpCvw13A848wHNG3bpBgI 39 | t+w/vJCX3bkRn8yEYAU6GdMbYe7v446hX3kY5UmgeJFr9xz1kq6AzYrMt/UHhNzO 40 | S+QckJaY0OGWvmTNspY3xCbbFtIDkCdBS8CZAw+itnofvnWWKQEXlt6otPh5njwy 41 | +O1t/Q+Z7OMDYQaH02IQx3188/kW3FzOY32knER1uzjmRO+jhA8CAwEAATANBgkq 42 | hkiG9w0BAQsFAAOCAQEAnDrROGRETB0woIcI1+acY1yRq4yAcH2/hdq2MoM+DCyM 43 | E8CJaOznGR9ND0ImWpTZqomHOUkOBpvu7u315blQZcLbL1LfHJGRTCHVhvVrcyEb 44 | fWTnRtAQdlirUm/obwXIitoz64VSbIVzcqqfg9C6ZREB9JbEX98/9Wp2gVY+31oC 45 | JfUvYadSYxh3nblvA4OL+iEZiW8NE3hbW6WPXxvS7Euge0uWMPc4uEcnsE0ZVG3m 46 | +TGimzSdeWDvGBRWZHXczC2zD4aoE5vrl+GD2i++c6yjL/otHfYyUpzUfbI2hMAA 47 | 5tAF1D5vAAwA8nfPysumlLsIjohJZo4lgnhB++AlOg== 48 | -----END CERTIFICATE----- 49 | - server_key: 50 | label: >- 51 | Encryption key. 52 | These can generated with the bash script available in the ESP8266 Arduino repository 53 | file: "/key.ini" 54 | attribs: style="width: 254px; height:192px;" 55 | default: >- 56 | -----BEGIN RSA PRIVATE KEY----- 57 | MIIEpQIBAAKCAQEA9UoHBtn4oNKXjRgIOQ/rLxK/iI0a8Q5mDxhfuwa9//FkftSI 58 | IFY8UhGk2YNJpnfKOyYWqbqwuJhIZJ2sEIWp2301OnavuGBrpKOgBJJljgH2l/4Z 59 | uUPLmbXQoPH3QEUpd08wItP6jvo3H0+kK/DXcDzjzAc0bdukGAi37D+8kJfduRGf 60 | zIRgBToZ0xth7u/jjqFfeRjlSaB4kWv3HPWSroDNisy39QeE3M5L5ByQlpjQ4Za+ 61 | ZM2yljfEJtsW0gOQJ0FLwJkDD6K2eh++dZYpAReW3qi0+HmePDL47W39D5ns4wNh 62 | BofTYhDHfXzz+RbcXM5jfaScRHW7OOZE76OEDwIDAQABAoIBAQDKov5NFbNFQNR8 63 | djcM1O7Is6dRaqiwLeH4ZH1pZ3d9QnFwKanPdQ5eCj9yhfhJMrr5xEyCqT0nMn7T 64 | yEIGYDXjontfsf8WxWkH2TjvrfWBrHOIOx4LJEvFzyLsYxiMmtZXvy6YByD+Dw2M 65 | q2GH/24rRdI2klkozIOyazluTXU8yOsSGxHr/aOa9/sZISgLmaGOOuKI/3Zqjdhr 66 | eHeSqoQFt3xXa8jw01YubQUDw/4cv9rk2ytTdAoQUimiKtgtjsggpP1LTq4xcuqN 67 | d4jWhTcnorWpbD2cVLxrEbnSR3VuBCJEZv5axg5ZPxLEnlcId8vMtvTRb5nzzszn 68 | geYUWDPhAoGBAPyKVNqqwQl44oIeiuRM2FYenMt4voVaz3ExJX2JysrG0jtCPv+Y 69 | 84R6Cv3nfITz3EZDWp5sW3OwoGr77lF7Tv9tD6BptEmgBeuca3SHIdhG2MR+tLyx 70 | /tkIAarxQcTGsZaSqra3gXOJCMz9h2P5dxpdU+0yeMmOEnAqgQ8qtNBfAoGBAPim 71 | RAtnrd0WSlCgqVGYFCvDh1kD5QTNbZc+1PcBHbVV45EmJ2fLXnlDeplIZJdYxmzu 72 | DMOxZBYgfeLY9exje00eZJNSj/csjJQqiRftrbvYY7m5njX1kM5K8x4HlynQTDkg 73 | rtKO0YZJxxmjRTbFGMegh1SLlFLRIMtehNhOgipRAoGBAPnEEpJGCS9GGLfaX0HW 74 | YqwiEK8Il12q57mqgsq7ag7NPwWOymHesxHV5mMh/Dw+NyBi4xAGWRh9mtrUmeqK 75 | iyICik773Gxo0RIqnPgd4jJWN3N3YWeynzulOIkJnSNx5BforOCTc3uCD2s2YB5X 76 | jx1LKoNQxLeLRN8cmpIWicf/AoGBANjRSsZTKwV9WWIDJoHyxav/vPb+8WYFp8lZ 77 | zaRxQbGM6nn4NiZI7OF62N3uhWB/1c7IqTK/bVHqFTuJCrCNcsgld3gLZ2QWYaMV 78 | kCPgaj1BjHw4AmB0+EcajfKilcqtSroJ6MfMJ6IclVOizkjbByeTsE4lxDmPCDSt 79 | /9MKanBxAoGAY9xo741Pn9WUxDyRplww606ccdNf/ksHWNc/Y2B5SPwxxSnIq8nO 80 | j01SmsCUYVFAgZVOTiiycakjYLzxlc6p8BxSVqy6LlJqn95N8OXoQ+bkwUux/ekg 81 | gz5JWYhbD6c38khSzJb0pNXCo3EuYAVa36kDM96k1BtWuhRS10Q1VXk= 82 | -----END RSA PRIVATE KEY----- 83 | Application settings: 84 | - app_name: 85 | label: Name your application 86 | default: LargeTextArea 87 | - led_buildin: 88 | label: Enter the pin that the build in led is connected. 89 | Leave blank for auto. 90 | attribs: "min='2' max='23' step='1'" 91 | default: 92 | )~"; 93 | -------------------------------------------------------------------------------- /examples/ConfigAssist-LogExternal/ConfigAssist-LogExternal.ino: -------------------------------------------------------------------------------- 1 | #define LOGGER_LOG_MODE 3 // Set default logging mode using external function 2 | #define LOGGER_LOG_FILENAME "/logE" 3 | #define LOGGER_LOG_LEVEL 5 // Errors & Warnings & Info & Debug & Verbose 4 | void _log_printf(const char *format, ...); // Provide custom print log function 5 | 6 | #include // Config assist class 7 | 8 | bool logToFile = true; 9 | static File logFile; 10 | 11 | #define MAX_LOG_FMT 128 12 | static char fmtBuf[MAX_LOG_FMT]; 13 | static char outBuf[512]; 14 | static va_list arglist; 15 | 16 | // Custom log print function 17 | void _log_printf(const char *format, ...){ 18 | strncpy(fmtBuf, format, MAX_LOG_FMT); 19 | fmtBuf[MAX_LOG_FMT - 1] = 0; 20 | va_start(arglist, format); 21 | vsnprintf(outBuf, MAX_LOG_FMT, fmtBuf, arglist); 22 | va_end(arglist); 23 | Serial.print(outBuf); 24 | if (logToFile){ 25 | if(!logFile) logFile = STORAGE.open(LOGGER_LOG_FILENAME, "a+"); 26 | if(!logFile){ 27 | Serial.printf("Failed to open log file: %s\n", LOGGER_LOG_FILENAME); 28 | logToFile = false; 29 | return; 30 | } 31 | logFile.print(outBuf); 32 | logFile.flush(); 33 | } 34 | } 35 | 36 | // Print the log generated to serial port 37 | void serialPrintLog(){ 38 | Serial.printf("Display log: %s\n", LOGGER_LOG_FILENAME); 39 | File f = STORAGE.open(LOGGER_LOG_FILENAME, "r"); 40 | // Read from the file until there's nothing else in it: 41 | while (f.available()) 42 | { 43 | Serial.write(f.read()); 44 | } 45 | // close the file: 46 | f.close(); 47 | Serial.print("\nDisplay log..Done\n"); 48 | } 49 | 50 | // Setup function 51 | void setup() { 52 | Serial.begin(115200); 53 | Serial.print("\n\n\n\n"); 54 | Serial.flush(); 55 | Serial.print("Starting..\n"); 56 | 57 | #if defined(ESP32) 58 | if(!STORAGE.begin(true)) Serial.println("ESP32 storage init failed!"); 59 | #else 60 | if(!STORAGE.begin()) Serial.println("ESP8266 storage init failed!"); 61 | #endif 62 | 63 | // Display the log file 64 | serialPrintLog(); 65 | 66 | LOG_I("* * * * Starting * * * * * \n"); 67 | 68 | LOG_E("F:This is an ERROR message \n"); 69 | LOG_W("F:This is a WARNING message \n"); 70 | LOG_I("F:This is an INFO message \n"); 71 | LOG_D("F:This is a DEBUG message \n"); 72 | LOG_V("F:This is a VERBOSE message \n"); 73 | 74 | // Create a config class with an ini filename and no dictionary 75 | ConfigAssist info("/info.ini"); 76 | 77 | if(false){ // Set to true to reset 78 | info.deleteConfig(); // Remove ini file and re-built 79 | info.clear(); // Clear loaded keys 80 | STORAGE.remove(LOGGER_LOG_FILENAME); 81 | } 82 | if(!info.valid()){ //Add boot counter 83 | info["bootCnt"] = 1; 84 | }else{ // Ini is valid, increase counter and display the value 85 | info["bootCnt"] = info("bootCnt").toInt() + 1; 86 | LOG_I("Info file: bootCnt: %lu\n", info("bootCnt").toInt()); 87 | } 88 | // ave keys & values into ini file 89 | info.saveConfigFile(); 90 | 91 | // On the fly Stop loggin to file 92 | logToFile = false; 93 | logFile.close(); 94 | 95 | LOG_E("S:This is an ERROR message \n"); 96 | LOG_W("S:This is a WARNING message \n"); 97 | LOG_I("S:This is an INFO message \n"); 98 | LOG_D("S:This is a DEBUG message \n"); 99 | LOG_V("S:This is a VERBOSE message \n"); 100 | 101 | LOG_D("End of setup()..\n"); 102 | 103 | } 104 | 105 | void loop() { 106 | // put your main code here, to run repeatedly: 107 | 108 | } 109 | 110 | -------------------------------------------------------------------------------- /examples/ConfigAssist-LogToFile/ConfigAssist-LogToFile.ino: -------------------------------------------------------------------------------- 1 | #define LOGGER_LOG_MODE 2 // Log to file 2 | #define LOGGER_LOG_LEVEL 5 // Errors & Warnings & Info & Debug & Verbose 3 | #define LOGGER_LOG_FILENAME "/log1" 4 | 5 | #include // Config assist class 6 | 7 | // Print the log generated to serial port 8 | void serialPrintLog(){ 9 | Serial.printf("Display log: %s\n", LOGGER_LOG_FILENAME); 10 | File f = STORAGE.open(LOGGER_LOG_FILENAME, "r"); 11 | // read from the file until there's nothing else in it: 12 | while (f.available()) 13 | { 14 | Serial.write(f.read()); 15 | } 16 | // close the file: 17 | f.close(); 18 | Serial.print("\nDisplay log..Done\n"); 19 | } 20 | 21 | // Setup function 22 | void setup() { 23 | Serial.begin(115200); 24 | Serial.print("\n\n\n\n"); 25 | Serial.flush(); 26 | Serial.print("Starting..\n"); 27 | 28 | #if defined(ESP32) 29 | if(!STORAGE.begin(true)) Serial.println("ESP32 storage init failed!"); 30 | #else 31 | if(!STORAGE.begin()) Serial.println("ESP8266 storage init failed!"); 32 | #endif 33 | 34 | //STORAGE.remove(LOGGER_LOG_FILENAME); //Uncomment to reset the log file 35 | 36 | //Display the log file 37 | serialPrintLog(); 38 | 39 | LOG_I("* * * * Starting * * * * * \n"); 40 | 41 | LOG_E("This is an ERROR message \n"); 42 | LOG_W("This is a WARNING message \n"); 43 | LOG_I("This is an INFO message \n"); 44 | LOG_D("This is a DEBUG message \n"); 45 | LOG_V("This is a VERBOSE message \n"); 46 | 47 | // Create a config class with an ini filename and disabled dictionary 48 | ConfigAssist info("/info.ini"); 49 | 50 | //info.deleteConfig(); //Uncomment to remove ini file and re-built 51 | 52 | if(!info.valid()){ //Add boot counter 53 | info["bootCnt"] = 1; 54 | }else{ //Ini is valid, increase counter and display the value 55 | info["bootCnt"] = info("bootCnt").toInt() + 1; 56 | LOG_I("Info file: bootCnt: %lu\n", info("bootCnt").toInt()); 57 | } 58 | //Save keys & values into ini file 59 | info.saveConfigFile(); 60 | LOG_D("End of setup()..\n"); 61 | LOGGER_CLOSE_LOG() 62 | } 63 | 64 | void loop() { 65 | // put your main code here, to run repeatedly: 66 | 67 | } 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /examples/ConfigAssist-Minimal/ConfigAssist-Minimal.ino: -------------------------------------------------------------------------------- 1 | #include // Config assist class 2 | #include // Config assist helper class 3 | #if defined(ESP32) 4 | WebServer server(80); 5 | #else 6 | ESP8266WebServer server(80); 7 | #endif 8 | // Create a config class with an ini filename for storage 9 | // Default wifi connect credentials yaml 10 | ConfigAssist conf("/minimal.ini", true); 11 | 12 | void setup() { 13 | // put your setup code here, to run once: 14 | 15 | Serial.begin(115200); 16 | Serial.print("\n\n\n\n"); 17 | Serial.flush(); 18 | 19 | LOG_I("Starting..\n"); 20 | 21 | //conf.deleteConfig(); // Uncomment to remove ini file and re-built 22 | 23 | // Define a ConfigAssist helper 24 | ConfigAssistHelper confHelper(conf); 25 | 26 | // Connect to any available network 27 | bool bConn = confHelper.connectToNetwork(15000); 28 | if(!bConn) LOG_E("Connect failed.\n"); 29 | 30 | // Start AP to fix wifi credentials 31 | conf.setup(server, !bConn); 32 | 33 | server.on("/", []() { // Append dump handler 34 | conf.init(); 35 | conf.dump(&server); 36 | }); 37 | 38 | // Start web server 39 | server.begin(); 40 | conf.dump(); 41 | } 42 | 43 | void loop() { 44 | // put your main code here, to run repeatedly: 45 | server.handleClient(); 46 | } 47 | -------------------------------------------------------------------------------- /examples/ConfigAssist-NtpTimeSync/ConfigAssist-NtpTimeSync.ino: -------------------------------------------------------------------------------- 1 | #include // Config assist class 2 | #include // Config assist helper class 3 | 4 | #include "appPMem.h" 5 | 6 | #ifndef LED_BUILTIN 7 | #define LED_BUILTIN 22 8 | #endif 9 | #if defined(ESP32) 10 | WebServer server(80); 11 | #else 12 | ESP8266WebServer server(80); 13 | #endif 14 | 15 | #define INI_FILE "/NtpTimeSync.ini" // Define SPIFFS storage file 16 | 17 | // Config class 18 | ConfigAssist conf(INI_FILE, VARIABLES_DEF_YAML); 19 | 20 | // Define a ConfigAssist helper class 21 | ConfigAssistHelper confHelper(conf); 22 | 23 | // Setup internal led variable if not set 24 | String s = (conf("led_buildin") == "") ? conf["led_buildin"] = LED_BUILTIN : ""; 25 | 26 | time_t tnow; 27 | unsigned long pingMillis = millis(); // Ping 28 | 29 | // Handler function for Home page 30 | void handleRoot() { 31 | 32 | String out("

Hello from {name}

"); 33 | out += "

Device time: " + conf.getLocalTime() +"

"; 34 | out += "Edit config"; 35 | 36 | #if defined(ESP32) 37 | out.replace("{name}", "ESP32"); 38 | #else 39 | out.replace("{name}", "ESP8266!"); 40 | #endif 41 | 42 | #if (CA_USE_TIMESYNC) 43 | // Send a web browser sync java script to client 44 | out += ""; 45 | #endif 46 | server.send(200, "text/html", out); 47 | } 48 | 49 | // Handler for page not found 50 | void handleNotFound() { 51 | String message = "File Not Found\n\n"; 52 | message += "URI: "; 53 | message += server.uri(); 54 | message += "\nMethod: "; 55 | message += (server.method() == HTTP_GET) ? "GET" : "POST"; 56 | message += "\nArguments: "; 57 | message += server.args(); 58 | message += "\n"; 59 | for (uint8_t i = 0; i < server.args(); i++) { 60 | message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; 61 | } 62 | server.send(404, "text/plain", message); 63 | } 64 | 65 | 66 | void onConnectionResult(ConfigAssistHelper::WiFiResult result, const String& msg){ 67 | 68 | switch (result) { 69 | case ConfigAssistHelper::WiFiResult::SUCCESS: 70 | LOG_D("Connected to Wi-Fi! IP: %s\n", msg.c_str()); 71 | confHelper.startMDNS(); 72 | conf.setupConfigPortalHandlers(server); 73 | // Start web server 74 | server.begin(); 75 | LOG_I("HTTP server started\n"); 76 | LOG_I("Device started. Visit http://%s\n", WiFi.localIP().toString().c_str()); 77 | 78 | // Setup time synchronization, Wait max 10 sec 79 | LOG_I("Start time sync..\n"); 80 | confHelper.syncTime(10000); 81 | pingMillis = millis(); 82 | tnow = time(nullptr); 83 | break; 84 | 85 | case ConfigAssistHelper::WiFiResult::INVALID_CREDENTIALS: 86 | LOG_D("Invalid credentials: %s\n", msg.c_str()); 87 | // Append config assist handlers to web server, setup ap on no connection 88 | conf.setup(server, true); 89 | break; 90 | 91 | case ConfigAssistHelper::WiFiResult::CONNECTION_TIMEOUT: 92 | LOG_D("Connection fail: %s\n", msg.c_str()); 93 | conf.setup(server, true); 94 | break; 95 | 96 | case ConfigAssistHelper::WiFiResult::DISCONNECTION_ERROR: 97 | LOG_D("Disconnect: %s\n", msg.c_str()); 98 | WiFi.setAutoReconnect(true); 99 | confHelper.setReconnect(true); 100 | break; 101 | 102 | default: 103 | LOG_D("Unknown result: %s\n", msg.c_str()); 104 | break; 105 | } 106 | } 107 | 108 | // Setup function 109 | void setup(void) { 110 | 111 | Serial.begin(115200); 112 | Serial.print("\n\n\n\n"); 113 | Serial.flush(); 114 | 115 | LOG_I("Starting.. \n"); 116 | if(false) conf.deleteConfig(); //Set to true to remove old ini file and re-built it fron dictionary 117 | 118 | //Active seperator open/close, All others closed 119 | conf.setDisplayType(ConfigAssistDisplayType::AccordionToggleClosed); 120 | 121 | // Register handlers for web server 122 | server.on("/", handleRoot); 123 | server.on("/d", []() { // Append dump handler 124 | conf.dump(&server); 125 | }); 126 | server.onNotFound(handleNotFound); // Append not found handler 127 | confHelper.setWiFiResultCallback(onConnectionResult); 128 | // Connect to any available network 129 | confHelper.connectToNetworkAsync(15000, conf("led_buildin").toInt()); 130 | } 131 | 132 | int syncType = 0; 133 | 134 | 135 | // App main loop 136 | void loop(void) { 137 | server.handleClient(); 138 | // Update led, print dots when connecting, check connection on connected 139 | confHelper.loop(); 140 | 141 | if(!WiFi.isConnected()) return; 142 | // Display info 143 | if (millis() - pingMillis >= 20000){ 144 | tnow = time(nullptr); 145 | LOG_I("Time in sync: %i clock: %s", confHelper.isTimeSync(), ctime(&tnow)); 146 | syncType++; 147 | pingMillis = millis(); 148 | }else{ 149 | return; 150 | } 151 | 152 | if(syncType == 1){ 153 | // Force time synchronization, 154 | // syncTime will not wait if already time is set 155 | // and time will be automatically sync in background. 156 | LOG_I("* * * Starting soft resync time...\n"); 157 | confHelper.syncTime(conf("sync_timeout").toInt() * 1000L, false); 158 | tnow = time(nullptr); 159 | LOG_I("Soft resync time: %i clock: %s", confHelper.isTimeSync(), ctime(&tnow)); 160 | 161 | }else if(syncType == 2){ 162 | // Force time synchronization, 163 | // Clock will be reseted and wait for max 20 sec to sync 164 | // If fail clock will be restored 165 | LOG_I("* * * Starting hard resync time...\n"); 166 | confHelper.syncTime(conf("sync_timeout").toInt() * 1000L, true); 167 | tnow = time(nullptr); 168 | LOG_I("Hard resynced time: %i clock: %s", confHelper.isTimeSync(), ctime(&tnow)); 169 | }else if(syncType == 3){ 170 | // Force time synchronization and don't wait for Synchronization 171 | LOG_I("* * * Starting ASYNC hard resync time...\n"); 172 | confHelper.syncTimeAsync(conf("sync_timeout").toInt() * 1000L, true); 173 | tnow = time(nullptr); 174 | LOG_N("ASYNC Hard resynced time: %i clock: %s", confHelper.isTimeSync(), ctime(&tnow)); 175 | } 176 | if(syncType > 3) syncType = 0; 177 | // Allow the cpu to switch to other tasks 178 | delay(2); 179 | } 180 | -------------------------------------------------------------------------------- /examples/ConfigAssist-NtpTimeSync/appPMem.h: -------------------------------------------------------------------------------- 1 | const char* VARIABLES_DEF_YAML PROGMEM = R"~( 2 | Wifi settings: 3 | - st_ssid: 4 | label: Name for WLAN 5 | - st_pass: 6 | label: Password for WLAN 7 | - host_name: 8 | label: >- 9 | Host name to use for MDNS and AP
{mac} will be replaced with device's mac id 10 | default: configAssist_{mac} 11 | 12 | Application settings: 13 | - app_name: 14 | label: Name your application 15 | default: NtpTimeSync 16 | - led_buildin: 17 | label: Enter the pin that the build in led is connected. 18 | Leave blank for auto. 19 | attribs: "min='2' max='23' step='1'" 20 | 21 | Time settings: 22 | - time_zone: 23 | label: Needs to be a valid time zone string 24 | default: EET-2EEST,M3.5.0/3,M10.5.0/4 25 | datalist: 26 | - Etc/GMT,GMT0 27 | - Etc/GMT-0,GMT0 28 | - Etc/GMT-1,<+01>-1 29 | - Etc/GMT-2,<+02>-2 30 | - Etc/GMT-3,<+03>-3 31 | - Etc/GMT-4,<+04>-4 32 | - Etc/GMT-5,<+05>-5 33 | - Etc/GMT-6,<+06>-6 34 | - Etc/GMT-7,<+07>-7 35 | - Etc/GMT-8,<+08>-8 36 | - Etc/GMT-9,<+09>-9 37 | - Etc/GMT-10,<+10>-10 38 | - Etc/GMT-11,<+11>-11 39 | - Etc/GMT-12,<+12>-12 40 | - Etc/GMT-13,<+13>-13 41 | - Etc/GMT-14,<+14>-14 42 | - Etc/GMT0,GMT0 43 | - Etc/GMT+0,GMT0 44 | - Etc/GMT+1,<-01>1 45 | - Etc/GMT+2,<-02>2 46 | - Etc/GMT+3,<-03>3 47 | - Etc/GMT+4,<-04>4 48 | - Etc/GMT+5,<-05>5 49 | - Etc/GMT+6,<-06>6 50 | - Etc/GMT+7,<-07>7 51 | - Etc/GMT+8,<-08>8 52 | - Etc/GMT+9,<-09>9 53 | - Etc/GMT+10,<-10>10 54 | - Etc/GMT+11,<-11>11 55 | - Etc/GMT+12,<-12>12 56 | 57 | - ntp_server1: 58 | label: Time server to sync time1 59 | default: "europe.pool.ntp.org" 60 | - ntp_server2: 61 | label: Time server to sync time2 62 | default: "time.windows.com" 63 | - ntp_server3: 64 | label: Time server to sync time3 65 | default: "pool.ntp.org" 66 | 67 | - sync_timeout: 68 | label: Timeout for time sync (sec) 69 | default: 15 70 | )~"; 71 | -------------------------------------------------------------------------------- /examples/ConfigAssist-Simple/ConfigAssist-Simple.ino: -------------------------------------------------------------------------------- 1 | #include // Config assist class 2 | bool reset = false; 3 | 4 | void setup() { 5 | // put your setup code here, to run once: 6 | 7 | Serial.begin(115200); 8 | Serial.print("\n\n\n\n"); 9 | Serial.flush(); 10 | 11 | LOG_I("Starting..\n"); 12 | 13 | // Create a config class with an ini filename for storage 14 | // and disabled yaml dictionary (NULL) 15 | // If ini file exists load it 16 | ConfigAssist info("/info.ini"); 17 | 18 | if(reset){ 19 | info.deleteConfig(); // Remove ini file and re-built 20 | info.clear(); // Clear loaded keys 21 | } 22 | // Ini file not exists 23 | if(!info.confExists()) { 24 | // Add a key even if not exists. 25 | // It will be not editable, but it will be saved to an ini file 26 | LOG_I("Config file not exists\n"); 27 | info["string"] = "This is a string"; 28 | info["numString"] = "12345"; 29 | info["integer"] = 1; 30 | info["float"] = 1.0; 31 | 32 | }else{ // Ini file is valid, display the values 33 | LOG_I("Config file already exists\n"); 34 | info["integer"] = info("integer").toInt() + 1; 35 | info["float"] = info("float").toFloat() + .5; 36 | } 37 | // Save keys & values into an ini file 38 | info.saveConfigFile(); 39 | info.dump(); 40 | } 41 | 42 | void loop() { 43 | // put your main code here, to run repeatedly: 44 | } 45 | -------------------------------------------------------------------------------- /examples/ConfigAssist-Test-Wifi/ConfigAssist-Test-Wifi.ino: -------------------------------------------------------------------------------- 1 | #include // Config assist class 2 | #include // Config assist helper class 3 | 4 | #if defined(ESP32) 5 | WebServer server(80); 6 | #else 7 | ESP8266WebServer server(80); 8 | #endif 9 | 10 | #define INI_FILE "/TestWifi.ini" // Define SPIFFS storage file 11 | 12 | #ifndef LED_BUILTIN 13 | #define LED_BUILTIN 22 14 | #endif 15 | const char* VARIABLES_DEF_YAML PROGMEM = R"~( 16 | Wifi connection settings: 17 | - st_ssid1: 18 | label: Name for WLAN 1 19 | - st_pass1: 20 | label: Password for WLAN 1 21 | - st_ip1: 22 | label: Static ip setup (ip mask gateway) (192.168.1.100 255.255.255.0 192.168.1.1) 23 | 24 | - st_ssid2: 25 | label: Name for WLAN 2 26 | - st_pass2: 27 | label: Password for WLAN 2 28 | - st_ip2: 29 | label: Static ip setup (ip mask gateway) (192.168.1.100 255.255.255.0 192.168.1.1) 30 | 31 | - st_ssid3: 32 | label: Name for WLAN 3 33 | - st_pass3: 34 | label: Password for WLAN 35 | - st_ip3: 36 | label: Static ip setup (ip mask gateway) (192.168.1.100 255.255.255.0 192.168.1.1) 37 | 38 | - connect_timeout: 39 | label: Seconds to wait until ST connection 40 | default: 10 41 | - conn_failover: 42 | label: Connect to next wifi on connection fail. 43 | checked: true 44 | 45 | Wifi Access Point settings: 46 | - ap_ssid: 47 | label: Access point ssid. (Leave blank for defaule) 48 | - ap_pass: 49 | label: Password for Access point, leave blank for none. 50 | - ap_ip: 51 | label: Static ip setup (ip mask gateway) (192.168.4.1 255.255.255.0 192.168.4.1) 52 | 53 | Application settings: 54 | - host_name: 55 | label: >- 56 | Host name to use for MDNS and AP
{mac} will be replaced with device's mac id 57 | default: configAssist_{mac} 58 | - app_name: 59 | label: Name your application 60 | default: TestWifi 61 | - led_buildin: 62 | label: Enter the pin that the build in led is connected. 63 | Leave blank for auto. 64 | attribs: "min='2' max='23' step='1'" 65 | - debug: 66 | label: Debug application 67 | checked: false 68 | )~"; 69 | 70 | ConfigAssist conf(INI_FILE, VARIABLES_DEF_YAML); 71 | // Define a ConfigAssist helper class to manage wifi connections 72 | ConfigAssistHelper confHelper(conf); 73 | 74 | String hostName; 75 | unsigned long pingMillis = millis(); 76 | 77 | // Print memory info 78 | void debugMemory(const char* caller) { 79 | #if defined(ESP32) 80 | LOG_D("%s > Free: heap %u, block: %u, pSRAM %u\n", caller, ESP.getFreeHeap(), heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL), ESP.getFreePsram()); 81 | #else 82 | LOG_D("%s > Free: heap %u\n", caller, ESP.getFreeHeap()); 83 | LOG_D("%s > WiFi: %i, state: %i\n", caller, WiFi.isConnected(), (int)confHelper.getLedState() ); 84 | #endif 85 | } 86 | 87 | // Web server root handler 88 | void handleRoot() { 89 | String out("

Hello from {name}

"); 90 | out += "

Device time: " + conf.getLocalTime() +"

"; 91 | out += "Edit config"; 92 | 93 | #if defined(ESP32) 94 | out.replace("{name}", "ESP32"); 95 | #else 96 | out.replace("{name}", "ESP8266!"); 97 | #endif 98 | // Send browser time sync script 99 | #if (CA_USE_TESTWIFI) 100 | out += ""; 101 | #endif 102 | server.send(200, "text/html", out); 103 | } 104 | 105 | // Page not found handler 106 | void handleNotFound() { 107 | String message = "File Not Found\n\n"; 108 | message += "URI: "; 109 | message += server.uri(); 110 | message += "\nMethod: "; 111 | message += (server.method() == HTTP_GET) ? "GET" : "POST"; 112 | message += "\nArguments: "; 113 | message += server.args(); 114 | message += "\n"; 115 | for (uint8_t i = 0; i < server.args(); i++) { 116 | message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; 117 | } 118 | server.send(404, "text/plain", message); 119 | } 120 | 121 | void onWiFiResult(ConfigAssistHelper::WiFiResult res, const String& message) { 122 | if (res == ConfigAssistHelper::WiFiResult::SUCCESS) { 123 | //LOG_D("Connected to Wi-Fi! IP: %s\n", message.c_str()); 124 | confHelper.startMDNS(); 125 | conf.setupConfigPortalHandlers(server); 126 | server.begin(); 127 | LOG_D("HTTP server started\n"); 128 | LOG_I("Device started. Visit http://%s\n", WiFi.localIP().toString().c_str()); 129 | 130 | }else if (res == ConfigAssistHelper::WiFiResult::DISCONNECTION_ERROR) { 131 | LOG_E("Disconnect error: %s\n", message.c_str()); 132 | confHelper.setReconnect(true); 133 | 134 | }else if (res == ConfigAssistHelper::WiFiResult::CONNECTION_TIMEOUT) { 135 | LOG_E("Connect timeout: %s\n", message.c_str()); 136 | //if(conf.is) 137 | conf.setupConfigPortal(server, true); 138 | 139 | }else if (res == ConfigAssistHelper::WiFiResult::INVALID_CREDENTIALS) { 140 | LOG_W("Invalid credentials : %s\n", message.c_str()); 141 | conf.setupConfigPortal(server, true); 142 | } 143 | } 144 | 145 | //Setup function 146 | void setup(void) { 147 | // Setup internal led variable if not set 148 | if (conf("led_buildin") == "") conf["led_buildin"] = LED_BUILTIN; 149 | Serial.begin(115200); 150 | Serial.print("\n\n\n\n"); 151 | Serial.flush(); 152 | 153 | LOG_I("Starting..\n"); 154 | debugMemory("setup"); 155 | 156 | //conf.deleteConfig(); //Uncomment to remove ini file and re-built it fron yaml 157 | 158 | // Setup web server 159 | server.on("/", handleRoot); 160 | server.on("/d", []() { // Append dump handler 161 | conf.dump(&server); 162 | }); 163 | 164 | server.onNotFound(handleNotFound); 165 | 166 | // Start connecting to any available network, 167 | // On wifi event onWiFiResult will be called 168 | // Starting mdns, webserver services will be 169 | // called when the connection has been made 170 | confHelper.connectToNetworkAsync(conf("connect_timeout").toInt()*1000L, LED_BUILTIN, onWiFiResult); 171 | LOG_I("Setup end\n"); 172 | } 173 | 174 | //Loop function 175 | void loop(void) { 176 | server.handleClient(); 177 | #if not defined(ESP32) 178 | MDNS.update(); 179 | #endif 180 | 181 | if (millis() - pingMillis >= 10000){ 182 | if(conf["debug"].toInt()) debugMemory("Loop"); 183 | pingMillis = millis(); 184 | } 185 | 186 | // Run connection checker 187 | confHelper.loop(); 188 | 189 | delay(2); 190 | } 191 | -------------------------------------------------------------------------------- /examples/ConfigAssist-VarAttributes/ConfigAssist-VarAttrtibutes.ino: -------------------------------------------------------------------------------- 1 | #include // Config assist class 2 | #include // Config assist helper class 3 | 4 | #include "appPMem.h" 5 | 6 | #ifndef LED_BUILTIN 7 | #define LED_BUILTIN 22 8 | #endif 9 | #if defined(ESP32) 10 | WebServer server(80); 11 | #else 12 | ESP8266WebServer server(80); 13 | #endif 14 | 15 | #define INI_FILE "/VarAttributes.ini" // Define SPIFFS storage file 16 | 17 | // Config class 18 | ConfigAssist conf(INI_FILE, VARIABLES_DEF_YAML); 19 | 20 | // Define a ConfigAssist helper class 21 | ConfigAssistHelper confHelper(conf); 22 | 23 | // Handler function for Home page 24 | void handleRoot() { 25 | 26 | String out("

Hello from {name}

"); 27 | out += "

Device time: " + conf.getLocalTime() +"

"; 28 | out += "Edit config"; 29 | 30 | #if defined(ESP32) 31 | out.replace("{name}", "ESP32"); 32 | #else 33 | out.replace("{name}", "ESP8266!"); 34 | #endif 35 | #if (CA_USE_TIMESYNC) 36 | out += ""; 37 | #endif 38 | server.send(200, "text/html", out); 39 | } 40 | 41 | // Handler for page not found 42 | void handleNotFound() { 43 | String message = "File Not Found\n\n"; 44 | message += "URI: "; 45 | message += server.uri(); 46 | message += "\nMethod: "; 47 | message += (server.method() == HTTP_GET) ? "GET" : "POST"; 48 | message += "\nArguments: "; 49 | message += server.args(); 50 | message += "\n"; 51 | for (uint8_t i = 0; i < server.args(); i++) { 52 | message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; 53 | } 54 | server.send(404, "text/plain", message); 55 | } 56 | 57 | // Setup function 58 | void setup(void) { 59 | // Setup internal led variable if not set 60 | if (conf("led_buildin") == "") conf["led_buildin"] = LED_BUILTIN; 61 | 62 | Serial.begin(115200); 63 | Serial.print("\n\n\n\n"); 64 | Serial.flush(); 65 | 66 | #if defined(CA_USE_LITTLEFS) 67 | LOG_I("Starting with LITTLEFS.. \n"); 68 | #else 69 | LOG_I("Starting with SPIFFS.. \n"); 70 | #endif 71 | //conf.deleteConfig(); // Uncomment to remove old ini file and re-built it fron dictionary 72 | 73 | //Active seperator open/close, All others closed 74 | conf.setDisplayType(ConfigAssistDisplayType::AllOpen); 75 | // Append a javascript on main page 76 | conf.setSubScript(INIT_SCRIPT); 77 | // Register handlers for web server 78 | server.on("/", handleRoot); 79 | server.on("/d", []() { // Append dump handler 80 | conf.dump(&server); 81 | }); 82 | server.onNotFound(handleNotFound); // Append not found handler 83 | 84 | // Connect to any available network 85 | bool bConn = confHelper.connectToNetwork(15000, conf("led_buildin").toInt()); 86 | 87 | if(!bConn){ 88 | LOG_E("Connect failed.\n"); 89 | // Append config assist handlers to web server 90 | // and setup an ap for config to be edited 91 | conf.setup(server, true); 92 | }else{ 93 | // Add config portal handlers 94 | conf.setup(server, false); 95 | // Start mdns 96 | confHelper.startMDNS(); 97 | // Start web server 98 | server.begin(); 99 | LOG_I("HTTP server started\n"); 100 | LOG_I("Device started. Visit http://%s\n", WiFi.localIP().toString().c_str()); 101 | } 102 | } 103 | 104 | // App main loop 105 | void loop(void) { 106 | server.handleClient(); 107 | #if not defined(ESP32) 108 | MDNS.update(); 109 | #endif 110 | 111 | // Allow the cpu to switch to other tasks 112 | delay(2); 113 | } 114 | -------------------------------------------------------------------------------- /examples/ConfigAssist-VarAttributes/appPMem.h: -------------------------------------------------------------------------------- 1 | const char* VARIABLES_DEF_YAML PROGMEM = R"~( 2 | Wifi settings: 3 | - st_ssid: 4 | label: Name for WLAN 5 | - st_pass: 6 | label: Password for WLAN 7 | - host_name: 8 | label: >- 9 | Host name to use for MDNS and AP
{mac} will be replaced with device's mac id 10 | default: ConfigAssist_{mac} 11 | 12 | Application settings: 13 | - app_name: 14 | label: Name your application 15 | default: VarAttribues 16 | - led_buildin: 17 | label: Pin that the build in led is connected. Leave blank for auto. 18 | Usually pin 4 on esp32, 2 on esp8266 19 | attribs: "min='2' max='23' step='1'" 20 | - display_style: 21 | label: Choose how the config sections are displayed. 22 | Must reboot to apply 23 | options: AllOpen: 0 24 | - AllClosed: 1 25 | - Accordion : 2 26 | - AccordionToggleClosed : 3 27 | default: AccordionToggleClosed 28 | 29 | Batch upload settings: 30 | - batch_upload: 31 | label: >- 32 | Enable batch upload measurements 33 | Upload interval ⁚ 1.0 mins 34 | checked: True 35 | attribs: onClick="enableDisableBatchUpload()" 36 | 37 | - batch_size: 38 | label: "Measurements size. Change to calculate Upload interval" 39 | default: 5 40 | attribs: >- 41 | min='1' max='35535' step='1' 42 | onChange=" 43 | const ctrl = document.getElementById('update_interval'); 44 | const batch = document.getElementById('batch_upload'); 45 | var ui = 0; 46 | if(batch.checked) 47 | ui = ( (ctrl.value/1000) * this.value ) / 60; 48 | else 49 | ui = ( (ctrl.value/1000) * 1 ) / 60; 50 | document.getElementById('uploadInterval').innerHTML = ui.toFixed(2) + ' mins'; 51 | " 52 | - update_interval: 53 | label: "Delay between measurements (ms).Change to calculate Upload interval" 54 | default: 30000 55 | attribs: >- 56 | onChange=" 57 | const ctrl = document.getElementById('batch_size'); 58 | const batch = document.getElementById('batch_upload'); 59 | var ui = 0; 60 | if(batch.checked) 61 | ui = (ctrl.value * (this.value / 1000) ) / 60; 62 | else 63 | ui = (1 * (this.value / 1000) ) / 60; 64 | document.getElementById('uploadInterval').innerHTML = ui.toFixed(2) + ' mins';" 65 | " 66 | )~"; 67 | 68 | PROGMEM const char INIT_SCRIPT[] = R"=====( 69 | function enableDisableBatchUpload(){ 70 | if( document.getElementById('batch_upload').checked ){ 71 | document.getElementById('batch_size').disabled = false; 72 | }else{ 73 | document.getElementById('batch_size').disabled = true; 74 | } 75 | document.getElementById('batch_size').dispatchEvent(new Event('change')) 76 | } 77 | document.addEventListener('DOMContentLoaded', function (event) { 78 | enableDisableBatchUpload(); 79 | }); 80 | )====="; -------------------------------------------------------------------------------- /examples/ConfigAssist-WiFiAsync/ConfigAssist-WiFiAsync.ino: -------------------------------------------------------------------------------- 1 | #include // Config assist class 2 | #include // Config assist helper class 3 | 4 | #if defined(ESP32) 5 | WebServer server(80); 6 | #else 7 | ESP8266WebServer server(80); 8 | #endif 9 | 10 | #ifndef LED_BUILTIN 11 | #define LED_BUILTIN 22 12 | #endif 13 | 14 | #define INI_FILE "/ConfigAssistWiFiAsync.ini" 15 | 16 | unsigned long pingMillis = millis(); // Ping 17 | 18 | // Default application config dictionary 19 | // Modify the file with the params for you application 20 | // Then you can use then then by val = config[name]; 21 | const char* VARIABLES_DEF_YAML PROGMEM = R"~( 22 | Wifi settings: 23 | - st_ssid: 24 | label: Name for WLAN 25 | - st_pass: 26 | label: Password for WLAN 27 | - host_name: 28 | label: Host name to use for MDNS and AP 29 | {mac} will be replaced with device's mac id 30 | default: configAssist_{mac} 31 | 32 | Application settings: 33 | - app_name: 34 | label: Name your application 35 | default: ConfigAssistDemo 36 | - led_buildin: 37 | label: Enter the pin that the build in led is connected. 38 | Leave blank for auto. 39 | attribs: "min='2' max='23' step='1'" 40 | - debug: 41 | label: Debug application 42 | checked: true 43 | 44 | ConfigAssist settings: 45 | - display_style: 46 | label: Choose how the config sections are displayed. 47 | Must reboot to apply 48 | options: 49 | - AllOpen: 0 50 | - AllClosed: 1 51 | - Accordion : 2 52 | - AccordionToggleClosed : 3 53 | default: AccordionToggleClosed - 54 | )~"; 55 | 56 | ConfigAssist conf(INI_FILE, VARIABLES_DEF_YAML); // Config assist class 57 | ConfigAssistHelper confHelper(conf); // Define a ConfigAssist helper 58 | 59 | // Setup led 60 | String s = (conf("led_buildin") == "") ? conf["led_buildin"] = LED_BUILTIN : ""; 61 | 62 | // Print memory info 63 | void debugMemory(const char* caller) { 64 | #if defined(ESP32) 65 | LOG_I("%s > Free: heap %u, block: %u, pSRAM %u\n", caller, ESP.getFreeHeap(), heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL), ESP.getFreePsram()); 66 | #else 67 | LOG_I("%s > Free: heap %u\n", caller, ESP.getFreeHeap()); 68 | #endif 69 | LOG_I("%s > Wifi status: %i, ssid: %s, rssi %i\n", caller, (int)confHelper.getLedState(), WiFi.SSID().c_str(), WiFi.RSSI() ); 70 | } 71 | 72 | // Handler function for Home page 73 | void handleRoot() { 74 | String out("

Hello from {name}

"); 75 | out += "

Device time: " + conf.getLocalTime() +"

"; 76 | out += "Edit config"; 77 | conf["refresh_rate"] = conf("refresh_rate").toInt() + 1; 78 | #if defined(ESP32) 79 | out.replace("{name}", "ESP32"); 80 | #else 81 | out.replace("{name}", "ESP8266!"); 82 | #endif 83 | #if (CA_USE_TIMESYNC) 84 | out += ""; 85 | #endif 86 | server.send(200, "text/html", out); 87 | } 88 | 89 | // Handler for page not found 90 | void handleNotFound() { 91 | String message = "File Not Found\n\n"; 92 | message += "URI: "; 93 | message += server.uri(); 94 | message += "\nMethod: "; 95 | message += (server.method() == HTTP_GET) ? "GET" : "POST"; 96 | message += "\nArguments: "; 97 | message += server.args(); 98 | message += "\n"; 99 | for (uint8_t i = 0; i < server.args(); i++) { 100 | message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; 101 | } 102 | server.send(404, "text/plain", message); 103 | } 104 | 105 | // CallBack function when conf remotely changes 106 | void onDataChanged(String key){ 107 | LOG_I("Data changed: %s = %s \n", key.c_str(), conf(key).c_str()); 108 | if(key == "display_style") 109 | conf.setDisplayType((ConfigAssistDisplayType)conf("display_style").toInt()); 110 | 111 | } 112 | 113 | // Setup function 114 | void setup(void) { 115 | Serial.begin(115200); 116 | Serial.print("\n\n\n\n"); 117 | Serial.flush(); 118 | 119 | #if defined(CA_USE_LITTLEFS) 120 | LOG_I("Starting with LITTLEFS.. \n"); 121 | #else 122 | LOG_I("Starting with SPIFFS.. \n"); 123 | #endif 124 | 125 | debugMemory("setup"); 126 | 127 | //conf.deleteConfig(); // Uncomment to remove old ini file and re-built it fron dictionary 128 | 129 | // Register handlers for web server 130 | server.on("/", handleRoot); // Add root handler 131 | server.on("/d", []() { // Append dump handler 132 | conf.dump(&server); 133 | }); 134 | 135 | server.onNotFound(handleNotFound); // Append not found handler 136 | 137 | debugMemory("Loaded config"); 138 | 139 | // Will be called when portal is updating a key 140 | conf.setRemotUpdateCallback(onDataChanged); 141 | 142 | //WiFi.setAutoConnect(false); 143 | WiFi.setAutoReconnect(true); 144 | 145 | // Set the defined display type 146 | conf.setDisplayType((ConfigAssistDisplayType)conf("display_style").toInt()); 147 | 148 | // Set Wi-Fi credentials from config and start connection process 149 | confHelper.connectToNetworkAsync(15000, 13, [](ConfigAssistHelper::WiFiResult result, const String& msg) { 150 | switch (result) { 151 | case ConfigAssistHelper::WiFiResult::SUCCESS: 152 | LOG_D("Connected to Wi-Fi! IP: %s\n", msg.c_str()); 153 | confHelper.startMDNS(); 154 | conf.setupConfigPortalHandlers(server); 155 | server.begin(); 156 | LOG_D("HTTP server started\n"); 157 | LOG_I("Device started. Visit http://%s\n", WiFi.localIP().toString().c_str()); 158 | break; 159 | 160 | case ConfigAssistHelper::WiFiResult::INVALID_CREDENTIALS: 161 | LOG_D("Invalid credentials: %s\n", msg.c_str()); 162 | // Append config assist handlers to web server, 163 | // setup a minimal ap to edit config 164 | conf.setupConfigPortal(server, true); 165 | break; 166 | 167 | case ConfigAssistHelper::WiFiResult::CONNECTION_TIMEOUT: 168 | LOG_D("Connection fail: %s\n", msg.c_str()); 169 | conf.setupConfigPortal(server, true); 170 | break; 171 | 172 | case ConfigAssistHelper::WiFiResult::DISCONNECTION_ERROR: 173 | LOG_D("Disconnected: %s\n", msg.c_str()); 174 | // Will reconnect automatically 175 | //WiFi.setAutoReconnect(true); 176 | confHelper.setReconnect(true); 177 | // confHelper.retryConnection(20000); 178 | break; 179 | 180 | default: 181 | LOG_D("Unknown result: %s\n", msg.c_str()); 182 | break; 183 | } 184 | }); 185 | } 186 | 187 | // App main loop 188 | void loop(void) { 189 | server.handleClient(); 190 | #if not defined(ESP32) 191 | MDNS.update(); 192 | #endif 193 | 194 | // Run connection checks 195 | confHelper.loop(); 196 | 197 | // Display info 198 | if (millis() - pingMillis >= 10000){ 199 | // if debug is enabled in config display memory debug messages 200 | if(conf("debug").toInt()) debugMemory("Loop"); 201 | pingMillis = millis(); 202 | } 203 | 204 | // Allow the cpu to switch to other tasks 205 | delay(2); 206 | } 207 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | ########################################### 4 | # Syntax Coloring Map For ConfigAssist-library 5 | ########################################### 6 | 7 | ########################################### 8 | # Datatypes (KEYWORD1) 9 | ########################################### 10 | 11 | ConfigAssist KEYWORD1 12 | 13 | ########################################### 14 | # Methods and Functions (KEYWORD2) 15 | ########################################### 16 | init KEYWORD2 17 | handleFormRequest KEYWORD2 18 | loadDict KEYWORD2 19 | setup KEYWORD2 20 | loadConfigFile KEYWORD2 21 | streamText KEYWORD2 22 | get KEYWORD2 23 | put KEYWORD2 24 | add KEYWORD2 25 | getHostName KEYWORD2 26 | getLocalTime KEYWORD2 27 | streamHtmlElementChunk KEYWORD2 28 | setRemotUpdateCallback KEYWORD2 29 | getCSS KEYWORD2 30 | getTimeSyncScript KEYWORD2 31 | getMessageHtml KEYWORD2 32 | deleteConfig KEYWORD2 33 | handleWifiScanRequest KEYWORD2 34 | sendHtmlUploadPage KEYWORD2 35 | sendHtmlOtaUploadPage KEYWORD2 36 | setWiFiResultCallback KEYWORD2 37 | setupConfigPortal KEYWORD2 38 | setupConfigPortalHandlers KEYWORD2 39 | connectToNetwork KEYWORD2 40 | connectToNetworkAsync KEYWORD2 41 | startMDNS KEYWORD2 42 | syncTime KEYWORD2 43 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ConfigAssist", 3 | "keywords": "configuration, form, setup, ota, ESP32, ESP8266, responsive, captive portal, ini, variables, wifi, access point", 4 | "description": "A lightweight library for managing configuration settings on ESP32 and ESP8266 devices. It allows for handling configurations via web interfaces and supporting features like Wi-Fi credentials storage, NTP sync, OTA firmware updates, and more. The class can load configurations from various file systems (SPIFFS, LittleFS), supports YAML-style settings, and provides mechanisms to render configuration forms and handle user input.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/gemi254/ConfigAssist-ESP32-ESP8266.git" 8 | }, 9 | "version": "2.8.7", 10 | "homepage": "https://github.com/gemi254/ConfigAssist-ESP32-ESP8266", 11 | "examples": "examples/*/*.ino", 12 | "frameworks": "arduino", 13 | "platforms": [ 14 | "atmelavr", 15 | "espressif8266", 16 | "espressif32" 17 | ] 18 | } -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ConfigAssist 2 | version=2.8.7 3 | author=gemi254 4 | maintainer=gemi254 5 | sentence=A lightweight library allowing managing configuration settings on ESP32 and ESP8266 devices devices using a web portal. 6 | paragraph=It allows for handling configurations via web interfaces and supporting features like YAML-style definitions, Wi-Fi credentials storage, NTP sync, OTA firmware updates, and more. It can load/save configurations to the file system, and provides mechanisms to render configuration forms and handle user input 7 | category=Device Control 8 | url=https://github.com/gemi254/ConfigAssist-ESP32-ESP8266 9 | depends=Preferences 10 | includes=ConfigAssist.h 11 | architectures=* -------------------------------------------------------------------------------- /src/ConfigAssist.h: -------------------------------------------------------------------------------- 1 | #if !defined(_CONFIG_ASSIST_H) 2 | #define _CONFIG_ASSIST_H 3 | 4 | #include 5 | 6 | #include "dYaml.h" 7 | using namespace dyml; 8 | 9 | #include "WiFi.h" 10 | 11 | #if !defined(STORAGE) 12 | #if defined(ESP8266) || defined(CA_USE_LITTLEFS) 13 | #include 14 | #define STORAGE LittleFS // one of: SPIFFS LittleFS SD_MMC 15 | #else 16 | #include "SPIFFS.h" 17 | #define STORAGE SPIFFS // SPIFFS 18 | #endif 19 | #endif 20 | 21 | #if defined(ESP32) 22 | #include 23 | #include 24 | #else 25 | #include 26 | #include 27 | #endif 28 | 29 | #if !defined(LOGGER_LOG_LEVEL) 30 | #define LOGGER_LOG_LEVEL 3 // Set log level for this module 31 | #endif 32 | 33 | #define CA_CLASS_VERSION "2.8.8" // Class version 34 | #define CA_MAX_PARAMS 50 // Maximum parameters to handle 35 | #define CA_DEF_CONF_FILE "/config.ini" // Default Ini file to save configuration 36 | #define CA_INI_FILE_DELIM '~' // Ini file pairs seperator 37 | #define CA_FILENAME_IDENTIFIER "_F" // Keys ending with this is a text box with the filename of a text area 38 | #define CA_DONT_ALLOW_SPACES false // Allow spaces in var names ? 39 | #define CA_ST_SSID_KEY "st_ssid" // Vars ending with this key or key + Num defines a ssid field 40 | #define CA_ST_PASSWD_KEY "st_pass" // The key part that defines a password field 41 | #define CA_ST_STATICIP_KEY "st_ip" // The key part that defines a static ip field 42 | #define CA_AP_SSID_KEY "ap_ssid" // Vars ending with this key or key + Num defines a ssid field 43 | #define CA_AP_PASSWD_KEY "ap_pass" // The key part that defines a password field 44 | #define CA_AP_STATICIP_KEY "ap_ip" // The key part that defines a static ip field 45 | #define CA_NTPSYNC_KEY "ntp_server" // Vars ending with this key or key + Num define a ntp server 46 | 47 | #define CA_HOSTNAME_KEY "host_name" // The key that defines host name 48 | #define CA_TIMEZONE_KEY "time_zone" // The key that defines time zone for setting time 49 | #define CA_FIRMWVER_KEY "firmware_ver" // The key that defines the firmware version 50 | #define CA_FIRMWURL_KEY "firmware_url" // The key that defines the url with firmware information 51 | #if !defined(CA_USE_WIFISCAN) 52 | #define CA_USE_WIFISCAN 1 // Set to 0 to disable wifi scan 53 | #endif 54 | #if !defined(CA_USE_TESTWIFI) 55 | #define CA_USE_TESTWIFI 1 // Set to 0 to disable test wifi st connection 56 | #endif 57 | #if !defined(CA_USE_TIMESYNC) 58 | #define CA_USE_TIMESYNC 1 // Set to 0 to disable sync esp with browser if out of sync 59 | #endif 60 | #if !defined(CA_USE_OTAUPLOAD) 61 | #define CA_USE_OTAUPLOAD 1 // Set to 0 to disable ota and reduce memory 62 | #endif 63 | #if !defined(CA_USE_FIMRMCHECK) 64 | #define CA_USE_FIMRMCHECK 1 // Set to 0 to disable firmware check and upgrade from url 65 | #endif 66 | #if !defined(CA_USE_PERSIST_CON) 67 | #define CA_USE_PERSIST_CON 0 // Set to 1 to enable saving wifi credentials to nvs 68 | #endif 69 | 70 | 71 | // Define Platform libs 72 | #if defined(ESP32) 73 | #define WEB_SERVER WebServer 74 | #else 75 | #define WEB_SERVER ESP8266WebServer 76 | #define STORAGE LittleFS // one of: SPIFFS LittleFS SD_MMC 77 | #endif 78 | 79 | #include "espLogger.h" 80 | 81 | #define IS_BOOL_TRUE(x) (x=="On" || x=="on" || x=="True" || x=="true" || x=="1") 82 | 83 | #if (CA_USE_PERSIST_CON) 84 | #define CA_PREFERENCES_NS "ConfigAssist" // Name space for pererences 85 | #include 86 | #endif 87 | 88 | //Structure for config elements 89 | struct confPairs { 90 | String name; 91 | String value; 92 | String label; 93 | String attribs; 94 | String text; 95 | int readNo; 96 | byte type; 97 | }; 98 | 99 | // Define display types for the portal 100 | enum class ConfigAssistDisplayType:uint8_t { 101 | // All cards open 102 | AllOpen = 0, 103 | // All cards closed 104 | AllClosed, 105 | // Open clicked and close other cards 106 | Accordion, 107 | // Open/Close clicked and close other cards 108 | AccordionToggleClosed, 109 | }; 110 | 111 | //Seperators of config elements 112 | struct confSeperators { 113 | String name; 114 | String value; 115 | }; 116 | 117 | // Positions of keys inside array 118 | struct confNdx { 119 | String key; 120 | int ndx; 121 | }; 122 | 123 | // Call back function type 124 | typedef std::function ConfigAssistChangeCbf; 125 | 126 | // ConfigAssist class 127 | class ConfigAssist{ 128 | public: 129 | // Initialize with defaults 130 | ConfigAssist(); 131 | // Initialize with custom ini file, load default dict ? 132 | ConfigAssist(const String& ini_file, bool defaultYaml = false); 133 | // Initialize with custom Ini file, and custom yaml dict 134 | ConfigAssist(const String& ini_file, const char * dictStr); 135 | ~ConfigAssist(); 136 | public: 137 | // Load configs after storage is started 138 | void init(); 139 | // Set the portal dislay type. See ConfigAssistDisplayType 140 | void setDisplayType(ConfigAssistDisplayType display) { _displayType = display; } 141 | // Start storage if not init 142 | void startStorage(); 143 | // Set ini file at run time 144 | void setIniFile(const String& ini_file); 145 | // Set yaml at run time.. Must called before _init 146 | void setDictStr(const char * dictStr, bool load = false); 147 | // Add a script to main page 148 | void setSubScript(const char * initScript){ _subScript = initScript; } 149 | // Is config loaded valid ? 150 | bool valid(); 151 | // Is configuration file exists> 152 | bool confExists(); 153 | // Is key exists in configuration 154 | bool exists(String key); 155 | // Start an AP with a web server and render config values loaded from yaml dictionary 156 | void setup(WEB_SERVER& server, bool apEnable = false); 157 | void setupConfigPortal(WEB_SERVER& server, bool apEnable = false); 158 | // Setup an access point, settings from config or defaults 159 | bool setupAP(bool startMDNS = false); 160 | // Setup web servers handlers to handle config portal 161 | void setupConfigPortalHandlers(WEB_SERVER& server); 162 | // Add a global callback function to handle changes on form updates 163 | void setRemotUpdateCallback(ConfigAssistChangeCbf ev); 164 | // AP started ? 165 | bool isAPEnabled(); 166 | // Get a temponary hostname 167 | static String getMacID(); 168 | // Get a temponary hostname 169 | String getHostName(); 170 | // Get static IP settings from ipString (IP address, subnet mask, and gateway) 171 | bool getIPFromString(String ipStr, IPAddress &ip, IPAddress &mask, IPAddress &gw); 172 | // Implement Access operator () i.e. val = config('key') 173 | String operator () (const String &key); 174 | // Implement Modify operator [] i.e. config['key'] = val 175 | String& operator[] (const String& key); 176 | // Return the value of a given key, Empty on not found 177 | String get(const String &key); 178 | // Update the value of key = val (int) 179 | bool put(const String &key, int val, bool force = false); 180 | // Update the value of key = val (string) 181 | bool put(const String &key, String val, bool force = false); 182 | // Clear all elements 183 | void clear(); 184 | private: 185 | // Add vectors by key (name in confPairs) 186 | int add(const String &key, const String &val, bool force = false); 187 | // Add unique name vectors pairs 188 | int add(confPairs &c); 189 | // Add seperator by key 190 | void addSeperator(const String &key, const String &val); 191 | // Rebuild keys indexes wher sorting by readNo 192 | void rebuildKeysNdx(); 193 | // Sort keys asc by name 194 | void sortKeysNdx(); 195 | // Sort keys in yaml definition read order 196 | void sortReadOrder(); 197 | // Sort seperator vectors by key (name in confSeperators) 198 | void sortSeperators(); 199 | public: 200 | // Return next key and val from configs on each call in key order 201 | bool getNextKeyVal(confPairs &c, bool reset = false); 202 | // Display config items in web server, yaml like 203 | void dumpYaml(WEB_SERVER *server = NULL); 204 | // Display config items in web server, or on log on NULL 205 | void dump(WEB_SERVER *server = NULL); 206 | // Load a description file. On updateProps = true update only additional pair info 207 | String loadDict(const char *dictStr, bool updateProps = false); 208 | // Build config file from dict 209 | void buildConfigFile(); 210 | // Load config pairs from an ini file 211 | bool loadConfigFile(String filename = ""); 212 | void loadConfig(); 213 | // Delete configuration files 214 | bool deleteConfig(String filename = ""); 215 | // Save configs vectors in an ini file 216 | bool saveConfigFile(String filename = ""); 217 | // Get system local time 218 | String getLocalTime(); 219 | // Compare browser with system time and correct if needed 220 | void checkTime(uint32_t timeUtc, int timeOffs); 221 | // Respond a HTTP request for /scan results 222 | void handleWifiScanRequest(); 223 | // Send html upload page to client 224 | void sendHtmlUploadPage(); 225 | #if (CA_USE_OTAUPLOAD) 226 | // Send html ota upload page to client 227 | void sendHtmlOtaUploadPage(); 228 | #endif 229 | #if (CA_USE_FIMRMCHECK) 230 | // Send html firmware check page to client 231 | void sendHtmlFirmwareCheckPage(); 232 | #endif 233 | // Upload a file to SPIFFS 234 | void handleFileUpload(); 235 | // Test the specified connection no 236 | String testWiFiSTConnection(String no); 237 | // Download a file in browser window 238 | void handleDownloadFile(const String &fileName); 239 | // Respond a not found HTTP request 240 | void handleNotFound(); 241 | // Respond a HTTP request for the form use the CONF_FILE 242 | // to save. Save, Reboot ESP, Reset to defaults, cancel edits 243 | void handleFormRequest(WEB_SERVER* server); 244 | // Send edit html to client 245 | void sendHtmlEditPage(WEB_SERVER* server); 246 | // Get edit page html table (no form) 247 | //String getEditHtml(); 248 | // Get page css 249 | String getCSS(); 250 | // Get browser time synchronization java script 251 | #if (CA_USE_TIMESYNC) 252 | String getTimeSyncScript(); 253 | #endif 254 | // Get html custom message page 255 | String getMessageHtml(); 256 | // Name ends with key + number? 257 | bool endsWith(const String &name, const String &key, String& no); 258 | private: 259 | // Split a String with delimeter, index -> itemNo 260 | String splitString(const String &s, char delim, int index); 261 | // Get child nodes as string 262 | inline String getChilds(dyml::Directyaml::Node &node); 263 | // Get all child nodes as tree string, on option key add key & val 264 | String getChildsTree(dyml::Directyaml::Node node, bool keyVal = false); 265 | // Is string numeric 266 | bool isNumeric(String s); 267 | // Decode given string, replace encoded characters 268 | String urlDecode(const String& text); 269 | // Load a file into a string 270 | bool loadText(const String &fPath, String& txt); 271 | // Write a string to a file 272 | bool saveText(const String &fPath, String& txt); 273 | // Stream text files to web server (long files) 274 | bool streamText(const String &fPath, WEB_SERVER &server); 275 | 276 | #if (CA_USE_PERSIST_CON) 277 | // Clear nvs 278 | bool clearPrefs(); 279 | // Save a key from nvs 280 | bool savePref(const String &key, const String &val); 281 | // Load a key from nvs 282 | bool loadPref(const String &key, String &val); 283 | #endif 284 | // Implement seperators mode 285 | void modifySeperator(int sepNo, String &outSep); 286 | // Render keys, values to html lines 287 | bool streamHtmlElementChunk(WEB_SERVER &server); 288 | // Render range 289 | String getRangeHtml(String defVal, String attribs); 290 | // Render options list 291 | String getOptionsListHtml(String defVal, String attribs, bool isDataList = false); 292 | // Get location of given key to retrieve other elements 293 | int getKeyPos(String key); 294 | // Get seperation location of given key 295 | int getSepKeyPos(String key); 296 | // Extract a config tokens from keyValPair and load it into configs vector 297 | void loadVectItem(String keyValPair); 298 | #if (CA_USE_WIFISCAN) 299 | // Build json on Wifi scan complete 300 | static void scanComplete(int networksFound); 301 | // Send wifi scan results to client 302 | static void checkScanRes(); 303 | // Start async wifi scan 304 | static void startScanWifi(); 305 | #endif 306 | private: 307 | enum input_types { TEXT_BOX=1, TEXT_AREA=2, CHECK_BOX=3, OPTION_BOX=4, RANGE_BOX=5, COMBO_BOX=6}; 308 | std::vector _configs; 309 | std::vector _keysNdx; 310 | std::vector _seperators; 311 | int _readNo; 312 | int _forcedNo ; 313 | uint8_t _row; 314 | bool _init; 315 | bool _iniLoaded; 316 | bool _dictLoaded; 317 | bool _dirty; 318 | const char * _dictStr; 319 | const char * _subScript; 320 | String _confFile; 321 | static WEB_SERVER * _server; 322 | static String _jWifi; 323 | bool _apEnabled; 324 | ConfigAssistDisplayType _displayType; 325 | ConfigAssistChangeCbf _changeCbf; 326 | static bool _storageStarted; 327 | #if (CA_USE_PERSIST_CON) 328 | static Preferences _prefs; 329 | #endif 330 | }; 331 | #endif // _CONFIG_ASSIST_H 332 | -------------------------------------------------------------------------------- /src/ConfigAssistHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "ConfigAssistHelper.h" 2 | #if defined(ESP32) 3 | #include 4 | #include 5 | #else 6 | #include 7 | #include 8 | #endif 9 | 10 | // Constructor: Initializes ConfigAssistHelper with a reference to ConfigAssist 11 | ConfigAssistHelper::ConfigAssistHelper(ConfigAssist& conf): 12 | _conf(conf), 13 | _ledPin(0), 14 | _ledState(LEDState::OFF), 15 | _reconnect(false), 16 | _waitForResult(false), 17 | _timeOld(0), 18 | _connectTimeout(10000) 19 | { 20 | _resultCallback = nullptr; 21 | } 22 | // Destructor 23 | ConfigAssistHelper::~ConfigAssistHelper() {} 24 | 25 | // Set the environment time zone 26 | void ConfigAssistHelper::setEnvTimeZone(const char* tz) { 27 | // If the time zone is not defined, set it to "GMT0" 28 | if (tz[0] == '\0') { 29 | LOG_E("Tz undefined, set `GMT0`.\n"); 30 | setenv("TZ", "GMT0", 1); 31 | return; 32 | } 33 | // Set the time zone only if it's different from the current one 34 | if(getenv("TZ") && strcmp(getenv("TZ"), tz) == 0) return; 35 | LOG_D("Set tz: %s\n", tz); 36 | setenv("TZ", tz, 1); 37 | tzset(); 38 | } 39 | 40 | // Set the time zone using the configuration file's time zone key 41 | void ConfigAssistHelper::setEnvTimeZone() { 42 | setEnvTimeZone(_conf(CA_TIMEZONE_KEY).c_str()); 43 | } 44 | 45 | // Synchronize time asynchronously with a specified timeout 46 | void ConfigAssistHelper::syncTimeAsync(uint32_t syncTimeout, bool force) { 47 | syncTime(syncTimeout, force, true); 48 | } 49 | 50 | // Synchronize time with optional forced sync and asynchronous option 51 | void ConfigAssistHelper::syncTime(uint32_t syncTimeout, bool force, bool async) { 52 | // If not connected to Wi-Fi, return early 53 | if (WiFi.status() != WL_CONNECTED) { 54 | LOG_E("Sync time fail. Not conn\n"); 55 | return; 56 | } 57 | 58 | // Get the time zone from the config, or default to GMT0 59 | String tzString = _conf(CA_TIMEZONE_KEY); 60 | if (tzString == "") { 61 | LOG_E("Time zone key '%s' not in config!\n", CA_TIMEZONE_KEY); 62 | tzString = "GMT0"; 63 | } 64 | 65 | // Set the time zone based on the configuration or the default 66 | setEnvTimeZone(tzString.c_str()); 67 | 68 | // Retrieve the NTP servers from the configuration 69 | String ntpServers[3] = {"", "", ""}; 70 | confPairs c; 71 | int i = 0; 72 | _conf.getNextKeyVal(c, true); 73 | while (_conf.getNextKeyVal(c)) { 74 | String no; 75 | if (_conf.endsWith(c.name, CA_NTPSYNC_KEY, no)) { 76 | ntpServers[i] = _conf(c.name); 77 | if (++i >= 3) break; 78 | } 79 | } 80 | 81 | // If no NTP servers are found, return 82 | if(i == 0) { 83 | LOG_E("No Ntp servers in config\n"); 84 | return; 85 | } 86 | 87 | // Reset the clock if forced sync is enabled 88 | if(force) { 89 | _timeOld = time(nullptr); 90 | resetClock(); 91 | } 92 | 93 | // Synchronize time using the NTP servers 94 | LOG_D("Sync time, NTP servers: %s, %s, %s\n", ntpServers[0].c_str(), ntpServers[1].c_str(), ntpServers[2].c_str()); 95 | configTzTime(tzString.c_str(), ntpServers[0].c_str(), ntpServers[1].c_str(), ntpServers[2].c_str()); 96 | 97 | // Start a timer for the synchronization process 98 | time_t start = millis(); 99 | if(!async) waitForTimeSync(syncTimeout); 100 | 101 | // If the time was forced to sync, restore the clock if necessary 102 | if (force && !async && !isTimeSync()) { 103 | time_t elapsed = (millis() - start); 104 | restoreClock(elapsed); 105 | } 106 | 107 | // Log the duration of the time synchronization 108 | LOG_V("Time sync ended ms: %lu\n", (unsigned long)(millis() - start)); 109 | } 110 | 111 | // Check if the system time is synchronized 112 | bool ConfigAssistHelper::isTimeSync() { 113 | return time(nullptr) > 1000000000l; 114 | } 115 | 116 | // Wait for time synchronization with a timeout 117 | void ConfigAssistHelper::waitForTimeSync(uint32_t syncTimeout) { 118 | if(syncTimeout == 0) syncTimeout = _conf("sync_timeout").toInt(); 119 | if(syncTimeout == 0) syncTimeout = 15000; 120 | LOG_D("Waiting for time sync timeout: %i..\n", syncTimeout); 121 | uint32_t startAttemptTime = millis(); 122 | while (!isTimeSync() && millis() - startAttemptTime < syncTimeout) { 123 | printStatus(); 124 | delay(100); 125 | } 126 | printStatus(true); 127 | #if LOGGER_LOG_LEVEL > 3 128 | time_t tnow = time(nullptr); 129 | LOG_D("Wait sync: %i, time: %s", isTimeSync(), ctime(&tnow)); 130 | #endif 131 | } 132 | 133 | // Validate the Wi-Fi configuration in the config file 134 | bool ConfigAssistHelper::validateWiFiConfig() { 135 | confPairs c; 136 | _conf.getNextKeyVal(c, true); 137 | 138 | String ssid, password, ip; 139 | while(getStSSID(ssid, password, ip)) { 140 | if (!ssid.isEmpty()) break; 141 | } 142 | _conf.getNextKeyVal(c, true); 143 | 144 | return ssid != ""; 145 | } 146 | 147 | // Set a callback function for handling Wi-Fi result 148 | void ConfigAssistHelper::setWiFiResultCallback(WiFiResultCallback callback) { 149 | _resultCallback = callback; 150 | } 151 | 152 | // Set the LED pin for indicating connection status 153 | void ConfigAssistHelper::setLedPin(uint8_t pin) { 154 | _ledPin = pin; 155 | if(_ledPin > 0) pinMode(_ledPin, OUTPUT); 156 | } 157 | 158 | // Set the timeout for Wi-Fi connection 159 | void ConfigAssistHelper::setConnectionTimeout(uint32_t connectTimeout) { 160 | if(connectTimeout == 0) connectTimeout = _conf("connect_timeout").toInt() * 1000L; 161 | if(connectTimeout == 0) connectTimeout = 15000; 162 | LOG_D("Set conn timeout: %i\n", connectTimeout); 163 | _connectTimeout = connectTimeout; 164 | } 165 | 166 | // Enable or disable automatic reconnection to Wi-Fi 167 | void ConfigAssistHelper::setReconnect(bool reconnect) { 168 | _reconnect = reconnect; 169 | } 170 | 171 | // Get the current LED state (e.g., OFF, CONNECTING, CONNECTED) 172 | ConfigAssistHelper::LEDState ConfigAssistHelper::getLedState() { 173 | return _ledState; 174 | } 175 | 176 | // Reset clock to 0 177 | void ConfigAssistHelper::resetClock(){ 178 | struct timeval tv; 179 | tv.tv_sec = 0; 180 | tv.tv_usec = 0; 181 | // Reset clock 182 | settimeofday(&tv, nullptr); 183 | } 184 | // Restore clock to _timeOld + elapsed ms 185 | void ConfigAssistHelper::restoreClock(time_t elapsed){ 186 | struct timeval tv; 187 | tv.tv_sec = _timeOld + (elapsed) / 1000; 188 | tv.tv_usec = 0; 189 | settimeofday(&tv, nullptr); 190 | LOG_W("Restoring old clock time.\n"); 191 | } 192 | // Find wifi credentials from config. 193 | // Return true if found 194 | bool ConfigAssistHelper::getStSSID(String& ssid, String& pass, String& ip) { 195 | // Finds st_ssid1, Second call st_ssid2 etc. 196 | while (_conf.getNextKeyVal(_curSSID)) { 197 | String no; 198 | if (_conf.endsWith(_curSSID.name, CA_ST_SSID_KEY, no)) { 199 | if (_curSSID.value == "") continue; 200 | ssid = _curSSID.value; 201 | String key; 202 | key = CA_ST_PASSWD_KEY + no; 203 | pass = _conf(key); 204 | key = CA_ST_STATICIP_KEY + no; 205 | ip = _conf(key); 206 | return true; 207 | } 208 | } 209 | // Reset configs 210 | _conf.getNextKeyVal(_curSSID, true); 211 | ssid = ""; 212 | pass = ""; 213 | return false; 214 | } 215 | 216 | void ConfigAssistHelper::serialPrint(char ch){ 217 | #if defined(LOGGER_LOG_LEVEL) 218 | #if LOGGER_LOG_LEVEL > 2 219 | Serial.print(ch); 220 | #endif 221 | #else 222 | Serial.print(ch); 223 | #endif 224 | } 225 | 226 | // Flash led and print dot on serial to indicate connection 227 | void ConfigAssistHelper::printStatus(bool end) { 228 | static uint32_t lastPrintTime = 0; 229 | static int col = 0; 230 | 231 | if (end) { 232 | if (col > 0) serialPrint('\n'); 233 | col = 0; 234 | lastPrintTime = 0; 235 | return; 236 | } 237 | 238 | updateLED(); 239 | 240 | if (millis() - lastPrintTime >= 1000) { 241 | serialPrint('.'); 242 | lastPrintTime = millis(); 243 | if (++col >= 60) { 244 | col = 0; 245 | Serial.println(); 246 | } 247 | } 248 | } 249 | // Main loop to manage Wi-Fi connection and LED state updates 250 | void ConfigAssistHelper::loop() { 251 | if (_waitForResult){ 252 | waitForResult(); 253 | } else { 254 | updateLED(); 255 | // Dont check on TIMEOUT, CONNECTING 256 | if (_ledState == LEDState::CONNECTED || _ledState == LEDState::DISCONNECTED) 257 | checkConnection(); 258 | } 259 | 260 | #if defined(ESP8266) 261 | if(MDNS.isRunning()) MDNS.update(); // Handle MDNS updates for ESP8266 262 | #endif 263 | } 264 | 265 | // Flush led according to state 266 | void ConfigAssistHelper::updateLED() { 267 | if (_ledPin == 0) return; 268 | //Used to avoid redundant operations when the LED state remains unchanged. 269 | //static LEDState ledState = LEDState::OFF; 270 | static bool pinStateOld = 0; 271 | 272 | uint32_t currentTime = millis(); 273 | bool pinState = HIGH; 274 | 275 | switch (_ledState) { 276 | case LEDState::OFF: 277 | pinState = HIGH; 278 | break; 279 | case LEDState::CONNECTING: // off on 280 | pinState = (currentTime % 1000 < 500) ? LOW : HIGH; 281 | break; 282 | case LEDState::CONNECTED: 283 | pinState = HIGH; 284 | break; 285 | case LEDState::DISCONNECTED: 286 | pinState = (currentTime % 2000 < 1000) ? LOW : HIGH; 287 | break; 288 | case LEDState::TIMEOUT: 289 | pinState = (currentTime % 500 < 250) ? LOW : HIGH; 290 | break; 291 | } 292 | 293 | if(pinState == pinStateOld) return; 294 | //LOG_D("Led pin:%i, state: %i, pin: %i\n", _ledPin, (int)_ledState, pinState ); 295 | digitalWrite(_ledPin, pinState); 296 | pinStateOld = pinState; 297 | } 298 | 299 | // Wait for a connection result 300 | // Called from loop to wait a connection 301 | void ConfigAssistHelper::waitForResult(){ 302 | if (!WiFi.isConnected() && millis() - _startAttemptTime < _connectTimeout) { 303 | // Optionally, add visual feedback or retries 304 | printStatus(); 305 | } else { // Connect or timeout 306 | printStatus(true); 307 | if (WiFi.isConnected()) { 308 | LOG_I("Wifi connected. ip: %s\n", WiFi.localIP().toString().c_str()); 309 | _ledState = LEDState::CONNECTED; 310 | _waitForResult = false; 311 | if (_resultCallback) _resultCallback(WiFiResult::SUCCESS, WiFi.localIP().toString()); 312 | } else { // Disconected 313 | LOG_E("Conn timeout result\n"); 314 | // Next connection if any 315 | if(!connectWiFi(true)){ 316 | _waitForResult = false ; 317 | _ledState = LEDState::TIMEOUT; 318 | if (_resultCallback) _resultCallback(WiFiResult::CONNECTION_TIMEOUT, "Timeout result."); 319 | } 320 | } 321 | } 322 | } 323 | // Wait until connection or timout. Flash led and print dots 324 | void ConfigAssistHelper::waitForConnection(uint32_t connectTimeout) { 325 | setConnectionTimeout(connectTimeout); 326 | uint32_t startAttemptTime = millis(); 327 | 328 | while (!WiFi.isConnected() && millis() - startAttemptTime < connectTimeout) { 329 | printStatus(); 330 | delay(100); 331 | } 332 | printStatus(true); 333 | } 334 | // Check WiFi connection status and send event 335 | // If using AP (Access Point) mode or AP_STA mode, setAutoReconnect() 336 | // might not work as intended. 337 | void ConfigAssistHelper::checkConnection() 338 | { 339 | if ( millis() - _checkConnectionTime > 5000) { 340 | if(!WiFi.isConnected() && _ledState != LEDState::DISCONNECTED) { 341 | //WiFi.reconnect(); 342 | _ledState = LEDState::DISCONNECTED; 343 | if (_resultCallback) _resultCallback(WiFiResult::DISCONNECTION_ERROR, "Disconnection."); 344 | LOG_D("Reconnecting Wifi...\n"); 345 | 346 | #if defined(ESP8266) 347 | if( _reconnect && wifi_station_disconnect()) { 348 | wifi_station_connect(); 349 | } 350 | #else 351 | if(_reconnect) WiFi.setAutoReconnect(true); 352 | #endif 353 | 354 | if( _conf("conn_failover").toInt()){ 355 | // Try next connection 356 | connectWiFi(true); 357 | } 358 | 359 | }else if(WiFi.isConnected() && _ledState != LEDState::CONNECTED) { // Was not connected and reconn 360 | _ledState = LEDState::CONNECTED; 361 | if (_resultCallback) _resultCallback(WiFiResult::SUCCESS, WiFi.localIP().toString()); 362 | } 363 | 364 | LOG_V("Checking connection Wifimode: %i, ledstate: %i\n", (int)WiFi.getMode(), (int)_ledState); 365 | _checkConnectionTime = millis(); 366 | } 367 | } 368 | // Connect to the Wi-Fi network and manage connection timeout and LED state 369 | bool ConfigAssistHelper::connectToNetwork(uint32_t connectTimeout, const uint8_t ledPin, const bool async) { 370 | if(WiFi.isConnected()) return true; // Return if already connected 371 | if (!validateWiFiConfig()) { 372 | // If Wi-Fi configuration is invalid, invoke the callback with an error 373 | if (_resultCallback) { 374 | _resultCallback(WiFiResult::INVALID_CREDENTIALS, "Invalid Wi-Fi configuration."); 375 | } else { 376 | LOG_E("Invalid Wi-Fi configuration.\n"); 377 | } 378 | return false; 379 | } 380 | // Set LED pin for indicating connection status 381 | setLedPin(ledPin); 382 | setConnectionTimeout(connectTimeout); 383 | return connectWiFi(async); // Attempt to connect to Wi-Fi 384 | } 385 | 386 | // Connect to the network asynchronously and use the callback for results 387 | void ConfigAssistHelper::connectToNetworkAsync(uint32_t connectTimeout, const uint8_t ledPin, WiFiResultCallback callback) { 388 | if(callback) setWiFiResultCallback(callback); 389 | if(connectToNetwork(connectTimeout, ledPin, true)) 390 | _waitForResult = true; // Set flag to wait for connection result 391 | } 392 | 393 | // Helper method for handling Wi-Fi connection 394 | void ConfigAssistHelper::beginWiFiConnection(const String& ssid, const String& password, const String& ip) { 395 | LOG_I("Connecting to WiFi: %s\n", ssid.c_str()); 396 | LOG_D("Wifi pass: %s\n", password.c_str() ); 397 | if (ip != "") { 398 | IPAddress ipAddr, mask, gw; 399 | if (_conf.getIPFromString(ip, ipAddr, mask, gw)) { 400 | WiFi.config(ipAddr, gw, mask); 401 | LOG_D("Wifi ip: %s\n", ip.c_str() ); 402 | } 403 | } 404 | WiFi.disconnect(); 405 | if(WiFi.getMode() == WIFI_AP) WiFi.mode(WIFI_AP_STA); 406 | else WiFi.mode(WIFI_STA); 407 | WiFi.begin(ssid.c_str(), password.c_str()); 408 | } 409 | 410 | // Connect to next wifi ssid 411 | // Returns WiFi.isConnected(), on async always true 412 | bool ConfigAssistHelper::connectWiFi(bool async){ 413 | if(WiFi.isConnected()) return true; 414 | String st_ssid, st_pass, st_ip; 415 | // Get next not empty ssid 416 | if (getStSSID(st_ssid, st_pass, st_ip)) { 417 | // Begin wifi connection 418 | beginWiFiConnection(st_ssid, st_pass, st_ip); 419 | _startAttemptTime = millis(); 420 | _ledState = LEDState::CONNECTING; 421 | if (async) { 422 | // Unblock loop 423 | _waitForResult = true; 424 | // Check for connection on loop 425 | return true; 426 | } else { // Block mode, wait or timeout 427 | // Block loop 428 | _waitForResult = false; 429 | // Wait connection or timeout 430 | waitForConnection(_connectTimeout); 431 | if(WiFi.isConnected()){ 432 | LOG_I("Wifi conn ip: %s\n", WiFi.localIP().toString().c_str()); 433 | _ledState = LEDState::CONNECTED; 434 | if (_resultCallback) _resultCallback(WiFiResult::SUCCESS, WiFi.localIP().toString()); 435 | }else{ 436 | LOG_E("Conn timeout async: %i\n", async); 437 | _ledState = LEDState::TIMEOUT; 438 | _waitForResult = false; 439 | if (_resultCallback) _resultCallback(WiFiResult::CONNECTION_TIMEOUT, "Timeout connecting."); 440 | // Next connection if any 441 | if(!connectWiFi(async)){ 442 | return false; 443 | } 444 | } 445 | updateLED(); 446 | return WiFi.isConnected(); 447 | } 448 | }else{ // Available ssids end 449 | LOG_D("No more SSIDS, failover: %s\n", _conf("conn_failover").c_str() ); 450 | if( _conf("conn_failover").toInt()){ 451 | LOG_D("Starting over..\n"); 452 | // Reset configs 453 | _conf.getNextKeyVal(_curSSID, true); 454 | // Try from the beggining 455 | return connectWiFi(async); 456 | } 457 | } 458 | return false; 459 | } 460 | 461 | // Start mDNS (Multicast DNS) for the device to enable local network discovery 462 | bool ConfigAssistHelper::startMDNS() { 463 | if(MDNS.hostname(0) != "") { 464 | LOG_E("MDNS already running: %s\n", MDNS.hostname(0).c_str()); 465 | return false; 466 | } 467 | 468 | String host = _conf(CA_HOSTNAME_KEY); 469 | if(host == "") host = _conf.getHostName(); 470 | 471 | // Start mDNS with the given hostname 472 | bool ret = MDNS.begin(host.c_str()); 473 | if(ret) { 474 | LOG_D("MDNS started, host: %s\n", host.c_str()); 475 | } else { 476 | LOG_E("MDNS failed to start, host: %s\n", host.c_str()); 477 | } 478 | return ret; 479 | } 480 | -------------------------------------------------------------------------------- /src/ConfigAssistHelper.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIGASSISTHELPER_H 2 | #define CONFIGASSISTHELPER_H 3 | 4 | // Include necessary libraries depending on the platform 5 | #include "ConfigAssist.h" 6 | #if defined(ESP32) 7 | #include 8 | #else 9 | #include 10 | #endif 11 | #include 12 | #include 13 | 14 | // ConfigAssistHelper class declaration 15 | class ConfigAssistHelper { 16 | public: 17 | // Enum for Wi-Fi connection result 18 | enum class WiFiResult { 19 | SUCCESS, // Successful connection 20 | CONNECTION_TIMEOUT, // Connection timeout error 21 | DISCONNECTION_ERROR, // Disconnection error 22 | NTP_SYNC_FAILED, // NTP synchronization failed 23 | INVALID_CREDENTIALS // Invalid Wi-Fi credentials 24 | }; 25 | 26 | // Enum to track the LED state 27 | enum class LEDState { 28 | OFF, // LED is off 29 | CONNECTING, // LED is blinking while connecting 30 | CONNECTED, // LED is on after successful connection 31 | DISCONNECTED, // LED is on after disconnection 32 | TIMEOUT // LED shows timeout indication 33 | }; 34 | 35 | // Define callback type for Wi-Fi connection result 36 | using WiFiResultCallback = std::function; 37 | 38 | // Constructor that initializes the class with a ConfigAssist instance 39 | ConfigAssistHelper(ConfigAssist& conf); 40 | // Destructor 41 | ~ConfigAssistHelper(); 42 | 43 | // Set the time zone for the system 44 | void setEnvTimeZone(const char* tz); 45 | // Set the time zone from the configuration file 46 | void setEnvTimeZone(); 47 | // Synchronize time asynchronously with a timeout 48 | void syncTimeAsync(uint32_t syncTimeout = 0, bool force = false); 49 | // Synchronize time synchronously with a timeout 50 | void syncTime(uint32_t syncTimeout = 0, bool force = false, bool async = false); 51 | // Check if the time is synchronized 52 | bool isTimeSync(); 53 | // Wait for time synchronization with a timeout 54 | // if syncTimeout == 0, default sync_timeout from config or 15000 55 | void waitForTimeSync(uint32_t syncTimeout = 0); 56 | // Validate the Wi-Fi configuration in the configuration file 57 | bool validateWiFiConfig(); 58 | // Set a callback function to handle Wi-Fi result 59 | void setWiFiResultCallback(WiFiResultCallback callback); 60 | // Set the pin number for the LED indicator 61 | void setLedPin(uint8_t pin); 62 | // Set the connection timeout for Wi-Fi 63 | void setConnectionTimeout(uint32_t connectTimeout); 64 | // Set whether to reconnect to Wi-Fi automatically 65 | void setReconnect(bool reconnect); 66 | // Get the current LED state 67 | LEDState getLedState(); 68 | // Run the main loop to manage Wi-Fi connection and LED states 69 | void loop(); 70 | // Connect to the Wi-Fi network if connectTimeout == 0 default connect_timeout from config or 15000 71 | bool connectToNetwork(uint32_t connectTimeout = 0, const uint8_t ledPin = 0, const bool async = false); 72 | // Connect to the network asynchronously 73 | void connectToNetworkAsync(uint32_t connectTimeout = 0, const uint8_t ledPin = 0, WiFiResultCallback callback = nullptr); 74 | // Start mDNS (Multicast DNS) for the device 75 | bool startMDNS(); 76 | 77 | private: 78 | // Reset the system clock 79 | void resetClock(); 80 | // Restore the system clock after a time synchronization attempt 81 | void restoreClock(time_t elapsed); 82 | // Get SSID, password, and IP from the configuration 83 | bool getStSSID(String& ssid, String& pass, String& ip); 84 | // Print a character to the serial monitor 85 | void serialPrint(char ch = '.'); 86 | // Print the current status to the serial monitor 87 | void printStatus(bool end = false); 88 | // Update the LED state based on the current connection status 89 | void updateLED(); 90 | // Begin Wi-Fi connection with SSID, password, and IP 91 | void beginWiFiConnection(const String& ssid, const String& password, const String& ip); 92 | // Attempt to connect to Wi-Fi 93 | bool connectWiFi(bool async); 94 | // Wait for a result from the connection process 95 | void waitForResult(); 96 | // Wait for the connection to complete with a timeout 97 | void waitForConnection(uint32_t connectTimeout = 0); 98 | // Check if the device is still connected to Wi-Fi 99 | // Will try to reconnect or swich to another connection 100 | void checkConnection(); 101 | 102 | ConfigAssist& _conf; // Reference to the ConfigAssist instance 103 | confPairs _curSSID; 104 | uint8_t _ledPin; // Pin for the LED 105 | LEDState _ledState; // Current LED state 106 | bool _reconnect; // Flag to indicate whether to reconnect automatically 107 | bool _waitForResult; // Flag to wait for the result of the Wi-Fi connection 108 | time_t _timeOld; // Previous time value 109 | uint32_t _connectTimeout; // Timeout for Wi-Fi connection 110 | uint32_t _startAttemptTime; // Time when the connection attempt started 111 | uint32_t _checkConnectionTime; // Time to check connection status 112 | WiFiResultCallback _resultCallback; // Callback for Wi-Fi connection result 113 | }; 114 | 115 | #endif // CONFIGASSISTHELPER_H 116 | -------------------------------------------------------------------------------- /src/configAssistPMem.h: -------------------------------------------------------------------------------- 1 | // Minimal application config dictionary 2 | const char* CA_DEFAULT_DICT_JSON PROGMEM = R"=====( 3 | Wifi settings: 4 | - st_ssid: 5 | label: Name for WLAN 6 | default: 7 | - st_pass: 8 | label: Password for WLAN 9 | default: 10 | - host_name: 11 | label: >- 12 | Host name to use for MDNS and AP 13 | {mac} will be replaced with device's mac id 14 | default: configAssist_{mac} 15 | )====="; 16 | // Template for message page 17 | PROGMEM const char CONFIGASSIST_HTML_START[] = R"=====( 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | {title} 26 | 27 | )====="; 28 | 29 | // Template for message page 30 | PROGMEM const char CONFIGASSIST_HTML_MESSAGE[] = R"=====( 31 | 49 | 50 | 51 |

{msg}

52 | 53 | )====="; 54 | 55 | // Template for uploading a file 56 | PROGMEM const char CONFIGASSIST_HTML_UPLOAD[] = R"=====( 57 | 58 |

Select a config file to upload to the device

59 |
60 |
61 |
62 |


63 | [ Back ]

64 |
65 | 66 | 67 | )====="; 68 | #if (CA_USE_OTAUPLOAD) 69 | // Template for uploading a ota file 70 | const char* CONFIGASSIST_HTML_OTAUPLOAD = R"=====( 71 | 126 | 127 |

Select a firmware to upload to the device

128 |
129 | 130 | 131 |

132 |
133 |

134 |

135 | [ Back ]

136 | 137 |
138 | 139 | 140 | )====="; 141 | #endif 142 | 143 | #if (CA_USE_FIMRMCHECK) 144 | // Template for checking firmware 145 | PROGMEM const char CONFIGASSIST_HTML_FIRMW_CHECK[] = R"=====( 146 | 252 | 253 |
254 |

Your current firmware version is: {FIRMWARE_VERSION}

255 |
256 |
257 |

258 | 259 |

260 | 261 |

262 | 263 |

264 |

265 |
266 | [ Back ]

267 |
268 | 269 | 270 | )====="; 271 | #endif 272 | 273 | // Template for header, begin of the config form 274 | PROGMEM const char CONFIGASSIST_HTML_CSS[] =R"=====( 275 | )====="; 453 | // Html controls 454 | PROGMEM const char CONFIGASSIST_HTML_CSS_CTRLS[] = R"=====( 455 | )====="; 619 | 620 | PROGMEM const char CONFIGASSIST_HTML_SCRIPT[] = R"=====( 621 |