├── examples ├── .DS_Store ├── GettingStarted │ ├── basicUse │ │ └── basicUse.ino │ ├── OTA │ │ └── OTA.ino │ ├── wifiCallback │ │ └── wifiCallback.ino │ ├── basicUserPass │ │ └── basicUserPass.ino │ ├── Adafruit_IO │ │ └── Adafruit_IO.ino │ ├── relayControl │ │ └── relayControl.ino │ ├── buttonDemo │ │ └── buttonDemo.ino │ └── RelayControlV2 │ │ └── RelayControlV2.ino ├── AdvancedFeatures │ ├── arbitraryConfigKeys │ │ └── arbitraryConfigKeys.ino │ ├── multiNetwork │ │ └── multiNetwork.ino │ ├── secureMQTT │ │ └── secureMQTT.ino │ ├── MQTT_Will │ │ └── MQTT_Will.ino │ ├── configServerDemo │ │ └── configServerDemo.ino │ ├── configServerApDemo │ │ └── configServerApDemo.ino │ └── configAndStatusApDemo │ │ └── configAndStatusApDemo.ino └── InputOutput │ ├── temperatureSensor_ds18b20 │ └── temperatureSensor_ds18b20.ino │ └── RGBLight │ └── RGBLight.ino ├── library.json ├── keywords.txt ├── src ├── ESPHelperWebConfig.h ├── ESPHelper.h ├── sharedData.h ├── ESPHelperWebConfig.cpp ├── config_html.h └── ESPHelper.cpp ├── web_to_header.py ├── README.md ├── static └── config.html └── LICENSE /examples/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ItKindaWorks/ESPHelper/HEAD/examples/.DS_Store -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ESPHelper", 3 | "authors": 4 | { 5 | "name": "ItKindaWorks", 6 | "email": "itkindaworksinc@gmail.com", 7 | "url": "http://itkindaworks.com" 8 | }, 9 | "repository": 10 | { 11 | "type": "git", 12 | "url": "https://github.com/ItKindaWorks/ESPHelper" 13 | }, 14 | "license": "GPL-3.0-or-later", 15 | "keywords": "mqtt, iot, esp8266, esp32, ota", 16 | "description": "A library to make using WiFi & MQTT on the ESP8266 & ESP32 easy.", 17 | "version": "2.0.4", 18 | "examples": "examples/*/*.ino", 19 | "frameworks": "arduino", 20 | "platforms": [ 21 | "espressif", 22 | "espressif32" 23 | ], 24 | "dependencies": { 25 | "bblanchon/ArduinoJson": "@^7.0.4", 26 | "itkindaworks/Metro": "@^1.0.1", 27 | "knolleary/PubSubClient": "@2.8", 28 | "powerbroker2/SafeString": "@^4.1.30", 29 | "esp32async/ESPAsyncWebServer": "@ ^3.6.0" 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For ESPHelper 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | ESPHelper KEYWORD1 10 | ESPHelperFS KEYWORD1 11 | ESPHelperWebConfig KEYWORD1 12 | netInfo KEYWORD1 13 | subscription KEYWORD1 14 | 15 | ####################################### 16 | # Methods and Functions (KEYWORD2) 17 | ####################################### 18 | 19 | begin KEYWORD2 20 | end KEYWORD2 21 | useSecureClient KEYWORD2 22 | broadcastMode KEYWORD2 23 | disableBroadcast KEYWORD2 24 | loop KEYWORD2 25 | subscribe KEYWORD2 26 | addSubscription KEYWORD2 27 | removeSubscription KEYWORD2 28 | unsubscribe KEYWORD2 29 | publish KEYWORD2 30 | setCallback KEYWORD2 31 | setMQTTCallback KEYWORD2 32 | setWifiCallback KEYWORD2 33 | reconnect KEYWORD2 34 | updateNetwork KEYWORD2 35 | getSSID KEYWORD2 36 | serSSID KEYWORD2 37 | getPASS KEYWORD2 38 | setPass KEYWORD2 39 | getMQTTIP KEYWORD2 40 | setMQTTIP KEYWORD2 41 | getMQTTQOS KEYWORD2 42 | setMQTTQOS KEYWORD2 43 | setWill KEYWORD2 44 | getIP KEYWORD2 45 | getIPAddress KEYWORD2 46 | getNetInfo KEYWORD2 47 | setNetInfo KEYWORD2 48 | setHopping KEYWORD2 49 | listSubscriptions KEYWORD2 50 | heartbeat KEYWORD2 51 | enableHeartbeat KEYWORD2 52 | disableHeartbeat KEYWORD2 53 | OTA_enable KEYWORD2 54 | OTA_disable KEYWORD2 55 | OTA_begin KEYWORD2 56 | OTA_setPassword KEYWORD2 57 | OTA_setHostname KEYWORD2 58 | OTA_setHostnameWithVersion KEYWORD2 59 | 60 | ####################################### 61 | # Constants (LITERAL1) 62 | ####################################### 63 | 64 | MAX_SUBSCRIPTIONS LITERAL1 65 | DEFAULT_QOS LITERAL1 66 | VERSION LITERAL1 67 | -------------------------------------------------------------------------------- /examples/GettingStarted/basicUse/basicUse.ino: -------------------------------------------------------------------------------- 1 | /* 2 | basicUse.ino 3 | Copyright (c) 2017 ItKindaWorks All right reserved. 4 | github.com/ItKindaWorks 5 | 6 | This file is part of ESPHelper 7 | 8 | ESPHelper is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | ESPHelper is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with ESPHelper. If not, see . 20 | */ 21 | 22 | #include "ESPHelper.h" 23 | 24 | netInfo homeNet = { .mqttHost = "YOUR MQTT-IP", //can be blank if not using MQTT 25 | .mqttUser = "YOUR MQTT USERNAME", //can be blank 26 | .mqttPass = "YOUR MQTT PASSWORD", //can be blank 27 | .mqttPort = 1883, //default port for MQTT is 1883 - only chance if needed. 28 | .ssid = "YOUR SSID", 29 | .pass = "YOUR NETWORK PASS"}; 30 | 31 | ESPHelper myESP(&homeNet); 32 | 33 | void setup() { 34 | 35 | Serial.begin(115200); //start the serial line 36 | delay(500); 37 | 38 | Serial.println("Starting Up, Please Wait..."); 39 | 40 | myESP.addSubscription("/test"); 41 | 42 | myESP.setMQTTCallback(callback); 43 | myESP.begin(); 44 | 45 | Serial.println("Initialization Finished."); 46 | } 47 | 48 | void loop(){ 49 | myESP.loop(); //run the loop() method as often as possible - this keeps the network services running 50 | 51 | //Put application code here 52 | 53 | yield(); 54 | } 55 | 56 | void callback(char* topic, uint8_t* payload, unsigned int length) { 57 | //put mqtt callback code here 58 | } -------------------------------------------------------------------------------- /examples/GettingStarted/OTA/OTA.ino: -------------------------------------------------------------------------------- 1 | /* 2 | OTA.ino 3 | Copyright (c) 2017 ItKindaWorks All right reserved. 4 | github.com/ItKindaWorks 5 | 6 | This file is part of ESPHelper 7 | 8 | ESPHelper is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | ESPHelper is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with ESPHelper. If not, see . 20 | */ 21 | 22 | #include "ESPHelper.h" 23 | 24 | netInfo homeNet = { .mqttHost = "YOUR MQTT-IP", //can be blank if not using MQTT 25 | .mqttUser = "YOUR MQTT USERNAME", //can be blank 26 | .mqttPass = "YOUR MQTT PASSWORD", //can be blank 27 | .mqttPort = 1883, //default port for MQTT is 1883 - only chance if needed. 28 | .ssid = "YOUR SSID", 29 | .pass = "YOUR NETWORK PASS"}; 30 | 31 | ESPHelper myESP(&homeNet); 32 | 33 | void setup() { 34 | 35 | Serial.begin(115200); //start the serial line 36 | delay(500); 37 | 38 | Serial.println("Starting Up, Please Wait..."); 39 | 40 | myESP.OTA_enable(); 41 | myESP.OTA_setPassword("SET OTA PASSWORD"); 42 | myESP.OTA_setHostnameWithVersion("SET OTA HOSTNAME"); 43 | 44 | myESP.addSubscription("/test"); 45 | 46 | myESP.setMQTTCallback(callback); 47 | myESP.begin(); 48 | 49 | Serial.println("Initialization Finished."); 50 | } 51 | 52 | void loop(){ 53 | myESP.loop(); //run the loop() method as often as possible - this keeps the network services running 54 | 55 | //Put application code here 56 | 57 | yield(); 58 | } 59 | 60 | void callback(char* topic, uint8_t* payload, unsigned int length) { 61 | //put mqtt callback code here 62 | } -------------------------------------------------------------------------------- /examples/GettingStarted/wifiCallback/wifiCallback.ino: -------------------------------------------------------------------------------- 1 | /* 2 | wifiCallback.ino 3 | Copyright (c) 2017 ItKindaWorks All right reserved. 4 | github.com/ItKindaWorks 5 | 6 | This file is part of ESPHelper 7 | 8 | ESPHelper is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | ESPHelper is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with ESPHelper. If not, see . 20 | */ 21 | 22 | #include "ESPHelper.h" 23 | 24 | netInfo homeNet = { .mqttHost = "YOUR MQTT-IP", //can be blank if not using MQTT 25 | .mqttUser = "YOUR MQTT USERNAME", //can be blank 26 | .mqttPass = "YOUR MQTT PASSWORD", //can be blank 27 | .mqttPort = 1883, //default port for MQTT is 1883 - only chance if needed. 28 | .ssid = "YOUR SSID", 29 | .pass = "YOUR NETWORK PASS"}; 30 | 31 | ESPHelper myESP(&homeNet); 32 | 33 | void setup() { 34 | 35 | Serial.begin(115200); //start the serial line 36 | delay(500); 37 | 38 | Serial.println("Starting Up, Please Wait..."); 39 | 40 | myESP.addSubscription("/test"); 41 | myESP.setWifiCallback(wifiCallback); 42 | myESP.setMQTTCallback(MQTTcallback); 43 | myESP.begin(); 44 | 45 | 46 | Serial.println("Initialization Finished."); 47 | } 48 | 49 | void loop(){ 50 | myESP.loop(); //run the loop() method as often as possible - this keeps the network services running 51 | 52 | //Put application code here 53 | 54 | yield(); 55 | } 56 | 57 | void MQTTcallback(char* topic, uint8_t* payload, unsigned int length) { 58 | //put mqtt callback code here 59 | } 60 | 61 | void wifiCallback(){ 62 | Serial.println("WiFi Connected!"); 63 | } -------------------------------------------------------------------------------- /examples/AdvancedFeatures/arbitraryConfigKeys/arbitraryConfigKeys.ino: -------------------------------------------------------------------------------- 1 | /* 2 | arbitraryConfigKeys.ino 3 | Copyright (c) 2017 ItKindaWorks All right reserved. 4 | github.com/ItKindaWorks 5 | 6 | This file is part of ESPHelper 7 | 8 | ESPHelper is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | ESPHelper is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with ESPHelper. If not, see . 20 | */ 21 | 22 | #include "ESPHelperFS.h" 23 | 24 | //filename and json key to use in the demo 25 | const char* file = "/test.json"; 26 | const char* keyName = "count"; 27 | 28 | void setup(void){ 29 | Serial.begin(115200); 30 | 31 | //load the filesystem and initialize the key to 0 (then close the FS) 32 | //Note: Although it is not strictly necessary to close the FS it is still a good idea 33 | //to only have the FS open as long as it is needed and then close it again to prevent 34 | //possible data corruption. It does make things a bit slower but it's also safer. 35 | ESPHelperFS::begin(); 36 | ESPHelperFS::addKey(keyName, "0", file); 37 | ESPHelperFS::end(); 38 | } 39 | 40 | void loop(void){ 41 | 42 | //load the FS and load the config file key 43 | ESPHelperFS::begin(); 44 | String value = ESPHelperFS::loadKey(keyName, file); 45 | 46 | 47 | 48 | //convert the string to int and increment the counter 49 | int counter = value.toInt(); 50 | counter++; 51 | 52 | //print out the new value of the counter 53 | Serial.print("Counter = "); 54 | Serial.println(counter); 55 | 56 | //convert the key back to a char string for saving 57 | char dataOut[5]; 58 | sprintf(dataOut, "%d", counter); 59 | 60 | //save the updated key and close the filesystem 61 | ESPHelperFS::addKey(keyName, dataOut, file); 62 | ESPHelperFS::end(); 63 | 64 | //wait for a second 65 | delay(1000); 66 | } 67 | -------------------------------------------------------------------------------- /src/ESPHelperWebConfig.h: -------------------------------------------------------------------------------- 1 | /* 2 | ESPHelperWebConfig.h 3 | Copyright (c) 2019 ItKindaWorks All right reserved. 4 | github.com/ItKindaWorks 5 | 6 | This file is part of ESPHelper 7 | 8 | ESPHelper is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | ESPHelper is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with ESPHelper. If not, see . 20 | */ 21 | 22 | 23 | 24 | #ifndef ESPHELPER_WEBCONFIG_H 25 | #define ESPHELPER_WEBCONFIG_H 26 | 27 | 28 | #include "ESPHelper.h" 29 | 30 | #ifdef ESP8266 31 | #include 32 | #endif 33 | 34 | #ifdef ESP32 35 | #include 36 | #include 37 | #endif 38 | 39 | #include 40 | #include 41 | #include "SafeString.h" 42 | 43 | #include "config_html.h" 44 | 45 | 46 | 47 | class ESPHelperWebConfig{ 48 | 49 | public: 50 | ESPHelperWebConfig(int port, const char* URI); //constructor 51 | ESPHelperWebConfig(AsyncWebServer *server, const char* URI); 52 | ~ESPHelperWebConfig(); //destructor 53 | 54 | bool begin(const char* hostname); 55 | bool begin(); 56 | 57 | void useConfig(NetInfo& config); 58 | 59 | void fillConfig(bool preFill = true); 60 | 61 | bool handle(); 62 | 63 | NetInfo& getConfig(); 64 | 65 | void setFlashReset(const char* uri); 66 | 67 | 68 | private: 69 | String handleGet(const String& var); 70 | void handlePost(AsyncWebServerRequest *request); 71 | void handleNotFound(AsyncWebServerRequest *request); 72 | void handleReset(AsyncWebServerRequest *request); 73 | 74 | AsyncWebServer *_server; 75 | 76 | char _resetURI[64]; 77 | char _pageURI[64]; 78 | 79 | bool _preFill = false; 80 | 81 | bool _resetSet = false; 82 | 83 | NetInfo _config; 84 | bool _runningLocal = false; 85 | bool _configChanged = false; 86 | 87 | 88 | }; 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /web_to_header.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import shutil 4 | import minify_html 5 | 6 | 7 | 8 | STATIC_DIR = "static" 9 | OUTPUT_DIR = "src" 10 | 11 | def convert_file(source_path, dest_path): 12 | if source_path.endswith(".html"): 13 | print(f"\t[HTML->H] Minifying {source_path} to {dest_path}") 14 | temp_path = source_path + ".tmp" 15 | shutil.move(source_path, temp_path) 16 | 17 | 18 | with open(temp_path, "r") as temp_file: 19 | content = temp_file.read() 20 | minified = minify_html.minify(content, minify_js=False, remove_processing_instructions=True) 21 | 22 | with open(source_path, "w") as minified_file: 23 | minified_file.write(minified) 24 | 25 | with open(dest_path, "w") as out_file: 26 | subprocess.run(["xxd", "-i", source_path], stdout=out_file) 27 | 28 | os.remove(source_path) # Clean up the temporary file 29 | shutil.move(temp_path, source_path) # Restore the original file name 30 | 31 | 32 | else: 33 | with open(dest_path, "w") as out_file: 34 | subprocess.run(["xxd", "-i", source_path], stdout=out_file) 35 | 36 | 37 | 38 | def post_process_file(file_path): 39 | # Post-process to add PROGMEM to the array declaration 40 | with open(file_path, "r") as file: 41 | lines = file.readlines() 42 | 43 | lines = ["#pragma once\n\n"] + lines # Add pragma once at the top 44 | 45 | with open(file_path, "w") as file: 46 | for line in lines: 47 | if "[] =" in line: 48 | line = line.replace("[] =", "[] PROGMEM =") 49 | if "unsigned" in line: 50 | line = line.replace("unsigned", "inline unsigned") 51 | file.write(line) 52 | 53 | 54 | def main(): 55 | 56 | # print("[HTML->H] Converting HTML/CSS/JS files to C header files...") 57 | if not os.path.exists(OUTPUT_DIR): 58 | os.makedirs(OUTPUT_DIR) 59 | for filename in os.listdir(STATIC_DIR): 60 | if filename.endswith(".html") or filename.endswith(".css") or filename.endswith(".js"): 61 | input_file = os.path.join(STATIC_DIR, filename) 62 | output_file = os.path.join(OUTPUT_DIR, filename.replace('.', '_') + ".h") 63 | print(f"[HTML->H] Converting {input_file} -> {output_file}") 64 | convert_file(input_file, output_file) 65 | post_process_file(output_file) 66 | 67 | if __name__ == "__main__": 68 | main() 69 | -------------------------------------------------------------------------------- /examples/AdvancedFeatures/multiNetwork/multiNetwork.ino: -------------------------------------------------------------------------------- 1 | /* 2 | MultiNetwork.ino 3 | Copyright (c) 2017 ItKindaWorks All right reserved. 4 | github.com/ItKindaWorks 5 | 6 | This file is part of ESPHelper 7 | 8 | ESPHelper is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | ESPHelper is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with ESPHelper. If not, see . 20 | */ 21 | 22 | #include "ESPHelper.h" 23 | 24 | 25 | //set this info for your own network 26 | netInfo homeNet1 = {.mqttHost = "YOUR MQTT-IP", //can be blank if not using MQTT 27 | .mqttUser = "YOUR MQTT USERNAME", //can be blank 28 | .mqttPass = "YOUR MQTT PASSWORD", //can be blank 29 | .mqttPort = 1883, //default port for MQTT is 1883 - only chance if needed. 30 | .ssid = "YOUR SSID", 31 | .pass = "YOUR NETWORK PASS"}; 32 | 33 | netInfo homeNet2 = {.mqttHost = "YOUR MQTT-IP", //can be blank if not using MQTT 34 | .mqttUser = "YOUR MQTT USERNAME", //can be blank 35 | .mqttPass = "YOUR MQTT PASSWORD", //can be blank 36 | .mqttPort = 1883, //default port for MQTT is 1883 - only chance if needed. 37 | .ssid = "YOUR SSID", 38 | .pass = "YOUR NETWORK PASS"}; 39 | 40 | netInfo homeNet3 = {.mqttHost = "YOUR MQTT-IP", //can be blank if not using MQTT 41 | .mqttUser = "YOUR MQTT USERNAME", //can be blank 42 | .mqttPass = "YOUR MQTT PASSWORD", //can be blank 43 | .mqttPort = 1883, //default port for MQTT is 1883 - only chance if needed. 44 | .ssid = "YOUR SSID", 45 | .pass = "YOUR NETWORK PASS"}; 46 | 47 | netInfo *knownNetworks[3] = { 48 | &homeNet1, 49 | &homeNet2, 50 | &homeNet3 51 | }; 52 | 53 | ESPHelper myESP(knownNetworks, 3); 54 | 55 | void setup() { 56 | 57 | Serial.begin(115200); //start the serial line 58 | delay(500); 59 | 60 | Serial.println("Starting Up, Please Wait..."); 61 | 62 | // myESP.setHopping(false); //uncomment to prevent hopping between networks in network array 63 | 64 | myESP.addSubscription("/test"); 65 | myESP.setMQTTCallback(callback); 66 | myESP.begin(); 67 | 68 | Serial.println("Initialization Finished."); 69 | } 70 | 71 | void loop(){ 72 | myESP.loop(); //run the loop() method as often as possible - this keeps the network services running 73 | 74 | //Put application code here 75 | 76 | yield(); 77 | } 78 | 79 | void callback(char* topic, uint8_t* payload, unsigned int length) { 80 | //put mqtt callback code here 81 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESPHelper 2 | 3 | ESPHelper is a C++ library designed to simplify WiFi and MQTT connectivity for ESP8266 and ESP32 platforms. It manages WiFi and MQTT connections, automatic reconnection, topic subscriptions, and supports OTA (Over-The-Air) updates, making it ideal for IoT and home automation projects. 4 | 5 | ## Features 6 | 7 | - **Automatic WiFi and MQTT Connection Management:** Handles connecting, reconnecting, and resubscribing to MQTT topics. 8 | - **MQTT Topic Subscription Management:** Add, remove, and auto-resubscribe to topics. 9 | - **OTA Updates:** Easily enable/disable OTA, set OTA password and hostname. 10 | - **Broadcast Mode:** Create an access point for configuration or OTA when no WiFi is available. 11 | - **Web Configuration:** Optional web interface for device configuration ([`ESPHelperWebConfig`](src/ESPHelperWebConfig.h)). 12 | - **Callback Support:** Set custom callbacks for WiFi connection, WiFi loss, and MQTT messages. 13 | - **Secure MQTT:** Supports SSL/TLS connections to MQTT brokers. 14 | 15 | ## Requirements 16 | 17 | Make sure you have these libraries installed: 18 | * [Metro](https://github.com/ItKindaWorks/ESPHelper_Metro) 19 | * [PubSubClient](https://github.com/knolleary/pubsubclient) (Current Supported Version - 2.8) 20 | * [ArduinoJson](https://github.com/bblanchon/ArduinoJson) (Current Supported Version - 7.0.4) 21 | * [SafeString](https://github.com/PowerBroker2/SafeString) (Current Supported Version - 4.1.30) 22 | 23 | In addition to those libraries, make sure that you have the ESP core files installed for your platform. 24 | * [ESP8266 Arduino Core](https://github.com/esp8266/Arduino) 25 | * [ESP32 Arduino Core](https://github.com/espressif/arduino-esp32) 26 | 27 | ## Getting Started 28 | 29 | See the [examples/GettingStarted](examples/GettingStarted/) folder for usage examples. 30 | 31 | ### Basic Usage 32 | 33 | ```cpp 34 | #include "ESPHelper.h" 35 | 36 | ESPHelper helper; 37 | 38 | void setup() { 39 | helper.setSSID("yourSSID"); 40 | helper.setPASS("yourPassword"); 41 | helper.setMQTTIP("mqtt.example.com"); 42 | helper.begin(); 43 | } 44 | 45 | void loop() { 46 | helper.loop(); 47 | } 48 | ``` 49 | 50 | Useful Methods: 51 | --------------- 52 | 53 | * *bool begin();* 54 | Initialize the system (must be called once). 55 | 56 | * *int loop();* 57 | must be called as often as possible to maintain connections and run the various subsystems 58 | 59 | * *bool subscribe(char\* topic);* 60 | subscribe to a given MQTT topic (will NOT auto re-subscribe on connection lost) 61 | 62 | * *bool addSubscription(char\* topic);* 63 | add a topic to the subscription list (will auto re-subscribe on connection lost) 64 | 65 | * *bool removeSubscription(char\* topic);* 66 | remove a topic from the subscription list and unsubscribe 67 | 68 | * *void publish(char\* topic, char\* payload);* 69 | publish a given MQTT message to a given topic 70 | 71 | ### ToDo 72 | 73 | * Implement callback for lost WiFi connection 74 | 75 | -------------------------------------------------------------------------------- /examples/GettingStarted/basicUserPass/basicUserPass.ino: -------------------------------------------------------------------------------- 1 | /* 2 | basicUserPass.ino 3 | Copyright (c) 2017 ItKindaWorks All right reserved. 4 | github.com/ItKindaWorks 5 | 6 | This file is part of ESPHelper 7 | 8 | ESPHelper is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | ESPHelper is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with ESPHelper. If not, see . 20 | */ 21 | 22 | #include "ESPHelper.h" 23 | #include "Metro.h" 24 | 25 | #define SSID "YOUR SSID" 26 | #define NETWORK_PASS "YOUR NETWORK PASS" 27 | 28 | #define MQTT_HOST "MQTT_HOST" 29 | #define MQTT_USERNAME "YOUR_MQTT_USERNAME" 30 | #define MQTT_PASS "YOUR_MQTT_PASSWORD" 31 | #define MQTT_PORT 1883 32 | #define MQTT_TOPIC "/test" 33 | 34 | 35 | //Metro timer for 5 second intervals (used to time publishing to MQTT) 36 | Metro postMetro = Metro(5000); 37 | 38 | 39 | netInfo homeNet = { .mqttHost = MQTT_HOST, 40 | .mqttUser = MQTT_USERNAME, 41 | .mqttPass = MQTT_PASS, 42 | .mqttPort = MQTT_PORT, //default port for MQTT is 1883 - only change if needed. 43 | .ssid = SSID, 44 | .pass = NETWORK_PASS}; 45 | 46 | ESPHelper myESP(&homeNet); 47 | 48 | 49 | int counter = 0; 50 | 51 | 52 | void setup() { 53 | Serial.begin(115200); 54 | 55 | //setup ESPHelper 56 | myESP.setMQTTCallback(callback); 57 | myESP.addSubscription(MQTT_TOPIC); 58 | myESP.begin(); 59 | } 60 | 61 | 62 | void loop(){ 63 | 64 | //check and make sure that we have a full connection to both WIFI and MQTT 65 | //and only post if the timer has gone off 66 | if(myESP.loop() == FULL_CONNECTION && postMetro.check()){ 67 | //print to the serial line 68 | Serial.print(counter); 69 | Serial.println(" Sent"); 70 | 71 | //generate a string from our counter variable 72 | char pubString[10]; 73 | itoa(counter, pubString, 10); 74 | 75 | //publish the data to MQTT 76 | myESP.publish(MQTT_TOPIC, pubString); 77 | 78 | //increment the counter 79 | counter++; 80 | } 81 | 82 | 83 | yield(); 84 | } 85 | 86 | 87 | void callback(char* topic, uint8_t* payload, unsigned int length) { 88 | 89 | //generate a new payload string that is null terminated 90 | char newPayload[40]; 91 | memcpy(newPayload, payload, length); 92 | newPayload[length] = '\0'; 93 | 94 | //print that info back out the the serial line 95 | Serial.print(newPayload); 96 | Serial.println(" Received"); 97 | } 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /examples/GettingStarted/Adafruit_IO/Adafruit_IO.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Adafruit_IO.ino 3 | Copyright (c) 2017 ItKindaWorks All right reserved. 4 | github.com/ItKindaWorks 5 | 6 | This file is part of ESPHelper 7 | 8 | ESPHelper is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | ESPHelper is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with ESPHelper. If not, see . 20 | */ 21 | 22 | #include "ESPHelper.h" 23 | #include "Metro.h" 24 | 25 | #define SSID "YOUR SSID" 26 | #define NETWORK_PASS "YOUR NETWORK PASS" 27 | 28 | #define AIO_USERNAME "YOUR_AIO_USERNAME" 29 | #define AIO_KEY "YOUR_AIO_KEY" 30 | #define AIO_FEED "AIO_FEED_NAME" 31 | 32 | 33 | //Metro timer for 5 second intervals (used to time publishing to MQTT) 34 | Metro postMetro = Metro(5000); 35 | 36 | 37 | netInfo homeNet = { .mqttHost = "io.adafruit.com", //can be blank if not using MQTT 38 | .mqttUser = AIO_USERNAME, //can be blank 39 | .mqttPass = AIO_KEY, //can be blank 40 | .mqttPort = 1883, //default port for MQTT is 1883 - only chance if needed. 41 | .ssid = SSID, 42 | .pass = NETWORK_PASS}; 43 | 44 | ESPHelper myESP(&homeNet); 45 | 46 | 47 | int counter = 0; 48 | 49 | 50 | void setup() { 51 | Serial.begin(115200); 52 | 53 | //setup ESPHelper 54 | myESP.setMQTTCallback(callback); 55 | myESP.addSubscription(AIO_USERNAME"/feeds/"AIO_FEED); 56 | myESP.begin(); 57 | 58 | 59 | } 60 | 61 | 62 | void loop(){ 63 | 64 | //check and make sure that we have a full connection to both WIFI and MQTT 65 | //and only post if the timer has gone off 66 | if(myESP.loop() == FULL_CONNECTION && postMetro.check()){ 67 | //print to the serial line 68 | Serial.print(counter); 69 | Serial.println(" Sent"); 70 | 71 | //generate a string from our counter variable 72 | char pubString[10]; 73 | itoa(counter, pubString, 10); 74 | 75 | //publish the data to MQTT 76 | myESP.publish(AIO_USERNAME"/feeds/"AIO_FEED, pubString); 77 | 78 | //increment the counter 79 | counter++; 80 | } 81 | 82 | 83 | yield(); 84 | } 85 | 86 | 87 | void callback(char* topic, uint8_t* payload, unsigned int length) { 88 | 89 | //generate a new payload string that is null terminated 90 | char newPayload[40]; 91 | memcpy(newPayload, payload, length); 92 | newPayload[length] = '\0'; 93 | 94 | //print that info back out the the serial line 95 | Serial.print(newPayload); 96 | Serial.println(" Received"); 97 | } 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /examples/AdvancedFeatures/secureMQTT/secureMQTT.ino: -------------------------------------------------------------------------------- 1 | /* 2 | secureMQTT.ino 3 | Copyright (c) 2017 ItKindaWorks All right reserved. 4 | github.com/ItKindaWorks 5 | 6 | This file is part of ESPHelper 7 | 8 | ESPHelper is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | ESPHelper is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with ESPHelper. If not, see . 20 | */ 21 | 22 | #include "ESPHelper.h" 23 | #include "Metro.h" 24 | 25 | #define SSID "YOUR SSID" 26 | #define NETWORK_PASS "YOUR NETWORK PASS" 27 | 28 | #define MQTT_HOST "MQTT_HOST" 29 | #define MQTT_USERNAME "YOUR_MQTT_USERNAME" 30 | #define MQTT_PASS "YOUR_MQTT_PASSWORD" 31 | #define MQTT_PORT 1883 32 | #define MQTT_TOPIC "/test" 33 | 34 | //SHA1 fingerprint for the host that you want to connect to. 35 | const char* fingerprint = "11:22:33:44:55:66:77:88:99:AA:BB:CC:EE:FF:11:22:33:44:55:66"; 36 | 37 | 38 | //Metro timer for 5 second intervals (used to time publishing to MQTT) 39 | Metro postMetro = Metro(5000); 40 | 41 | 42 | netInfo homeNet = { .mqttHost = MQTT_HOST, 43 | .mqttUser = MQTT_USERNAME, 44 | .mqttPass = MQTT_PASS, 45 | .mqttPort = MQTT_PORT, //default port for MQTT is 1883 - only change if needed. 46 | .ssid = SSID, 47 | .pass = NETWORK_PASS}; 48 | 49 | ESPHelper myESP(&homeNet); 50 | 51 | 52 | int counter = 0; 53 | 54 | 55 | void setup() { 56 | Serial.begin(115200); 57 | 58 | //setup ESPHelper 59 | myESP.useSecureClient(fingerprint); 60 | myESP.setMQTTCallback(callback); 61 | myESP.addSubscription(MQTT_TOPIC); 62 | myESP.begin(); 63 | } 64 | 65 | 66 | void loop(){ 67 | 68 | //check and make sure that we have a full connection to both WIFI and MQTT 69 | //and only post if the timer has gone off 70 | if(myESP.loop() == FULL_CONNECTION && postMetro.check()){ 71 | //print to the serial line 72 | Serial.print(counter); 73 | Serial.println(" Sent"); 74 | 75 | //generate a string from our counter variable 76 | char pubString[10]; 77 | itoa(counter, pubString, 10); 78 | 79 | //publish the data to MQTT 80 | myESP.publish(MQTT_TOPIC, pubString); 81 | 82 | //increment the counter 83 | counter++; 84 | } 85 | 86 | 87 | yield(); 88 | } 89 | 90 | 91 | void callback(char* topic, uint8_t* payload, unsigned int length) { 92 | 93 | //generate a new payload string that is null terminated 94 | char newPayload[40]; 95 | memcpy(newPayload, payload, length); 96 | newPayload[length] = '\0'; 97 | 98 | //print that info back out the the serial line 99 | Serial.print(newPayload); 100 | Serial.println(" Received"); 101 | } 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /examples/AdvancedFeatures/MQTT_Will/MQTT_Will.ino: -------------------------------------------------------------------------------- 1 | /* 2 | MQTT_Will.ino 3 | Copyright (c) 2018 Sk4zz All right reserved. 4 | github.com/Sk4zz 5 | 6 | This file is part of ESPHelper 7 | github.com/ItKindaWorks 8 | 9 | ESPHelper is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | ESPHelper is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License 20 | along with ESPHelper. If not, see . 21 | */ 22 | 23 | 24 | #include 25 | #include 26 | #include "ESPHelper.h" 27 | 28 | #define INTERNAL_LED 2 //Set Pin Number for the internal LED this is for ESP-12E 29 | #define STATUS_TOPIC "test/status" //Set the default topic to publish status messages 30 | 31 | 32 | // Update these with values suitable for your network. 33 | netInfo homeNet = { .mqttHost = "192.168.2.3", //can be blank if not using MQTT 34 | .mqttUser = "", //can be blank 35 | .mqttPass = "", //can be blank 36 | .mqttPort = 1883, //default port for MQTT is 1883 - only chance if needed. 37 | .ssid = "", 38 | .pass = "", 39 | .otaPassword = "", //password for OTA flashing. can be left blank 40 | .hostname ="ESP8266", //Hostname of the board 41 | .willTopic = STATUS_TOPIC, //Topic for Last Will 42 | .willMessage = "offline", //Last Will message 43 | .willQoS = 1, //QoS of last will message 44 | .willRetain = 1}; //Retain flag for last will message 45 | 46 | ESPHelper myESP(&homeNet); 47 | 48 | bool MQTT_Connection; 49 | 50 | bool checkMqttConnection() { 51 | // Check if the Connection status has changed. 52 | // If Status changed to FULL_CONNECTION publish "online" status message on STATUS_TOPIC 53 | // Toggle the INTERNAL_LED 54 | if( MQTT_Connection != (myESP.getStatus() == FULL_CONNECTION) ){ 55 | MQTT_Connection = (myESP.getStatus() == FULL_CONNECTION); 56 | if (MQTT_Connection) { 57 | Serial.print("Full Connection established. publishing status on Topic "); 58 | Serial.println(STATUS_TOPIC); 59 | myESP.publish(STATUS_TOPIC, "online", true); 60 | } 61 | digitalWrite(INTERNAL_LED, !MQTT_Connection); 62 | } 63 | } 64 | 65 | 66 | void setup() { 67 | Serial.begin(115200); 68 | 69 | pinMode(INTERNAL_LED, OUTPUT); // Initialize the INTERNAL_LED pin as an output 70 | digitalWrite(INTERNAL_LED, HIGH); // Switch INTERNAL_LED OFF 71 | MQTT_Connection = false; 72 | 73 | myESP.OTA_enable(); 74 | myESP.addSubscription("test/incoming"); 75 | myESP.setMQTTCallback(callback); 76 | myESP.begin(); 77 | } 78 | 79 | 80 | void callback(char* topic, byte* payload, unsigned int length) { 81 | Serial.print("Message arrived ["); 82 | Serial.print(topic); 83 | Serial.print("] "); 84 | for (int i = 0; i < length; i++) { 85 | Serial.print((char)payload[i]); 86 | } 87 | Serial.println(); 88 | } 89 | 90 | void loop() { 91 | myESP.loop(); //run the loop() method as often as possible - this keeps the network services running 92 | 93 | checkMqttConnection(); 94 | 95 | yield(); 96 | } 97 | -------------------------------------------------------------------------------- /examples/GettingStarted/relayControl/relayControl.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 ItKindaWorks All right reserved. 3 | github.com/ItKindaWorks 4 | 5 | This file is part of RelayControl 6 | 7 | RelayControl is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | RelayControl is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with RelayControl. If not, see . 19 | */ 20 | 21 | /* 22 | This is a simple MQTT relay/light controller program for the ESP8266. 23 | By sending a '1' or '0' to the relayTopic the relayPin can be toggled 24 | on or off. This program also posts a status update to the status topic 25 | which is the relayTopic plus "/status" (ex. if the relayTopic 26 | is "/home/light" then the statusTopic would be "home/light/status") 27 | */ 28 | 29 | #include "ESPHelper.h" 30 | 31 | #define TOPIC "/your/mqtt/topic" 32 | #define STATUS TOPIC "/status" //dont change this - this is for the status topic which is whatever your mqtt topic is plus /status (ex /home/light/status) 33 | 34 | #define NETWORK_HOSTNAME "YOUR OTA HOST NAME" 35 | #define OTA_PASSWORD "YOUR OTA PASSWORD" 36 | 37 | #define RELAY_PIN 3 //rx pin on esp 38 | #define BLINK_PIN 1 //tx/led on esp-01 39 | 40 | 41 | char* relayTopic = TOPIC; 42 | char* statusTopic = STATUS; 43 | char* hostnameStr = NETWORK_HOSTNAME; 44 | 45 | const int relayPin = RELAY_PIN; 46 | const int blinkPin = BLINK_PIN; //tx pin on esp 47 | 48 | //set this info for your own network 49 | netInfo homeNet = { .mqttHost = "YOUR MQTT-IP", //can be blank if not using MQTT 50 | .mqttUser = "YOUR MQTT USERNAME", //can be blank 51 | .mqttPass = "YOUR MQTT PASSWORD", //can be blank 52 | .mqttPort = 1883, //default port for MQTT is 1883 - only chance if needed. 53 | .ssid = "YOUR SSID", 54 | .pass = "YOUR NETWORK PASS"}; 55 | 56 | ESPHelper myESP(&homeNet); 57 | 58 | void setup() { 59 | //setup ota 60 | myESP.OTA_enable(); 61 | myESP.OTA_setPassword(OTA_PASSWORD); 62 | myESP.OTA_setHostnameWithVersion(hostnameStr); 63 | 64 | 65 | //setup the rest of ESPHelper 66 | myESP.enableHeartbeat(blinkPin); //comment out to disable the heartbeat 67 | myESP.addSubscription(relayTopic); //add the relay topic to the subscription list 68 | myESP.setMQTTCallback(callback); 69 | myESP.begin(); 70 | 71 | 72 | pinMode(relayPin, OUTPUT); 73 | delay(100); 74 | } 75 | 76 | 77 | void loop(){ 78 | //loop ESPHelper and wait for commands from mqtt 79 | myESP.loop(); 80 | yield(); 81 | } 82 | 83 | 84 | //mqtt callback 85 | void callback(char* topic, byte* payload, unsigned int length) { 86 | String topicStr = topic; 87 | 88 | //if the payload from mqtt was 1, turn the relay on and update the status topic with 1 89 | if(payload[0] == '1'){ 90 | digitalWrite(relayPin, HIGH); 91 | myESP.publish(statusTopic, "1",true); 92 | } 93 | 94 | //else turn the relay off and update the status topic with 0 95 | else if (payload[0] == '0'){ 96 | digitalWrite(relayPin, LOW); 97 | myESP.publish(statusTopic, "0", true); 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /examples/InputOutput/temperatureSensor_ds18b20/temperatureSensor_ds18b20.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 ItKindaWorks All right reserved. 3 | github.com/ItKindaWorks 4 | 5 | This file is part of ESPHelper 6 | 7 | ESPHelper is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | ESPHelper is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with ESPHelper. If not, see . 19 | */ 20 | 21 | /* 22 | This is a simple program that periodically (10 seconds) reads a ds18b20 temperature 23 | sensor and publishes the result to an MQTT topic. Change the Topic/Hostname/OTA Password 24 | and network settings to match your system. 25 | */ 26 | 27 | #include "ESPHelper.h" 28 | #include 29 | #include 30 | #include 31 | 32 | #define TEMP_TOPIC "/your/mqtt/topic" 33 | #define NETWORK_HOSTNAME "YOUR OTA HOSTNAME" 34 | #define OTA_PASSWORD "YOUR OTA PASSWORD" 35 | 36 | #define ONE_WIRE_BUS 2 37 | #define BLINK_PIN 1 38 | 39 | char* tempTopic = TEMP_TOPIC; 40 | char* hostnameStr = NETWORK_HOSTNAME; 41 | const int wireBus = ONE_WIRE_BUS; 42 | const int blinkPin = BLINK_PIN; 43 | 44 | 45 | //the current temperature 46 | float currentTemp = 0; 47 | 48 | //timer to set how often the sensor should publish to mqtt (in ms) 49 | Metro publishTimer = Metro(10000); 50 | 51 | //ds18b20 variables 52 | OneWire oneWire(ONE_WIRE_BUS); 53 | DallasTemperature sensors(&oneWire); 54 | 55 | //set this info for your own network 56 | netInfo homeNet = { .mqttHost = "YOUR MQTT-IP", //can be blank if not using MQTT 57 | .mqttUser = "YOUR MQTT USERNAME", //can be blank 58 | .mqttPass = "YOUR MQTT PASSWORD", //can be blank 59 | .mqttPort = 1883, //default port for MQTT is 1883 - only chance if needed. 60 | .ssid = "YOUR SSID", 61 | .pass = "YOUR NETWORK PASS"}; 62 | 63 | ESPHelper myESP(&homeNet); 64 | 65 | void setup() { 66 | 67 | //setup ota on esphelper 68 | myESP.OTA_enable(); 69 | myESP.OTA_setPassword(OTA_PASSWORD); 70 | myESP.OTA_setHostnameWithVersion(hostnameStr); 71 | 72 | //enable the connection heartbeat 73 | myESP.enableHeartbeat(blinkPin); 74 | 75 | //start ESPHelper 76 | myESP.begin(); 77 | 78 | //start the dallas temperature sensor library 79 | sensors.begin(); 80 | delay(500); 81 | } 82 | 83 | void loop(){ 84 | 85 | 86 | while(1){ 87 | if(myESP.loop() == FULL_CONNECTION){ 88 | 89 | //only read/publish the temperature if the timer is up 90 | if(publishTimer.check()){ 91 | 92 | sensors.requestTemperatures(); // Send the command to get temperatures 93 | currentTemp = sensors.getTempCByIndex(0); //get the temperature 94 | 95 | //convert the float to a string 96 | char temperature[10]; 97 | dtostrf(currentTemp,4,1,temperature); 98 | 99 | //publish to mqtt 100 | myESP.publish(tempTopic,temperature, true); 101 | } 102 | } 103 | 104 | yield(); 105 | } 106 | 107 | } 108 | 109 | 110 | void callback(char* topic, uint8_t* payload, unsigned int length) { 111 | 112 | } 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /examples/AdvancedFeatures/configServerDemo/configServerDemo.ino: -------------------------------------------------------------------------------- 1 | /* 2 | configServerDemo.ino 3 | Copyright (c) 2017 ItKindaWorks All right reserved. 4 | github.com/ItKindaWorks 5 | 6 | This file is part of ESPHelper 7 | 8 | ESPHelper is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | ESPHelper is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with ESPHelper. If not, see . 20 | */ 21 | 22 | #include "ESPHelper.h" 23 | #include "ESPHelperFS.h" 24 | #include "ESPHelperWebConfig.h" 25 | 26 | netInfo config; 27 | ESPHelper myESP; 28 | ESPHelperWebConfig configPage(80, "/"); 29 | 30 | netInfo homeNet = { .mqttHost = "YOUR MQTT-IP", //can be blank if not using MQTT 31 | .mqttUser = "YOUR MQTT USERNAME", //can be blank 32 | .mqttPass = "YOUR MQTT PASSWORD", //can be blank 33 | .mqttPort = 1883, //default port for MQTT is 1883 - only chance if needed. 34 | .ssid = "YOUR SSID", 35 | .pass = "YOUR NETWORK PASS", 36 | .otaPassword = "YOUR OTA PASS", 37 | .hostname = "NEW-ESP8266"}; 38 | 39 | void setup(void){ 40 | loadConfig(); 41 | 42 | //setup other ESPHelper info and enable OTA updates 43 | myESP.setHopping(false); 44 | myESP.OTA_setPassword(config.otaPassword); 45 | myESP.OTA_setHostnameWithVersion(config.hostname); 46 | myESP.OTA_enable(); 47 | 48 | //startup the config page 49 | configPage.begin(config.hostname); 50 | } 51 | 52 | void loop(void){ 53 | if(myESP.loop() >= FULL_CONNECTION){ 54 | //regular loop code goes here 55 | } 56 | 57 | 58 | 59 | 60 | //handle saving a new network config 61 | if(configPage.handle()){ 62 | Serial.println("Saving new network config and restarting..."); 63 | myESP.saveConfigFile(configPage.getConfig(), "/netConfig.json"); 64 | delay(500); 65 | ESP.restart(); 66 | } 67 | 68 | delay(5); 69 | } 70 | 71 | 72 | 73 | 74 | //attempt to load a network configuration from the filesystem 75 | void loadConfig(){ 76 | //check for a good config file and start ESPHelper with the file stored on the ESP 77 | if(ESPHelperFS::begin()){ 78 | Serial.println("Filesystem loaded - Loading Config"); 79 | if(ESPHelperFS::validateConfig("/netConfig.json") == GOOD_CONFIG){ 80 | Serial.println("Config loaded"); 81 | delay(10); 82 | myESP.begin("/netConfig.json"); 83 | } 84 | 85 | //if no good config can be loaded (no file/corruption/etc.) then 86 | //attempt to generate a new config and restart the module 87 | else{ 88 | Serial.println("Could not load config - saving new config from default values and restarting"); 89 | delay(10); 90 | ESPHelperFS::createConfig(&homeNet, "/netConfig.json"); 91 | ESPHelperFS::end(); 92 | ESP.restart(); 93 | } 94 | } 95 | 96 | //if the filesystem cannot be started, just fail over to the 97 | //built in network config hardcoded in here 98 | else{ 99 | Serial.println("Could not load filesystem, proceeding with default config values"); 100 | delay(10); 101 | myESP.begin(&homeNet); 102 | } 103 | 104 | //load the netInfo from espHelper 105 | config = myESP.getNetInfo(); 106 | } 107 | -------------------------------------------------------------------------------- /static/config.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | System Configuration 6 | 17 | 18 | 19 |
20 |
21 |

ESP8266 System Configuration

22 |
23 |
24 | Device Name:
25 |
26 | SSID:
27 |
28 | SSID Password:
29 |
30 | OTA Password:
31 |
32 |
33 |

MQTT Settings

34 | MQTT Host (IP):
35 |
36 | MQTT User:
37 |
38 | MQTT Port:
39 |
40 | MQTT Password:
41 |
42 |
43 |

MQTT Will Settings

44 | Will Topic:
45 |
46 | Will Message:
47 |
48 | Will QoS:
49 |
50 | Will Retain:
51 |
55 |

Press Submit to update ESP8266 config file

56 | 57 |
58 |

Go to Device Status Page

59 |
60 |
61 | 62 | -------------------------------------------------------------------------------- /examples/GettingStarted/buttonDemo/buttonDemo.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 ItKindaWorks All right reserved. 3 | github.com/ItKindaWorks 4 | 5 | This file is part of ESPHelper 6 | 7 | ESPHelper is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | ESPHelper is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with ESPHelper. If not, see . 19 | */ 20 | 21 | /* 22 | This is a simple demo of an MQTT enabled button. A button is attahed 23 | to pin 0 with a pull-up resistor and each time the button is pressed, 24 | it toggles the state and publishes the new state to the MQTT broker. 25 | It is also subscribed to the same topic that it publishes to so that 26 | the state of the toggle can be updated from the MQTT side. 27 | */ 28 | #include "ESPHelper.h" 29 | 30 | #define TOPIC "/your/mqtt/topic" 31 | #define NETWORK_HOSTNAME "YOUR OTA HOSTNAME" 32 | #define OTA_PASSWORD "YOUR OTA PASSWORD" 33 | 34 | #define BUTTON_PIN 0 //button on pin 0 with pull up resistor (pulled low on press) 35 | #define BLINK_PIN 1 36 | 37 | 38 | char* buttonTopic = TOPIC; 39 | char* hostnameStr = NETWORK_HOSTNAME; 40 | 41 | const int buttonPin = BUTTON_PIN; 42 | const int blinkPin = BLINK_PIN; 43 | 44 | bool currentState = false; 45 | 46 | bool lastButtonState = false; 47 | 48 | //set this info for your own network 49 | netInfo homeNet = { .mqttHost = "YOUR MQTT-IP", //can be blank if not using MQTT 50 | .mqttUser = "YOUR MQTT USERNAME", //can be blank 51 | .mqttPass = "YOUR MQTT PASSWORD", //can be blank 52 | .mqttPort = 1883, //default port for MQTT is 1883 - only chance if needed. 53 | .ssid = "YOUR SSID", 54 | .pass = "YOUR NETWORK PASS"}; 55 | 56 | ESPHelper myESP(&homeNet); 57 | 58 | void setup() { 59 | //setup ota on esphelper 60 | myESP.OTA_enable(); 61 | myESP.OTA_setPassword(OTA_PASSWORD); 62 | myESP.OTA_setHostnameWithVersion(hostnameStr); 63 | 64 | //enable the connection heartbeat 65 | myESP.enableHeartbeat(blinkPin); 66 | 67 | //subscribe to the button topic (this allows outside control of the state of the switch) 68 | myESP.addSubscription(buttonTopic); 69 | 70 | //setup the mqtt callback function 71 | myESP.setMQTTCallback(callback); 72 | 73 | //start ESPHelper 74 | myESP.begin(); 75 | 76 | //set the button pin as an input 77 | pinMode(buttonPin, INPUT); 78 | } 79 | 80 | 81 | void loop(){ 82 | if(myESP.loop() == FULL_CONNECTION){ 83 | 84 | //read the button (low on press, high on release) 85 | bool buttonState = digitalRead(buttonPin); 86 | 87 | //if the button is pressed (LOW) and previously was not pressed(HIGH) 88 | if(buttonState == LOW && lastButtonState == HIGH){ 89 | //invert the current state 90 | currentState = !currentState; 91 | 92 | //publish to mqtt based on current state 93 | if(currentState){ 94 | myESP.publish(buttonTopic, "1", true); 95 | } 96 | else{ 97 | myESP.publish(buttonTopic, "0", true); 98 | } 99 | 100 | //set the lastButtonState to LOW to prevent multiple triggers 101 | lastButtonState = LOW; 102 | 103 | //wait half a second (poor mans debounce) 104 | delay(500); 105 | } 106 | 107 | //else if the button is not pressed and set lastButtonState to HIGH 108 | else if(buttonState == HIGH){lastButtonState = HIGH;} 109 | } 110 | yield(); 111 | } 112 | 113 | 114 | void callback(char* topic, byte* payload, unsigned int length) { 115 | 116 | //if the payload is '1' then set the state to true 117 | if(payload[0] == '1'){ 118 | currentState = true; 119 | } 120 | 121 | //otherwise set the current state to false 122 | else{ 123 | currentState = false; 124 | } 125 | } 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /examples/GettingStarted/RelayControlV2/RelayControlV2.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 ItKindaWorks All right reserved. 3 | github.com/ItKindaWorks 4 | 5 | This file is part of ESPHelper 6 | 7 | ESPHelper is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | ESPHelper is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with ESPHelper. If not, see . 19 | */ 20 | 21 | #include "ESPHelper.h" 22 | 23 | #define TOPIC "/your/mqtt/topic" 24 | #define STATUS TOPIC "/status" //dont change this - this is for the status topic which is whatever your mqtt topic is plus /status (ex /home/light/status) 25 | 26 | #define NETWORK_HOSTNAME "YOUR OTA HOST NAME" 27 | #define OTA_PASSWORD "YOUR OTA PASSWORD" 28 | 29 | #define RELAY_PIN 3 //rx pin on esp 30 | #define BLINK_PIN 1 //tx/led on esp-01 31 | #define BUTTON_PIN 0 32 | 33 | 34 | char* relayTopic = TOPIC; 35 | char* statusTopic = STATUS; 36 | char* hostnameStr = NETWORK_HOSTNAME; 37 | char* otaPassword = OTA_PASSWORD; 38 | 39 | //initialized the pins 40 | const int buttonPin = BUTTON_PIN; 41 | const int relayPin = RELAY_PIN; 42 | const int blinkPin = BLINK_PIN; 43 | 44 | //this is the current state of the relay 45 | bool currentState = false; 46 | 47 | //flag to mark when a rising edge has been detected 48 | bool risingEdge = false; 49 | 50 | //keeps track of the previous button state for edge detection 51 | bool lastButtonState = false; 52 | 53 | //set this info for your own network 54 | netInfo homeNet = { .mqttHost = "YOUR MQTT-IP", //can be blank if not using MQTT 55 | .mqttUser = "YOUR MQTT USERNAME", //can be blank 56 | .mqttPass = "YOUR MQTT PASSWORD", //can be blank 57 | .mqttPort = 1883, //default port for MQTT is 1883 - only chance if needed. 58 | .ssid = "YOUR SSID", 59 | .pass = "YOUR NETWORK PASS"}; 60 | 61 | ESPHelper myESP(&homeNet); 62 | 63 | 64 | void setup() { 65 | 66 | //setup Arduino OTA 67 | myESP.OTA_enable(); 68 | myESP.OTA_setPassword(otaPassword); 69 | myESP.OTA_setHostnameWithVersion(hostnameStr); 70 | 71 | //enable the connection heartbeat 72 | myESP.enableHeartbeat(blinkPin); 73 | 74 | //add a subscription to the relatTopic 75 | myESP.addSubscription(relayTopic); 76 | 77 | //add in the MQTT callback 78 | myESP.setMQTTCallback(callback); 79 | 80 | //start ESPHelper 81 | myESP.begin(); 82 | 83 | //set the button as an input 84 | pinMode(buttonPin, INPUT); 85 | 86 | //set the relay pin as an output and set to off 87 | pinMode(relayPin, OUTPUT); 88 | digitalWrite(relayPin, LOW); 89 | } 90 | 91 | 92 | void loop(){ 93 | if(myESP.loop() == FULL_CONNECTION){ 94 | 95 | //read the state of the button (LOW is pressed) 96 | bool buttonState = digitalRead(buttonPin); 97 | 98 | //if the button is pressed (LOW) and previously was not pressed(HIGH) 99 | if(buttonState == LOW && lastButtonState == HIGH){ 100 | 101 | //debounce the signal and if the button is still pressed, mark as a rising edge 102 | delay(50); 103 | if(digitalRead(buttonPin) == LOW){ 104 | lastButtonState = LOW; 105 | risingEdge = true; 106 | } 107 | 108 | } 109 | 110 | //else if the button is not pressed and set lastButtonState to HIGH and reset the rising edge flag 111 | else if(buttonState == HIGH){ 112 | lastButtonState = HIGH; 113 | risingEdge = false; 114 | } 115 | 116 | 117 | //rising edge detected 118 | if(risingEdge){ 119 | 120 | //set the state to the opposite of the current state 121 | setState(!currentState); 122 | 123 | //reset the rising edge flag 124 | risingEdge = false; 125 | 126 | } 127 | } 128 | yield(); 129 | } 130 | 131 | 132 | void callback(char* topic, byte* payload, unsigned int length) { 133 | if(payload[0] == '1'){ 134 | setState(true); 135 | } 136 | else{ 137 | setState(false); 138 | } 139 | } 140 | 141 | //sets a new state for the relay and publishes to MQTT 142 | void setState(bool newState){ 143 | 144 | //dont do anything unless the newState and currentState are different 145 | if(newState != currentState){ 146 | 147 | //set the current state 148 | currentState = newState; 149 | 150 | //publish to the MQTT status topic 151 | if(currentState){myESP.publish(statusTopic, "1", true);} 152 | else{myESP.publish(statusTopic, "0", true);} 153 | 154 | //set the relay on or off 155 | digitalWrite(relayPin, currentState); 156 | } 157 | } 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /examples/AdvancedFeatures/configServerApDemo/configServerApDemo.ino: -------------------------------------------------------------------------------- 1 | /* 2 | configServerApDemo.ino 3 | Copyright (c) 2017 ItKindaWorks All right reserved. 4 | github.com/ItKindaWorks 5 | 6 | This file is part of ESPHelper 7 | 8 | ESPHelper is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | ESPHelper is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with ESPHelper. If not, see . 20 | */ 21 | 22 | #include "ESPHelper.h" 23 | #include "ESPHelperFS.h" 24 | #include "ESPHelperWebConfig.h" 25 | #include "Metro.h" 26 | 27 | netInfo config; 28 | ESPHelper myESP; 29 | ESPHelperWebConfig configPage(80, "/"); 30 | 31 | netInfo homeNet = { .mqttHost = "YOUR MQTT-IP", //can be blank if not using MQTT 32 | .mqttUser = "YOUR MQTT USERNAME", //can be blank 33 | .mqttPass = "YOUR MQTT PASSWORD", //can be blank 34 | .mqttPort = 1883, //default port for MQTT is 1883 - only chance if needed. 35 | .ssid = "YOUR SSID", 36 | .pass = "YOUR NETWORK PASS", 37 | .otaPassword = "YOUR OTA PASS", 38 | .hostname = "NEW-ESP8266"}; 39 | 40 | Metro connectTimeout = Metro(20000); 41 | bool timeout = false; 42 | 43 | const char* broadcastSSID = "ESP-Hotspot"; 44 | const char* broadcastPASS = ""; 45 | IPAddress broadcastIP = {192, 168, 1, 1}; 46 | 47 | 48 | 49 | void setup(void){ 50 | Serial.begin(115200); 51 | 52 | //print some debug 53 | Serial.println("Starting Up - Please Wait..."); 54 | delay(100); 55 | 56 | //startup the wifi and web server 57 | startWifi(); 58 | configPage.begin(config.hostname); 59 | } 60 | 61 | 62 | 63 | void loop(void){ 64 | //get the current status of ESPHelper 65 | int espHelperStatus = myESP.loop(); 66 | manageESPHelper(espHelperStatus); 67 | 68 | if(espHelperStatus >= FULL_CONNECTION){ 69 | //regular loop code goes here 70 | } 71 | 72 | 73 | delay(5); 74 | } 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | //ESPHelper & config setup and runtime handler functions 87 | 88 | 89 | 90 | void manageESPHelper(int wifiStatus){ 91 | //if the unit is broadcasting or connected to wifi then reset the timeout vars 92 | if(wifiStatus == BROADCAST || wifiStatus >= WIFI_ONLY){ 93 | connectTimeout.reset(); 94 | timeout = false; 95 | } 96 | //otherwise check for a timeout condition and handle setting up broadcast 97 | else if(wifiStatus < WIFI_ONLY){ 98 | checkForWifiTimeout(); 99 | } 100 | //handle saving a new network config 101 | if(configPage.handle()){ 102 | Serial.println("Saving new network config and restarting..."); 103 | myESP.saveConfigFile(configPage.getConfig(), "/netConfig.json"); 104 | delay(500); 105 | ESP.restart(); 106 | } 107 | } 108 | 109 | 110 | 111 | void startWifi(){ 112 | loadConfig(); 113 | 114 | //setup other ESPHelper info and enable OTA updates 115 | myESP.setHopping(false); 116 | myESP.OTA_setPassword(config.otaPassword); 117 | myESP.OTA_setHostnameWithVersion(config.hostname); 118 | myESP.OTA_enable(); 119 | 120 | Serial.println("Connecting to network"); 121 | delay(10); 122 | //connect to wifi before proceeding. If cannot connect then switch to ap mode and create a network to config from 123 | while(myESP.loop() < WIFI_ONLY){ 124 | checkForWifiTimeout(); 125 | if(timeout){return;} 126 | delay(10); 127 | } 128 | 129 | Serial.println("Sucess!"); 130 | Serial.println(String("To connect to this device go to " + String(myESP.getIP()))); 131 | } 132 | 133 | //attempt to load a network configuration from the filesystem 134 | void loadConfig(){ 135 | //check for a good config file and start ESPHelper with the file stored on the ESP 136 | if(ESPHelperFS::begin()){ 137 | Serial.println("Filesystem loaded - Loading Config"); 138 | if(ESPHelperFS::validateConfig("/netConfig.json") == GOOD_CONFIG){ 139 | Serial.println("Config loaded"); 140 | delay(10); 141 | myESP.begin("/netConfig.json"); 142 | } 143 | 144 | //if no good config can be loaded (no file/corruption/etc.) then 145 | //attempt to generate a new config and restart the module 146 | else{ 147 | Serial.println("Could not load config - saving new config from default values and restarting"); 148 | delay(10); 149 | ESPHelperFS::createConfig(&homeNet, "/netConfig.json"); 150 | ESPHelperFS::end(); 151 | ESP.restart(); 152 | } 153 | } 154 | 155 | //if the filesystem cannot be started, just fail over to the 156 | //built in network config hardcoded in here 157 | else{ 158 | Serial.println("Could not load filesystem, proceeding with default config values"); 159 | delay(10); 160 | myESP.begin(&homeNet); 161 | } 162 | 163 | //load the netInfo from espHelper 164 | config = myESP.getNetInfo(); 165 | } 166 | 167 | 168 | //function that checks for no network connection for a period of time 169 | //and starting up AP mode when that time has elapsed 170 | void checkForWifiTimeout(){ 171 | if(connectTimeout.check() && !timeout){ 172 | Serial.println("Network Connection timeout - starting broadcast (AP) mode..."); 173 | timeout = true; 174 | myESP.broadcastMode(broadcastSSID, broadcastPASS, broadcastIP); 175 | myESP.OTA_setPassword(config.otaPassword); 176 | myESP.OTA_setHostnameWithVersion(config.hostname); 177 | myESP.OTA_enable(); 178 | } 179 | } 180 | 181 | 182 | -------------------------------------------------------------------------------- /src/ESPHelper.h: -------------------------------------------------------------------------------- 1 | /* 2 | ESPHelper.h 3 | Copyright (c) 2019 ItKindaWorks Inc All right reserved. 4 | github.com/ItKindaWorks 5 | 6 | This file is part of ESPHelper 7 | 8 | ESPHelper is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | ESPHelper is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with ESPHelper. If not, see . 20 | */ 21 | 22 | 23 | 24 | #ifndef ESP_HELPER_H 25 | #define ESP_HELPER_H 26 | 27 | #ifdef ESP32 28 | #include 29 | #include 30 | #endif 31 | 32 | 33 | #ifdef ESP8266 34 | #include 35 | #include 36 | #include 37 | #endif 38 | 39 | 40 | #include 41 | #include 42 | #include 43 | // #include 44 | #include 45 | 46 | #include 47 | 48 | #include "sharedData.h" 49 | #include "Metro.h" 50 | 51 | 52 | #define PUB_SUB_VERSION 28 53 | 54 | 55 | // #define DEBUG 56 | 57 | #ifdef DEBUG 58 | #define debugPrint(x) Serial.print(x) //debug on 59 | #define debugPrintln(x) Serial.println(x) //debug on 60 | #else 61 | #define debugPrint(x) {;} //debug off 62 | #define debugPrintln(x) {;} //debug off 63 | #endif 64 | 65 | 66 | void printNetInfo(const NetInfo *net, const char* header, bool printMQTT = true, bool printWill = true); 67 | 68 | 69 | class ESPHelper{ 70 | 71 | public: 72 | ESPHelper(); 73 | ESPHelper(const NetInfo *startingNet, bool storeLocal = true); 74 | 75 | bool begin(); 76 | bool begin(const NetInfo *startingNet, bool storeLocal = true); 77 | void end(); 78 | 79 | void useSecureClient(const char* fingerprint); 80 | 81 | void broadcastMode(const char* ssid, const char* password, const IPAddress ip); 82 | void disableBroadcast(); 83 | 84 | int loop(); 85 | 86 | bool subscribe(const char* topic, int qos); 87 | bool addSubscription(const char* topic); 88 | bool removeSubscription(const char* topic); 89 | bool unsubscribe(const char* topic); 90 | 91 | void publish(const char* topic, const char* payload); 92 | void publish(const char* topic, const char* payload, bool retain); 93 | boolean publishJson(const char* topic, JsonDocument& doc, bool retain); 94 | 95 | 96 | bool setCallback(MQTT_CALLBACK_SIGNATURE); 97 | void setMQTTCallback(MQTT_CALLBACK_SIGNATURE); 98 | 99 | void setWifiCallback(void (*callback)()); 100 | void setWifiLostCallback(void (*callback)()); 101 | 102 | void reconnect(); 103 | 104 | void updateNetwork(); //manually disconnect and reconnecting to network/mqtt using current values (generally called after setting new network values) 105 | 106 | const char* getSSID(); 107 | void setSSID(const char *ssid); 108 | 109 | const char* getPASS(); 110 | void setPASS(const char *pass); 111 | 112 | const char* getMQTTIP(); 113 | void setMQTTIP(const char *mqttIP); 114 | void setMQTTIP(const char *mqttIP, const char *mqttUser, const char *mqttPass); 115 | 116 | int getMQTTQOS(); 117 | void setMQTTQOS(int qos); 118 | 119 | void setWill(const char *willTopic, const char *willMessage); 120 | void setWill(const char *willTopic, const char *willMessage, const int willQoS, const bool willRetain); 121 | 122 | String getIP(); 123 | IPAddress getIPAddress(); 124 | 125 | int getStatus(); 126 | 127 | 128 | NetInfo* getNetInfo(); 129 | 130 | void listSubscriptions(); 131 | 132 | void OTA_enable(); 133 | void OTA_disable(); 134 | void OTA_begin(); 135 | void OTA_setPassword(const char* pass); 136 | void OTA_setHostname(const char* hostname); 137 | void OTA_setHostnameWithVersion(const char* hostname); 138 | char* getHostname(); 139 | PubSubClient* getMQTTClient(); 140 | bool setMQTTBuffer(int size); 141 | 142 | String macToStr(const uint8_t* mac); 143 | 144 | void resubscribe(); 145 | 146 | private: 147 | 148 | void init(); 149 | void validateConfig(); 150 | 151 | 152 | 153 | int setConnectionStatus(); 154 | 155 | NetInfo _currentNet; 156 | 157 | PubSubClient client; 158 | 159 | Metro reconnectMetro = Metro(500); 160 | 161 | WiFiClient wifiClient; 162 | WiFiClientSecure wifiClientSecure; 163 | const char* _fingerprint; 164 | bool _useSecureClient = false; 165 | 166 | 167 | String _clientName; 168 | 169 | void (*_wifiCallback)(); 170 | bool _wifiCallbackSet = false; 171 | 172 | void (*_wifiLostCallback)(); 173 | bool _wifiLostCallbackSet = false; 174 | 175 | std::function _mqttCallback; 176 | // #ifdef ESP8266 177 | // std::function _mqttCallback; 178 | // #endif 179 | // #ifdef ESP32 180 | // void(*_mqttCallback)(char*, uint8_t*, unsigned int) ; 181 | // #endif 182 | 183 | bool _mqttCallbackSet = false; 184 | 185 | int _connectionStatus = NO_CONNECTION; 186 | 187 | //AP mode variables 188 | IPAddress _broadcastIP; 189 | char _broadcastSSID[64]; 190 | char _broadcastPASS[64]; 191 | 192 | bool _ssidSet = false; 193 | bool _passSet = false; 194 | bool _mqttSet = false; 195 | bool _mqttUserSet = false; 196 | bool _mqttPassSet = false; 197 | bool _willTopicSet = false; 198 | bool _willMessageSet = false; 199 | 200 | bool _useOTA = false; 201 | bool _OTArunning = false; 202 | 203 | 204 | bool _hasBegun = false; 205 | 206 | 207 | subscription _subscriptions[MAX_SUBSCRIPTIONS]; 208 | 209 | char _hostname[64]; 210 | 211 | int _qos = DEFAULT_QOS; 212 | 213 | IPAddress _apIP = IPAddress(192, 168, 1, 1); 214 | 215 | 216 | }; 217 | 218 | #endif 219 | -------------------------------------------------------------------------------- /src/sharedData.h: -------------------------------------------------------------------------------- 1 | /* 2 | sharedTypes.h 3 | Copyright (c) 2019 ItKindaWorks Inc All right reserved. 4 | github.com/ItKindaWorks 5 | 6 | This file is part of ESPHelper 7 | 8 | ESPHelper is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | ESPHelper is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with ESPHelper. If not, see . 20 | */ 21 | 22 | 23 | 24 | #ifndef SHARED_TYPES_H 25 | #define SHARED_TYPES_H 26 | 27 | #include 28 | 29 | 30 | #define VERSION "2-0-4" 31 | 32 | 33 | 34 | //Maximum number of subscriptions that can be auto-subscribed 35 | //feel free to change this if you need more subsciptions 36 | #define MAX_SUBSCRIPTIONS 25 37 | 38 | #define DEFAULT_QOS 1; //at least once - devices are guarantee to get a message. 39 | 40 | #define MAX_TOPIC_LENGTH 128 41 | 42 | 43 | enum connStatus {NO_CONNECTION, BROADCAST, ROAMING, WIFI_ONLY, FULL_CONNECTION}; 44 | 45 | struct ESPHelperConf { 46 | char mqttHost[32]; 47 | char mqttUser[16]; 48 | char mqttPass[16]; 49 | int mqttPort; 50 | char ssid[32]; 51 | char pass[32]; 52 | char otaPassword[16]; 53 | char hostname[32]; 54 | char willTopic[128]; 55 | char willMessage[32]; 56 | int willQoS; 57 | bool willRetain; 58 | }; 59 | 60 | class NetInfo { 61 | public: 62 | 63 | /** 64 | * @brief Construct a new NetInfo object with local storage 65 | * 66 | */ 67 | explicit NetInfo() { 68 | _conf = new ESPHelperConf(); 69 | _storeLocal = true; 70 | clear(); 71 | } 72 | 73 | /** 74 | * @brief Construct a new NetInfo object with external storage 75 | * 76 | * @param externalConf Pointer to an existing ESPHelperConf object 77 | */ 78 | explicit NetInfo(ESPHelperConf& externalConf) { 79 | _conf = &externalConf; 80 | _storeLocal = false; 81 | } 82 | 83 | // Deleted copy operations for memory safety 84 | NetInfo(const NetInfo& other) = delete; 85 | NetInfo& operator=(const NetInfo& other) = delete; 86 | 87 | /** 88 | * @brief Destroy the Net Info object 89 | * 90 | */ 91 | ~NetInfo() { 92 | if (_storeLocal && _conf != nullptr) { 93 | delete _conf; 94 | } 95 | } 96 | 97 | 98 | 99 | 100 | // ESPHelperConf* getRawConfPtr() const { 101 | // return _conf; 102 | // } 103 | 104 | /* * @brief Use an external ESPHelperConf object for storage 105 | * 106 | * This method allows the NetInfo instance to use an existing ESPHelperConf object 107 | * instead of managing its own memory. It is useful for sharing configurations. 108 | * 109 | * @param externalConf Pointer to an existing ESPHelperConf object 110 | */ 111 | void setExternalMemory(ESPHelperConf* externalConf) { 112 | if (_storeLocal && _conf != externalConf) { 113 | delete _conf; 114 | } 115 | _conf = externalConf; 116 | _storeLocal = false; 117 | } 118 | 119 | /** 120 | * @brief Clone the current configuration to another NetInfo object 121 | * 122 | * @param target The target NetInfo object to clone to 123 | * @param storeLocal If true, the target will store a local copy of the configuration 124 | */ 125 | void cloneTo(NetInfo& target, bool storeLocal) const { 126 | if (storeLocal) { 127 | target.setToLocalCopy(*_conf); 128 | } else { 129 | target.setExternalMemory(_conf); 130 | } 131 | } 132 | 133 | // const ESPHelperConf* getConfPointer() const { return _conf; } 134 | 135 | void setMqttHost(const char* val) { safeCopy(_conf->mqttHost, val, sizeof(_conf->mqttHost)); } 136 | void setMqttUser(const char* val) { safeCopy(_conf->mqttUser, val, sizeof(_conf->mqttUser)); } 137 | void setMqttPass(const char* val) { safeCopy(_conf->mqttPass, val, sizeof(_conf->mqttPass)); } 138 | void setSsid(const char* val) { safeCopy(_conf->ssid, val, sizeof(_conf->ssid)); } 139 | void setPass(const char* val) { safeCopy(_conf->pass, val, sizeof(_conf->pass)); } 140 | void setOtaPassword(const char* val) { safeCopy(_conf->otaPassword, val, sizeof(_conf->otaPassword)); } 141 | void setHostname(const char* val) { safeCopy(_conf->hostname, val, sizeof(_conf->hostname)); } 142 | void setMqttWillTopic(const char* val) { safeCopy(_conf->willTopic, val, sizeof(_conf->willTopic)); } 143 | void setMqttWillMessage(const char* val) { safeCopy(_conf->willMessage, val, sizeof(_conf->willMessage)); } 144 | void setMqttPort(int val) { _conf->mqttPort = val; } 145 | void setMqttWillRetain(bool val) { _conf->willRetain = val; } 146 | void setMqttWillQoS(int val) { _conf->willQoS = val; } 147 | 148 | const char* getMqttHost() const { return _conf->mqttHost; } 149 | const char* getMqttUser() const { return _conf->mqttUser; } 150 | const char* getMqttPass() const { return _conf->mqttPass; } 151 | const char* getSsid() const { return _conf->ssid; } 152 | const char* getPass() const { return _conf->pass; } 153 | const char* getOtaPassword() const { return _conf->otaPassword; } 154 | const char* getHostname() const { return _conf->hostname; } 155 | const char* getMqttWillTopic() const { return _conf->willTopic; } 156 | const char* getMqttWillMessage() const { return _conf->willMessage; } 157 | int getMqttPort() const { return _conf->mqttPort; } 158 | int getMqttWillQoS() const { return _conf->willQoS; } 159 | bool getMqttWillRetain() const { return _conf->willRetain; } 160 | 161 | private: 162 | 163 | //denotes whether the ESPHelperConf is stored in external memory or locally within this instance 164 | bool _storeLocal = true; 165 | ESPHelperConf* _conf = nullptr; 166 | 167 | /* * @brief Safely copy a string to a destination buffer 168 | * 169 | * This method copies a string from source to destination, ensuring that the destination 170 | * does not overflow. It also ensures that the destination is null-terminated. 171 | * 172 | * @param dest Pointer to the destination buffer 173 | * @param src Pointer to the source string 174 | * @param maxLen Maximum length of the destination buffer 175 | */ 176 | void safeCopy(char* dest, const char* src, size_t maxLen) { 177 | if (src) { 178 | strncpy(dest, src, maxLen - 1); 179 | dest[maxLen - 1] = '\0'; 180 | } else if (dest && maxLen > 0) { 181 | dest[0] = '\0'; 182 | } 183 | } 184 | 185 | /* * @brief Clear the configuration data 186 | * 187 | * This method resets all fields in the ESPHelperConf structure to their default values. 188 | * It is useful for initializing or resetting the configuration. 189 | */ 190 | void clear() { 191 | memset(_conf, 0, sizeof(ESPHelperConf)); 192 | _conf->mqttPort = 1883; 193 | _conf->willQoS = 1; 194 | _conf->willRetain = true; 195 | } 196 | 197 | 198 | 199 | 200 | /* * @brief Set the NetInfo to a local copy of the provided ESPHelperConf 201 | * 202 | * This method creates a local copy of the provided ESPHelperConf object. 203 | * It is useful when you want to ensure that the NetInfo instance has its own copy 204 | * of the configuration data. 205 | * 206 | * @param conf The ESPHelperConf object to copy from 207 | */ 208 | void setToLocalCopy(const ESPHelperConf& conf) { 209 | if (!_storeLocal) { 210 | _conf = new ESPHelperConf(); 211 | _storeLocal = true; 212 | } 213 | memcpy(_conf, &conf, sizeof(ESPHelperConf)); 214 | } 215 | 216 | 217 | }; 218 | // typedef struct NetInfo NetInfo; 219 | 220 | 221 | struct subscription{ 222 | bool isUsed = false; 223 | const char* topic; 224 | }; 225 | typedef struct subscription subscription; 226 | 227 | 228 | 229 | 230 | #endif -------------------------------------------------------------------------------- /examples/AdvancedFeatures/configAndStatusApDemo/configAndStatusApDemo.ino: -------------------------------------------------------------------------------- 1 | /* 2 | configAndStatusAPDemo.ino 3 | Copyright (c) 2017 ItKindaWorks All right reserved. 4 | github.com/ItKindaWorks 5 | 6 | This file is part of ESPHelper 7 | 8 | ESPHelper is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | ESPHelper is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with ESPHelper. If not, see . 20 | */ 21 | 22 | #include "ESPHelper.h" 23 | #include "ESPHelperFS.h" 24 | #include "ESPHelperWebConfig.h" 25 | #include "Metro.h" 26 | 27 | 28 | //enable this to allow the device to reset if it goes into broadcast mode 29 | //for more than a set amount of time (3 min by default). This is useful 30 | //in environments where the device has a weak network signal and experiences 31 | //occasional dropouts. 32 | bool BROADCAST_TIMEOUT_EN = false; 33 | 34 | netInfo config; 35 | ESPHelper myESP; 36 | 37 | 38 | //setup a server on port 80 (http). We use an external server here because we want more than just a config page 39 | //but also a status page or anything else that we want to display 40 | ESP8266WebServer server(80); 41 | ESPHelperWebConfig configPage(&server, "/config"); 42 | 43 | //defualt net info for unconfigured devices 44 | netInfo homeNet = { .mqttHost = "YOUR MQTT-IP", //can be blank if not using MQTT 45 | .mqttUser = "YOUR MQTT USERNAME", //can be blank 46 | .mqttPass = "YOUR MQTT PASSWORD", //can be blank 47 | .mqttPort = 1883, //default port for MQTT is 1883 - only chance if needed. 48 | .ssid = "YOUR SSID", 49 | .pass = "YOUR NETWORK PASS", 50 | .otaPassword = "YOUR OTA PASS", 51 | .hostname = "NEW-ESP8266"}; 52 | 53 | //timeout before starting AP mode for configuration 54 | Metro connectTimeout = Metro(20000); 55 | Metro broadcastModeTimeout = Metro(180000); 56 | bool timeout = false; 57 | 58 | //AP moade setup info 59 | const char* broadcastSSID = "ESP-Hotspot"; 60 | const char* broadcastPASS = ""; 61 | IPAddress broadcastIP = {192, 168, 1, 1}; 62 | 63 | 64 | 65 | 66 | void setup(void){ 67 | Serial.begin(115200); 68 | 69 | //print some debug 70 | Serial.println("Starting Up - Please Wait..."); 71 | delay(100); 72 | 73 | //startup the wifi and web server (more in the lines below) 74 | startWifi(); 75 | 76 | //setup the http server and config page (fillConfig will take the netInfo file and use that for 77 | //default values) 78 | configPage.fillConfig(&config); 79 | configPage.begin(config.hostname); 80 | 81 | // Actually start the server (again this would be done automatically 82 | //if we were just using the config page and didnt use an external server...) 83 | server.begin(); 84 | server.on("/", HTTP_GET, handleStatus); 85 | } 86 | 87 | 88 | 89 | void loop(void){ 90 | //get the current status of ESPHelper 91 | int espHelperStatus = myESP.loop(); 92 | manageESPHelper(espHelperStatus); 93 | 94 | if(espHelperStatus >= FULL_CONNECTION){ 95 | //regular loop code goes here 96 | } 97 | 98 | 99 | delay(5); 100 | } 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | //ESPHelper & config setup and runtime handler functions 113 | 114 | 115 | 116 | void manageESPHelper(int wifiStatus){ 117 | if (BROADCAST_TIMEOUT_EN && wifiStatus == BROADCAST && broadcastModeTimeout.check()) 118 | { 119 | Serial.println("BROADCAST mode timeout and reset ESP"); 120 | ESP.reset(); 121 | delay(5000); 122 | } 123 | //if the unit is broadcasting or connected to wifi then reset the timeout vars 124 | if(wifiStatus == BROADCAST || wifiStatus >= WIFI_ONLY){ 125 | connectTimeout.reset(); 126 | timeout = false; 127 | } 128 | //otherwise check for a timeout condition and handle setting up broadcast 129 | else if(wifiStatus < WIFI_ONLY){ 130 | checkForWifiTimeout(); 131 | } 132 | //handle saving a new network config 133 | if(configPage.handle()){ 134 | Serial.println("Saving new network config and restarting..."); 135 | myESP.saveConfigFile(configPage.getConfig(), "/netConfig.json"); 136 | delay(500); 137 | ESP.restart(); 138 | } 139 | } 140 | 141 | 142 | 143 | void startWifi(){ 144 | loadConfig(); 145 | 146 | //setup other ESPHelper info and enable OTA updates 147 | myESP.setHopping(false); 148 | myESP.OTA_setPassword(config.otaPassword); 149 | myESP.OTA_setHostnameWithVersion(config.hostname); 150 | myESP.OTA_enable(); 151 | 152 | Serial.println("Connecting to network"); 153 | delay(10); 154 | //connect to wifi before proceeding. If cannot connect then switch to ap mode and create a network to config from 155 | while(myESP.loop() < WIFI_ONLY){ 156 | checkForWifiTimeout(); 157 | if(timeout){return;} 158 | delay(10); 159 | } 160 | 161 | Serial.println("Sucess!"); 162 | Serial.println(String("To connect to this device go to " + String(myESP.getIP()))); 163 | } 164 | 165 | //attempt to load a network configuration from the filesystem 166 | void loadConfig(){ 167 | //check for a good config file and start ESPHelper with the file stored on the ESP 168 | if(ESPHelperFS::begin()){ 169 | Serial.println("Filesystem loaded - Loading Config"); 170 | if(ESPHelperFS::validateConfig("/netConfig.json") == GOOD_CONFIG){ 171 | Serial.println("Config loaded"); 172 | delay(10); 173 | myESP.begin("/netConfig.json"); 174 | } 175 | 176 | //if no good config can be loaded (no file/corruption/etc.) then 177 | //attempt to generate a new config and restart the module 178 | else{ 179 | Serial.println("Could not load config - saving new config from default values and restarting"); 180 | delay(10); 181 | ESPHelperFS::createConfig(&homeNet, "/netConfig.json"); 182 | ESPHelperFS::end(); 183 | ESP.restart(); 184 | } 185 | } 186 | 187 | //if the filesystem cannot be started, just fail over to the 188 | //built in network config hardcoded in here 189 | else{ 190 | Serial.println("Could not load filesystem, proceeding with default config values"); 191 | delay(10); 192 | myESP.begin(&homeNet); 193 | } 194 | 195 | //load the netInfo from espHelper for use in the config page 196 | config = myESP.getNetInfo(); 197 | } 198 | 199 | 200 | //function that checks for no network connection for a period of time 201 | //and starting up AP mode when that time has elapsed 202 | void checkForWifiTimeout(){ 203 | if(connectTimeout.check() && !timeout){ 204 | Serial.println("Network Connection timeout - starting broadcast (AP) mode..."); 205 | timeout = true; 206 | myESP.broadcastMode(broadcastSSID, broadcastPASS, broadcastIP); 207 | myESP.OTA_setPassword(config.otaPassword); 208 | myESP.OTA_setHostnameWithVersion(config.hostname); 209 | myESP.OTA_enable(); 210 | broadcastModeTimeout.reset(); 211 | } 212 | } 213 | 214 | 215 | //main config page that allows user to enter in configuration info 216 | void handleStatus() { 217 | server.send(200, "text/html", \ 218 | String("\ 219 |
\ 220 | Device Info\ 221 |
\ 222 | \ 223 |

System Status
\ 224 | Device Name: " + String(myESP.getHostname()) + "
\ 225 | Connected SSID: " + String(myESP.getSSID()) + "
\ 226 | Device IP: " + String(myESP.getIP()) + "
\ 227 | Uptime (ms): " + String(millis()) + "

\ 228 | \ 229 | ")); 230 | } 231 | 232 | 233 | -------------------------------------------------------------------------------- /src/ESPHelperWebConfig.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ESPHelperWebConfig.cpp 3 | Copyright (c) 2017 ItKindaWorks All right reserved. 4 | github.com/ItKindaWorks 5 | 6 | This file is part of ESPHelper 7 | 8 | ESPHelper is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | ESPHelper is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with ESPHelper. If not, see . 20 | */ 21 | #include "ESPHelperWebConfig.h" 22 | 23 | 24 | ESPHelperWebConfig::ESPHelperWebConfig(int port, const char* URI){ 25 | _server = new AsyncWebServer(port); 26 | _runningLocal = true; 27 | createSafeStringFromCharArray(pageURI, _pageURI); 28 | pageURI.clear(); 29 | pageURI.print(URI); 30 | createSafeStringFromCharArray(resetURI, _resetURI); 31 | resetURI.clear(); 32 | } 33 | 34 | ESPHelperWebConfig::ESPHelperWebConfig(AsyncWebServer *server, const char* URI){ 35 | _server = server; 36 | _runningLocal = false; 37 | createSafeStringFromCharArray(pageURI, _pageURI); 38 | pageURI.clear(); 39 | pageURI.print(URI); 40 | createSafeStringFromCharArray(resetURI, _resetURI); 41 | resetURI.clear(); 42 | } 43 | 44 | ESPHelperWebConfig::~ESPHelperWebConfig(){ 45 | if(_runningLocal){ 46 | _server->end(); // Stop the server 47 | delete _server; 48 | } 49 | _server = nullptr; 50 | } 51 | 52 | 53 | bool ESPHelperWebConfig::begin(const char* _hostname){ 54 | MDNS.begin(_hostname); 55 | return begin(); 56 | } 57 | 58 | bool ESPHelperWebConfig::begin(){ 59 | createSafeStringFromCharArray(resetURI, _resetURI); 60 | createSafeStringFromCharArray(pageURI, _pageURI); 61 | // Serial.printf("[ESPHelperWebConfig] Starting web config server on %s\n", pageURI.c_str()); 62 | 63 | 64 | //setup server handlers 65 | //these handler function definitions use lambdas to pass the funtion... more information can be found here: 66 | //https://stackoverflow.com/questions/39803135/c-unresolved-overloaded-function-type 67 | 68 | _server->on(pageURI.c_str(), HTTP_GET, [this](AsyncWebServerRequest *request){ 69 | //send back the config page using the handleGet function as the template processor 70 | request->send_P(200, "text/html", static_config_html, sizeof(static_config_html), [this](const String& var){ 71 | return handleGet(var); 72 | }); 73 | }); 74 | 75 | _server->on(pageURI.c_str(), HTTP_POST, [this](AsyncWebServerRequest *request){ 76 | this->handlePost(request); 77 | }); 78 | 79 | if(_resetSet){ 80 | // Serial.printf("[ESPHelperWebConfig] Starting reset on %s\n", resetURI.c_str()); 81 | //if the reset URI has been set then add a handler for it 82 | _server->on(resetURI.c_str(), HTTP_POST, [this](AsyncWebServerRequest *request){ 83 | this->handleReset(request); 84 | }); 85 | } 86 | 87 | if(_runningLocal){ 88 | _server->begin(); // Actually start the server 89 | } 90 | 91 | 92 | return true; 93 | } 94 | 95 | void ESPHelperWebConfig::useConfig(NetInfo& config){ 96 | config.cloneTo(_config, false); 97 | } 98 | 99 | void ESPHelperWebConfig::fillConfig(bool preFill){ 100 | _preFill = preFill; 101 | } 102 | 103 | bool ESPHelperWebConfig::handle(){ 104 | // _server->handleClient(); 105 | return _configChanged; 106 | } 107 | 108 | NetInfo& ESPHelperWebConfig::getConfig(){ 109 | _configChanged = false; 110 | return _config; 111 | } 112 | 113 | 114 | 115 | 116 | 117 | //main config page that allows user to enter in configuration info 118 | String ESPHelperWebConfig::handleGet(const String& var){ 119 | 120 | if(var == "HELPER_PAGE_URI") 121 | return _pageURI; 122 | else if(var == "HELPER_HOSTNAME") 123 | return _config.getHostname(); 124 | else if(var == "HELPER_SSID") 125 | return _config.getSsid(); 126 | else if(var == "HELPER_MQTT_HOST") 127 | return _config.getMqttHost(); 128 | else if(var == "HELPER_MQTT_USER") 129 | return _config.getMqttUser(); 130 | else if(var == "HELPER_MQTT_PORT") 131 | return String(_config.getMqttPort()); 132 | else if(var == "HELPER_MQTT_WILL_TOPIC") 133 | return _config.getMqttWillTopic(); 134 | else if(var == "HELPER_MQTT_WILL_MESSAGE") 135 | return _config.getMqttWillMessage(); 136 | else if(var == "HELPER_MQTT_WILL_QOS") 137 | return String(_config.getMqttWillQoS()); 138 | else if(var == "HELPER_MQTT_WILL_RETAIN_0") 139 | return _config.getMqttWillRetain() ? "" : "selected"; 140 | else if(var == "HELPER_MQTT_WILL_RETAIN_1") 141 | return _config.getMqttWillRetain() ? "selected" : ""; 142 | 143 | return String(); 144 | } 145 | 146 | // If a POST request is made to URI /config 147 | void ESPHelperWebConfig::handlePost(AsyncWebServerRequest *request) { 148 | 149 | //make sure that all the arguments exist and that at least an SSID and hostname have been entered 150 | if( ! request->hasArg("ssid") || ! request->hasArg("netPass") 151 | || ! request->hasArg("hostname") || ! request->hasArg("mqttHost") 152 | || ! request->hasArg("mqttUser") || ! request->hasArg("mqttPass") 153 | || ! request->hasArg("mqttPort") || ! request->hasArg("otaPassword") 154 | || request->arg("ssid") == NULL || request->arg("hostname") == NULL){ // If the POST request doesn't have username and password data 155 | 156 | request->send(400, "text/plain", "400: Invalid Request - Did you make sure to specify an SSID and Hostname?"); // The request is invalid, so send HTTP status 400 157 | return; 158 | } 159 | 160 | //if there is an mqtt user/pass/port entered then there better also be a host! 161 | if((request->arg("mqttUser") != NULL || request->arg("mqttPass") != NULL || 162 | request->arg("mqttPort") != NULL) && request->arg("mqttHost") == NULL){ 163 | 164 | request->send(400, "text/html", 165 | String("
\ 166 | \ 167 |

400: Invalid Request - MQTT info specified without host

\ 168 |
\ 169 |

Open configuration page

").c_str()); 170 | return; 171 | } 172 | 173 | //convert the Strings returned by request->arg to char arrays that can be entered into NetInfo 174 | 175 | if(request->arg("netPass").length() != 0 or _preFill == false){ 176 | _config.setPass(request->arg("netPass").c_str()); 177 | } 178 | if(request->arg("mqttPass").length() != 0 or _preFill == false){ 179 | _config.setMqttPass(request->arg("mqttPass").c_str()); 180 | } 181 | if(request->arg("otaPassword").length() != 0 or _preFill == false){ 182 | _config.setOtaPassword(request->arg("otaPassword").c_str()); 183 | } 184 | 185 | _config.setSsid(request->arg("ssid").c_str()); 186 | _config.setHostname(request->arg("hostname").c_str()); 187 | _config.setMqttHost(request->arg("mqttHost").c_str()); 188 | _config.setMqttUser(request->arg("mqttUser").c_str()); 189 | 190 | //the port is special because it doesnt get stored as a string so we take care of that 191 | if(request->arg("mqttPort") != NULL){_config.setMqttPort(request->arg("mqttPort").toInt());} 192 | else{_config.setMqttPort(1883);} 193 | 194 | // MQTT Will fields 195 | if (request->hasArg("mqttWillTopic")) { 196 | _config.setMqttWillTopic(request->arg("mqttWillTopic").c_str()); 197 | } 198 | if (request->hasArg("mqttWillMessage")) { 199 | _config.setMqttWillMessage(request->arg("mqttWillMessage").c_str()); 200 | } 201 | if (request->hasArg("mqttWillQos")) { 202 | _config.setMqttWillQoS(request->arg("mqttWillQos").toInt()); 203 | } 204 | if (request->hasArg("mqttWillRetain")) { 205 | _config.setMqttWillRetain(request->arg("mqttWillRetain").toInt() == 1); 206 | } 207 | 208 | //tell the user that the config is loaded in and the module is restarting 209 | request->send(200, "text/html", 210 | String("
\ 211 | \ 212 | \ 213 |

Config info loaded

\ 214 |
\ 215 |

Open configuration page

\ 216 |

Wait for the ESP8266 to restart with the new settings

\ 217 |

You will be redirected to the home page in 10 seconds.

")); 218 | 219 | 220 | 221 | // printNetInfo(&_config, "[ESPHelperWeb Post]", true, true); 222 | 223 | _configChanged = true; 224 | } 225 | 226 | void ESPHelperWebConfig::setFlashReset(const char* uri){ 227 | createSafeStringFromCharArray(resetURI, _resetURI); 228 | resetURI.clear(); 229 | resetURI.print(uri); 230 | _resetSet = true; 231 | } 232 | 233 | void ESPHelperWebConfig::handleReset(AsyncWebServerRequest *request){ 234 | //tell the user that the config is loaded in and the module is restarting 235 | #warning "TODO: Implement flash reset from web config" 236 | // _server->send(200, "text/plain", String("Resetting SPIFFS and restarting with default values")); 237 | 238 | // LittleFS.format(); 239 | // ESP.restart(); 240 | } 241 | 242 | void ESPHelperWebConfig::handleNotFound(AsyncWebServerRequest *request){ 243 | request->send(404, "text/plain", "404: Not found"); // Send HTTP status 404 (Not Found) when there's no handler for the URI in the request 244 | } 245 | -------------------------------------------------------------------------------- /examples/InputOutput/RGBLight/RGBLight.ino: -------------------------------------------------------------------------------- 1 | /* 2 | wifiRGB.ino 3 | Copyright (c) 2017 ItKindaWorks All right reserved. 4 | github.com/ItKindaWorks 5 | 6 | This file is part of wifiRGB 7 | 8 | wifiRGB is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | wifiRGB is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with wifiRGB. If not, see . 20 | */ 21 | 22 | /* 23 | This is an MQTT RGB light controller program for the ESP8266. 24 | The light can be controlled in one of three ways. You can send 25 | an HSB update, RGB update, or moodlight mode on/off. 26 | All updates must be formatted correctly, see below: 27 | 28 | ex RGB string: "r255,050,000" (r = 255, g = 050, b = 000) 29 | ex HSB string: "h1.00,0.50,0.01" (h = 1.00, s = 0.50, b = 0.01) 30 | ex moodlight string: "m1" (moodlight activate) 31 | 32 | This program also posts a status update to the status topic 33 | which is the lightTopic plus "/status" (ex. if the lightTopic 34 | is "/home/RGBlight" then the statusTopic would be "home/RGBlight/status") 35 | */ 36 | 37 | #include "ESPHelper.h" 38 | #include 39 | #include "Metro.h" 40 | 41 | #define TOPIC "/your/mqtt/topic" 42 | #define STATUS TOPIC "/status" 43 | #define NETWORK_HOSTNAME "YOUR OTA HOSTNAME" 44 | #define OTA_PASSWORD "YOUR OTA PASSWORD" 45 | 46 | #define RED_PIN 12 47 | #define GREEN_PIN 13 48 | #define BLUE_PIN 14 49 | 50 | typedef struct lightState{ 51 | double hue; 52 | double saturation; 53 | double brightness; 54 | int red; 55 | int redRate; 56 | int green; 57 | int greenRate; 58 | int blue; 59 | int blueRate; 60 | int fadePeriod; 61 | int updateType; 62 | }; 63 | 64 | typedef struct timer { 65 | unsigned long previousTime; 66 | int interval; 67 | }; 68 | 69 | 70 | 71 | enum superModes {SET, MOOD}; 72 | enum moodColors{RED, GREEN, BLUE}; 73 | enum modes {NORMAL, FADING}; 74 | enum updateTypes{HSB, RGB, POWER}; 75 | 76 | 77 | lightState nextState; 78 | 79 | int superMode = SET; //overall mode of the light (moodlight, network controlled, etc) 80 | boolean newCommand = false; 81 | 82 | 83 | char* lightTopic = TOPIC; 84 | char* statusTopic = STATUS; 85 | char* hostnameStr = NETWORK_HOSTNAME; 86 | 87 | const int redPin = RED_PIN; 88 | const int greenPin = GREEN_PIN; 89 | const int bluePin = BLUE_PIN; 90 | 91 | char statusString[50]; //string containing the current setting for the light 92 | 93 | 94 | //set this info for your own network 95 | netInfo homeNet = { .mqttHost = "YOUR MQTT-IP", //can be blank if not using MQTT 96 | .mqttUser = "YOUR MQTT USERNAME", //can be blank 97 | .mqttPass = "YOUR MQTT PASSWORD", //can be blank 98 | .mqttPort = 1883, //default port for MQTT is 1883 - only chance if needed. 99 | .ssid = "YOUR SSID", 100 | .pass = "YOUR NETWORK PASS"}; 101 | 102 | ESPHelper myESP(&homeNet); 103 | 104 | 105 | void setup() { 106 | //initialize the light as an output and set to LOW (off) 107 | pinMode(redPin, OUTPUT); 108 | pinMode(bluePin, OUTPUT); 109 | pinMode(greenPin, OUTPUT); 110 | 111 | //all off 112 | digitalWrite(redPin, LOW); //all off 113 | digitalWrite(greenPin, LOW); 114 | digitalWrite(bluePin, LOW); 115 | 116 | delay(1000); 117 | 118 | colorTest(); 119 | 120 | //setup ota on esphelper 121 | myESP.OTA_enable(); 122 | myESP.OTA_setPassword(OTA_PASSWORD); 123 | myESP.OTA_setHostnameWithVersion(hostnameStr); 124 | 125 | //subscribe to the lighttopic 126 | myESP.setMQTTCallback(callback); 127 | myESP.addSubscription(lightTopic); 128 | myESP.begin(); 129 | } 130 | 131 | 132 | 133 | void loop(){ 134 | static bool connected = false; //keeps track of connection state to reset from MOOD to SET when network connection is made 135 | 136 | 137 | if(myESP.loop() == FULL_CONNECTION){ 138 | 139 | //if the light was previously not connected to wifi and mqtt, update the status topic with the light being off 140 | if(!connected){ 141 | connected = true; //we have reconnected so now we dont need to flag the setting anymore 142 | myESP.publish(statusTopic, "h0.00,0.00,0.00 ", true); 143 | } 144 | 145 | lightHandler(); 146 | } 147 | 148 | 149 | yield(); 150 | } 151 | 152 | 153 | void lightHandler(){ 154 | //new and current lightStates 155 | static lightState newState; 156 | static lightState currentState; 157 | 158 | static int currentMoodColor = 0; //the current moodlight chosen color 159 | 160 | static int isFading = 0; 161 | 162 | 163 | //if the super mode is mood light and the light isnt currently fading, 164 | //then change to a new color and start a new fade 165 | if(superMode == MOOD && isFading == 0){ 166 | if(currentMoodColor == RED){ 167 | nextState.red = 0; 168 | nextState.green = 1023; 169 | nextState.blue = 0; 170 | nextState.updateType = RGB; 171 | newCommand = true; 172 | currentMoodColor = GREEN; 173 | 174 | } 175 | else if(currentMoodColor == GREEN){ 176 | nextState.red = 0; 177 | nextState.green = 0; 178 | nextState.blue = 1023; 179 | nextState.updateType = RGB; 180 | newCommand = true; 181 | currentMoodColor = BLUE; 182 | } 183 | else if(currentMoodColor == BLUE){ 184 | nextState.red = 1023; 185 | nextState.green = 0; 186 | nextState.blue = 0; 187 | nextState.updateType = RGB; 188 | newCommand = true; 189 | currentMoodColor = RED; 190 | } 191 | } 192 | 193 | lightUpdater(&newState, currentState); 194 | isFading = lightChanger(newState, ¤tState); 195 | 196 | } 197 | 198 | 199 | //this function actually changes the light values and does the fading 200 | //returns 1 if fading 201 | //returns 0 if not fading 202 | //returns -1 if the timer doesnt get triggered 203 | int lightChanger(lightState newState, lightState *currentState){ 204 | static Metro changeTimer = Metro(1); 205 | 206 | static int changeMode = NORMAL; //the current mode (fading or normal) 207 | static int currentPeriod = 0; //time since starting the fade 208 | 209 | 210 | //only allow fade updates every 1ms 211 | if(changeTimer.check()){ 212 | 213 | //check to see if this is there is a new command and set the mode to FADING 214 | if(newCommand){ 215 | newCommand = false; 216 | changeMode = FADING; 217 | } 218 | 219 | 220 | 221 | if(changeMode == FADING){ 222 | 223 | //check whether or not a fade is needed - if so update the channel velues 224 | if((newState.red != currentState->red || newState.blue != currentState->blue || newState.green != currentState->green) || (currentPeriod <= currentState->fadePeriod)){ 225 | 226 | if(currentPeriod % newState.redRate == 0){ 227 | if(newState.red > currentState->red){currentState->red++;} 228 | else if (newState.red < currentState->red){currentState->red--;} 229 | } 230 | 231 | if(currentPeriod % newState.greenRate == 0){ 232 | if(newState.green > currentState->green){currentState->green++;} 233 | else if (newState.green < currentState->green){currentState->green--;} 234 | } 235 | 236 | if(currentPeriod % newState.blueRate == 0){ 237 | if(newState.blue > currentState->blue){currentState->blue++;} 238 | else if (newState.blue < currentState->blue){currentState->blue--;} 239 | } 240 | 241 | //write to the analog pins 242 | analogWrite(redPin, currentState->red); 243 | analogWrite(greenPin, currentState->green); 244 | analogWrite(bluePin, currentState->blue); 245 | 246 | //increment the period 247 | currentPeriod++; 248 | return 1; //return 1 on mode being FADING 249 | } 250 | 251 | //if no fade is needed then reset the period and set the mode to NORMAL 252 | else{ 253 | currentPeriod = 0; 254 | changeMode = NORMAL; 255 | return 0; //return 0 on mode being NORMAL 256 | } 257 | 258 | } 259 | 260 | 261 | else if (changeMode == NORMAL){ 262 | return 0; //return 0 on mode being NORMAL 263 | } 264 | 265 | } 266 | 267 | return -1; //return -1 on timer not set off 268 | 269 | } 270 | 271 | //calculates new information (color values, fade times, etc) for 272 | //new color updates 273 | void lightUpdater (lightState *newState, lightState currentState){ 274 | 275 | //calculate new vars only if there is a new command 276 | if (newCommand){ 277 | 278 | //determine which kind of update this is 279 | if(nextState.updateType == HSB){ 280 | 281 | //convert from HSB to RGB 282 | int newRGB[3]; 283 | H2R_HSBtoRGBfloat(nextState.hue, nextState.saturation, nextState.brightness, newRGB); 284 | newState->red = newRGB[0]; 285 | newState->green = newRGB[1]; 286 | newState->blue = newRGB[2]; 287 | 288 | //determine the RGB difference from the current values to new values (how far each channel needs to fade) 289 | int redDiff = abs(newState->red - currentState.red); 290 | int greenDiff = abs(newState->green - currentState.green); 291 | int blueDiff = abs(newState->blue - currentState.blue); 292 | 293 | //calculate the new fade times for each channel (how long to wait between fading up/down) 294 | if(redDiff > 0){newState->redRate = (nextState.fadePeriod / redDiff);} 295 | else{newState->redRate = nextState.fadePeriod;} 296 | 297 | if(greenDiff > 0){newState->greenRate = (nextState.fadePeriod / greenDiff);} 298 | else{newState->greenRate = nextState.fadePeriod;} 299 | 300 | if(blueDiff > 0){newState->blueRate = (nextState.fadePeriod / blueDiff);} 301 | else{newState->blueRate = nextState.fadePeriod;} 302 | 303 | //set the total time to fade 304 | newState->fadePeriod = nextState.fadePeriod; 305 | 306 | } 307 | else if(nextState.updateType == RGB){ 308 | 309 | //set new RGB values from update 310 | newState->red = nextState.red; 311 | newState->green = nextState.green; 312 | newState->blue = nextState.blue; 313 | 314 | //determine the RGB difference from the current values to new values (how far each channel needs to fade) 315 | int redDiff = abs(newState->red - currentState.red); 316 | int greenDiff = abs(newState->green - currentState.green); 317 | int blueDiff = abs(newState->blue - currentState.blue); 318 | 319 | //calculate the new fade times for each channel (how long to wait between fading up/down) 320 | if(redDiff > 0){newState->redRate = (nextState.fadePeriod / redDiff) + 1;} 321 | else{newState->redRate = nextState.fadePeriod;} 322 | 323 | if(greenDiff > 0){newState->greenRate = (nextState.fadePeriod / greenDiff) + 1;} 324 | else{newState->greenRate = nextState.fadePeriod;} 325 | 326 | if(blueDiff > 0){newState->blueRate = (nextState.fadePeriod / blueDiff) + 1;} 327 | else{newState->blueRate = nextState.fadePeriod;} 328 | 329 | //set the total time to fade 330 | newState->fadePeriod = nextState.fadePeriod; 331 | 332 | } 333 | } 334 | 335 | } 336 | 337 | 338 | 339 | //MQTT callback 340 | void callback(char* topic, byte* payload, unsigned int length) { 341 | 342 | //convert topic to string to make it easier to work with 343 | String topicStr = topic; 344 | 345 | char newPayload[40]; 346 | memcpy(newPayload, payload, length); 347 | newPayload[length] = '\0'; 348 | 349 | //handle HSB updates 350 | if(payload[0] == 'h'){ 351 | nextState.hue = atof(&newPayload[1]); 352 | nextState.saturation = atof(&newPayload[7]); 353 | nextState.brightness = atof(&newPayload[13]); 354 | 355 | nextState.updateType = HSB; 356 | nextState.fadePeriod = 2100; 357 | newCommand = true; 358 | superMode = SET; 359 | 360 | } 361 | 362 | //handle RGB updates 363 | else if (payload[0] == 'r'){ 364 | int newRed = atoi(&newPayload[1]); 365 | int newGreen = atoi(&newPayload[5]); 366 | int newBlue = atoi(&newPayload[9]); 367 | 368 | nextState.red = newRed; 369 | nextState.green = newGreen; 370 | nextState.blue = newBlue; 371 | 372 | nextState.updateType = RGB; 373 | newCommand = true; 374 | nextState.fadePeriod = 2100; 375 | superMode = SET; 376 | } 377 | 378 | //handle moodlight updates 379 | else if(payload[0] == 'm'){ 380 | 381 | if(payload[1] == '1'){ 382 | superMode = MOOD; 383 | nextState.fadePeriod = 10000; 384 | newCommand = true; 385 | } 386 | else if(payload[1] == '0'){ 387 | superMode = SET; 388 | nextState.fadePeriod = 2100; 389 | newCommand = true; 390 | } 391 | } 392 | 393 | 394 | //package up status message reply and send it back out to the status topic 395 | strcpy(statusString, newPayload); 396 | myESP.publish(statusTopic, statusString, true); 397 | } 398 | 399 | 400 | 401 | 402 | void colorTest(){ 403 | digitalWrite(redPin, HIGH); //red on 404 | delay(500); 405 | digitalWrite(redPin, LOW); //green on 406 | digitalWrite(greenPin, HIGH); 407 | delay(500); 408 | digitalWrite(greenPin, LOW); //blue on 409 | digitalWrite(bluePin, HIGH); 410 | delay(500); 411 | 412 | digitalWrite(redPin, HIGH); //all on 413 | digitalWrite(greenPin, HIGH); 414 | digitalWrite(bluePin, HIGH); 415 | delay(500); 416 | digitalWrite(redPin, LOW); //all off 417 | digitalWrite(greenPin, LOW); 418 | digitalWrite(bluePin, LOW); 419 | } 420 | 421 | 422 | char *ftoa(char *a, double f, int precision) 423 | { 424 | long p[] = {0,10,100,1000,10000,100000,1000000,10000000,100000000}; 425 | 426 | char *ret = a; 427 | long heiltal = (long)f; 428 | itoa(heiltal, a, 10); 429 | while (*a != '\0') a++; 430 | *a++ = '.'; 431 | long desimal = abs((long)((f - heiltal) * p[precision])); 432 | itoa(desimal, a, 10); 433 | return ret; 434 | } 435 | 436 | 437 | 438 | 439 | float map_double(double x, double in_min, double in_max, double out_min, double out_max) 440 | { 441 | return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; 442 | } 443 | 444 | 445 | -------------------------------------------------------------------------------- /src/config_html.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | inline unsigned char static_config_html[] PROGMEM = { 4 | 0x3c, 0x21, 0x64, 0x6f, 0x63, 0x74, 0x79, 0x70, 0x65, 0x20, 0x68, 0x74, 5 | 0x6d, 0x6c, 0x3e, 0x3c, 0x68, 0x74, 0x6d, 0x6c, 0x20, 0x6c, 0x61, 0x6e, 6 | 0x67, 0x3d, 0x65, 0x6e, 0x3e, 0x3c, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x63, 7 | 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x77, 0x69, 0x64, 0x74, 8 | 0x68, 0x3d, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2d, 0x77, 0x69, 0x64, 9 | 0x74, 0x68, 0x2c, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x73, 10 | 0x63, 0x61, 0x6c, 0x65, 0x3d, 0x31, 0x2c, 0x75, 0x73, 0x65, 0x72, 0x2d, 11 | 0x73, 0x63, 0x61, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x3d, 0x6e, 0x6f, 0x22, 12 | 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x76, 0x69, 0x65, 0x77, 0x70, 0x6f, 13 | 0x72, 0x74, 0x3e, 0x3c, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x3e, 0x53, 0x79, 14 | 0x73, 0x74, 0x65, 0x6d, 0x20, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 15 | 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3c, 0x2f, 0x74, 0x69, 0x74, 0x6c, 16 | 0x65, 0x3e, 0x3c, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3e, 0x62, 0x6f, 0x64, 17 | 0x79, 0x20, 0x7b, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 18 | 0x6e, 0x64, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x23, 0x30, 0x30, 19 | 0x64, 0x66, 0x66, 0x66, 0x3b, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x66, 20 | 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x3a, 0x76, 0x65, 0x72, 0x64, 0x61, 0x6e, 21 | 0x61, 0x3b, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x3a, 0x30, 0x3b, 22 | 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 23 | 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 24 | 0x69, 0x6e, 0x65, 0x72, 0x20, 0x7b, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, 25 | 0x61, 0x79, 0x3a, 0x66, 0x6c, 0x65, 0x78, 0x3b, 0x20, 0x6a, 0x75, 0x73, 26 | 0x74, 0x69, 0x66, 0x79, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 27 | 0x3a, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x20, 0x61, 0x6c, 0x69, 28 | 0x67, 0x6e, 0x2d, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x3a, 0x63, 0x65, 0x6e, 29 | 0x74, 0x65, 0x72, 0x3b, 0x20, 0x6d, 0x69, 0x6e, 0x2d, 0x68, 0x65, 0x69, 30 | 0x67, 0x68, 0x74, 0x3a, 0x31, 0x30, 0x30, 0x76, 0x68, 0x3b, 0x20, 0x7d, 31 | 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x63, 0x6f, 32 | 0x6e, 0x66, 0x69, 0x67, 0x2d, 0x62, 0x6f, 0x78, 0x20, 0x7b, 0x20, 0x62, 33 | 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x23, 0x66, 34 | 0x66, 0x66, 0x3b, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x3a, 35 | 0x33, 0x32, 0x70, 0x78, 0x20, 0x32, 0x34, 0x70, 0x78, 0x3b, 0x20, 0x62, 36 | 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 37 | 0x3a, 0x31, 0x32, 0x70, 0x78, 0x3b, 0x20, 0x62, 0x6f, 0x78, 0x2d, 0x73, 38 | 0x68, 0x61, 0x64, 0x6f, 0x77, 0x3a, 0x30, 0x20, 0x32, 0x70, 0x78, 0x20, 39 | 0x31, 0x32, 0x70, 0x78, 0x20, 0x72, 0x67, 0x62, 0x61, 0x28, 0x30, 0x2c, 40 | 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2e, 0x31, 0x35, 0x29, 0x3b, 0x20, 0x6d, 41 | 0x69, 0x6e, 0x2d, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x33, 0x32, 0x30, 42 | 0x70, 0x78, 0x3b, 0x20, 0x6d, 0x61, 0x78, 0x2d, 0x77, 0x69, 0x64, 0x74, 43 | 0x68, 0x3a, 0x34, 0x30, 0x30, 0x70, 0x78, 0x3b, 0x20, 0x74, 0x65, 0x78, 44 | 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x6c, 0x65, 0x66, 0x74, 45 | 0x3b, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 46 | 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2d, 0x62, 0x6f, 0x78, 0x20, 47 | 0x68, 0x33, 0x2c, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2d, 48 | 0x62, 0x6f, 0x78, 0x20, 0x68, 0x34, 0x20, 0x7b, 0x20, 0x74, 0x65, 0x78, 49 | 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x63, 0x65, 0x6e, 0x74, 50 | 0x65, 0x72, 0x3b, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x74, 51 | 0x6f, 0x70, 0x3a, 0x30, 0x3b, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 52 | 0x20, 0x20, 0x20, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2d, 53 | 0x62, 0x6f, 0x78, 0x20, 0x68, 0x72, 0x20, 0x7b, 0x20, 0x6d, 0x61, 0x72, 54 | 0x67, 0x69, 0x6e, 0x3a, 0x31, 0x38, 0x70, 0x78, 0x20, 0x30, 0x3b, 0x20, 55 | 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x63, 56 | 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2d, 0x62, 0x6f, 0x78, 0x20, 0x69, 0x6e, 57 | 0x70, 0x75, 0x74, 0x2c, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 58 | 0x2d, 0x62, 0x6f, 0x78, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x20, 59 | 0x7b, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x31, 0x30, 0x30, 0x25, 60 | 0x3b, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x62, 0x6f, 0x74, 61 | 0x74, 0x6f, 0x6d, 0x3a, 0x31, 0x30, 0x70, 0x78, 0x3b, 0x20, 0x70, 0x61, 62 | 0x64, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x36, 0x70, 0x78, 0x3b, 0x20, 0x62, 63 | 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 64 | 0x3a, 0x34, 0x70, 0x78, 0x3b, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 65 | 0x3a, 0x31, 0x70, 0x78, 0x20, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x20, 0x23, 66 | 0x63, 0x63, 0x63, 0x3b, 0x20, 0x62, 0x6f, 0x78, 0x2d, 0x73, 0x69, 0x7a, 67 | 0x69, 0x6e, 0x67, 0x3a, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x62, 68 | 0x6f, 0x78, 0x3b, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 69 | 0x20, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2d, 0x62, 0x6f, 70 | 0x78, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5b, 0x74, 0x79, 0x70, 0x65, 71 | 0x3d, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x5d, 0x20, 0x7b, 0x20, 0x77, 72 | 0x69, 0x64, 0x74, 0x68, 0x3a, 0x61, 0x75, 0x74, 0x6f, 0x3b, 0x20, 0x62, 73 | 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x23, 0x30, 74 | 0x30, 0x64, 0x66, 0x66, 0x66, 0x3b, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 75 | 0x3a, 0x23, 0x66, 0x66, 0x66, 0x3b, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 76 | 0x72, 0x3a, 0x6e, 0x6f, 0x6e, 0x65, 0x3b, 0x20, 0x63, 0x75, 0x72, 0x73, 77 | 0x6f, 0x72, 0x3a, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x20, 78 | 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x38, 0x70, 0x78, 0x20, 79 | 0x31, 0x38, 0x70, 0x78, 0x3b, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 80 | 0x2d, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x3a, 0x34, 0x70, 0x78, 0x3b, 81 | 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 82 | 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2d, 0x62, 0x6f, 0x78, 0x20, 0x69, 83 | 0x6e, 0x70, 0x75, 0x74, 0x5b, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x73, 0x75, 84 | 0x62, 0x6d, 0x69, 0x74, 0x5d, 0x3a, 0x68, 0x6f, 0x76, 0x65, 0x72, 0x20, 85 | 0x7b, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 86 | 0x3a, 0x23, 0x30, 0x61, 0x34, 0x66, 0x37, 0x35, 0x3b, 0x20, 0x7d, 0x0a, 87 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 88 | 0x66, 0x69, 0x67, 0x2d, 0x62, 0x6f, 0x78, 0x20, 0x70, 0x2c, 0x20, 0x2e, 89 | 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2d, 0x62, 0x6f, 0x78, 0x20, 0x61, 90 | 0x20, 0x7b, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 91 | 0x6e, 0x3a, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x20, 0x64, 0x69, 92 | 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 93 | 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x3a, 0x31, 0x30, 0x70, 0x78, 94 | 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x3b, 0x20, 0x7d, 0x3c, 0x2f, 0x73, 95 | 0x74, 0x79, 0x6c, 0x65, 0x3e, 0x3c, 0x62, 0x6f, 0x64, 0x79, 0x3e, 0x3c, 96 | 0x64, 0x69, 0x76, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x63, 0x65, 97 | 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 98 | 0x65, 0x72, 0x3e, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x63, 0x6c, 0x61, 0x73, 99 | 0x73, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2d, 0x62, 0x6f, 0x78, 100 | 0x3e, 0x3c, 0x68, 0x33, 0x3e, 0x3c, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x73, 101 | 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x23, 102 | 0x30, 0x61, 0x34, 0x66, 0x37, 0x35, 0x3b, 0x3e, 0x3c, 0x73, 0x74, 0x72, 103 | 0x6f, 0x6e, 0x67, 0x3e, 0x45, 0x53, 0x50, 0x38, 0x32, 0x36, 0x36, 0x20, 104 | 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20, 0x43, 0x6f, 0x6e, 0x66, 0x69, 105 | 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3c, 0x2f, 0x73, 0x74, 106 | 0x72, 0x6f, 0x6e, 0x67, 0x3e, 0x3c, 0x2f, 0x73, 0x70, 0x61, 0x6e, 0x3e, 107 | 0x3c, 0x2f, 0x68, 0x33, 0x3e, 0x3c, 0x68, 0x72, 0x3e, 0x3c, 0x66, 0x6f, 108 | 0x72, 0x6d, 0x20, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x25, 0x48, 109 | 0x45, 0x4c, 0x50, 0x45, 0x52, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5f, 0x55, 110 | 0x52, 0x49, 0x25, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x3d, 0x50, 111 | 0x4f, 0x53, 0x54, 0x3e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x4e, 112 | 0x61, 0x6d, 0x65, 0x3a, 0x3c, 0x62, 0x72, 0x3e, 0x3c, 0x69, 0x6e, 0x70, 113 | 0x75, 0x74, 0x20, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 114 | 0x65, 0x72, 0x3d, 0x22, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x48, 115 | 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x20, 0x28, 0x52, 0x65, 116 | 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x29, 0x22, 0x20, 0x6d, 0x61, 0x78, 117 | 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3d, 0x33, 0x32, 0x20, 0x6e, 0x61, 118 | 0x6d, 0x65, 0x3d, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x20, 119 | 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x33, 0x32, 0x20, 0x76, 0x61, 0x6c, 0x75, 120 | 0x65, 0x3d, 0x25, 0x48, 0x45, 0x4c, 0x50, 0x45, 0x52, 0x5f, 0x48, 0x4f, 121 | 0x53, 0x54, 0x4e, 0x41, 0x4d, 0x45, 0x25, 0x3e, 0x3c, 0x62, 0x72, 0x3e, 122 | 0x20, 0x53, 0x53, 0x49, 0x44, 0x3a, 0x3c, 0x62, 0x72, 0x3e, 0x3c, 0x69, 123 | 0x6e, 0x70, 0x75, 0x74, 0x20, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 124 | 0x6c, 0x64, 0x65, 0x72, 0x3d, 0x22, 0x53, 0x53, 0x49, 0x44, 0x20, 0x20, 125 | 0x28, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x29, 0x22, 0x20, 126 | 0x6d, 0x61, 0x78, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3d, 0x33, 0x32, 127 | 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x73, 0x73, 0x69, 0x64, 0x20, 0x73, 128 | 0x69, 0x7a, 0x65, 0x3d, 0x33, 0x32, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 129 | 0x3d, 0x25, 0x48, 0x45, 0x4c, 0x50, 0x45, 0x52, 0x5f, 0x53, 0x53, 0x49, 130 | 0x44, 0x25, 0x3e, 0x3c, 0x62, 0x72, 0x3e, 0x20, 0x53, 0x53, 0x49, 0x44, 131 | 0x20, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x3a, 0x3c, 0x62, 132 | 0x72, 0x3e, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x70, 0x6c, 0x61, 133 | 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x3d, 0x22, 0x28, 0x55, 134 | 0x73, 0x65, 0x20, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x56, 0x61, 135 | 0x6c, 0x75, 0x65, 0x29, 0x22, 0x20, 0x6d, 0x61, 0x78, 0x6c, 0x65, 0x6e, 136 | 0x67, 0x74, 0x68, 0x3d, 0x33, 0x32, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 137 | 0x6e, 0x65, 0x74, 0x50, 0x61, 0x73, 0x73, 0x20, 0x73, 0x69, 0x7a, 0x65, 138 | 0x3d, 0x33, 0x32, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x70, 0x61, 0x73, 139 | 0x73, 0x77, 0x6f, 0x72, 0x64, 0x3e, 0x3c, 0x62, 0x72, 0x3e, 0x20, 0x4f, 140 | 0x54, 0x41, 0x20, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x3a, 141 | 0x3c, 0x62, 0x72, 0x3e, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x70, 142 | 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x3d, 0x22, 143 | 0x28, 0x55, 0x73, 0x65, 0x20, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 144 | 0x56, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x22, 0x20, 0x6d, 0x61, 0x78, 0x6c, 145 | 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3d, 0x31, 0x36, 0x20, 0x6e, 0x61, 0x6d, 146 | 0x65, 0x3d, 0x6f, 0x74, 0x61, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 147 | 0x64, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x31, 0x38, 0x20, 0x74, 0x79, 148 | 0x70, 0x65, 0x3d, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x3e, 149 | 0x3c, 0x62, 0x72, 0x3e, 0x3c, 0x68, 0x72, 0x3e, 0x3c, 0x68, 0x34, 0x3e, 150 | 0x3c, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 151 | 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x23, 0x30, 0x61, 0x34, 0x66, 0x37, 152 | 0x35, 0x3b, 0x3e, 0x4d, 0x51, 0x54, 0x54, 0x20, 0x53, 0x65, 0x74, 0x74, 153 | 0x69, 0x6e, 0x67, 0x73, 0x3c, 0x2f, 0x73, 0x70, 0x61, 0x6e, 0x3e, 0x3c, 154 | 0x2f, 0x68, 0x34, 0x3e, 0x20, 0x4d, 0x51, 0x54, 0x54, 0x20, 0x48, 0x6f, 155 | 0x73, 0x74, 0x20, 0x28, 0x49, 0x50, 0x29, 0x3a, 0x3c, 0x62, 0x72, 0x3e, 156 | 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x70, 0x6c, 0x61, 0x63, 0x65, 157 | 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x3d, 0x22, 0x4d, 0x51, 0x54, 0x54, 158 | 0x20, 0x48, 0x6f, 0x73, 0x74, 0x22, 0x20, 0x6d, 0x61, 0x78, 0x6c, 0x65, 159 | 0x6e, 0x67, 0x74, 0x68, 0x3d, 0x33, 0x32, 0x20, 0x6e, 0x61, 0x6d, 0x65, 160 | 0x3d, 0x6d, 0x71, 0x74, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x73, 0x69, 161 | 0x7a, 0x65, 0x3d, 0x33, 0x32, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 162 | 0x25, 0x48, 0x45, 0x4c, 0x50, 0x45, 0x52, 0x5f, 0x4d, 0x51, 0x54, 0x54, 163 | 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x25, 0x3e, 0x3c, 0x62, 0x72, 0x3e, 0x20, 164 | 0x4d, 0x51, 0x54, 0x54, 0x20, 0x55, 0x73, 0x65, 0x72, 0x3a, 0x3c, 0x62, 165 | 0x72, 0x3e, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x70, 0x6c, 0x61, 166 | 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x3d, 0x22, 0x4d, 0x51, 167 | 0x54, 0x54, 0x20, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 168 | 0x20, 0x6d, 0x61, 0x78, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3d, 0x31, 169 | 0x36, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x6d, 0x71, 0x74, 0x74, 0x55, 170 | 0x73, 0x65, 0x72, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x31, 0x36, 0x20, 171 | 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 0x25, 0x48, 0x45, 0x4c, 0x50, 0x45, 172 | 0x52, 0x5f, 0x4d, 0x51, 0x54, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x25, 173 | 0x3e, 0x3c, 0x62, 0x72, 0x3e, 0x20, 0x4d, 0x51, 0x54, 0x54, 0x20, 0x50, 174 | 0x6f, 0x72, 0x74, 0x3a, 0x3c, 0x62, 0x72, 0x3e, 0x3c, 0x69, 0x6e, 0x70, 175 | 0x75, 0x74, 0x20, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 176 | 0x65, 0x72, 0x3d, 0x22, 0x4d, 0x51, 0x54, 0x54, 0x20, 0x50, 0x6f, 0x72, 177 | 0x74, 0x22, 0x20, 0x6d, 0x61, 0x78, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 178 | 0x3d, 0x31, 0x30, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x6d, 0x71, 0x74, 179 | 0x74, 0x50, 0x6f, 0x72, 0x74, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x31, 180 | 0x30, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 0x25, 0x48, 0x45, 0x4c, 181 | 0x50, 0x45, 0x52, 0x5f, 0x4d, 0x51, 0x54, 0x54, 0x5f, 0x50, 0x4f, 0x52, 182 | 0x54, 0x25, 0x3e, 0x3c, 0x62, 0x72, 0x3e, 0x20, 0x4d, 0x51, 0x54, 0x54, 183 | 0x20, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x3a, 0x3c, 0x62, 184 | 0x72, 0x3e, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x70, 0x6c, 0x61, 185 | 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x3d, 0x22, 0x28, 0x55, 186 | 0x73, 0x65, 0x20, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x56, 0x61, 187 | 0x6c, 0x75, 0x65, 0x29, 0x22, 0x20, 0x6d, 0x61, 0x78, 0x6c, 0x65, 0x6e, 188 | 0x67, 0x74, 0x68, 0x3d, 0x31, 0x36, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 189 | 0x6d, 0x71, 0x74, 0x74, 0x50, 0x61, 0x73, 0x73, 0x20, 0x73, 0x69, 0x7a, 190 | 0x65, 0x3d, 0x31, 0x38, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x70, 0x61, 191 | 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x3e, 0x3c, 0x62, 0x72, 0x3e, 0x3c, 192 | 0x68, 0x72, 0x3e, 0x3c, 0x68, 0x34, 0x3e, 0x3c, 0x73, 0x70, 0x61, 0x6e, 193 | 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 194 | 0x3a, 0x23, 0x30, 0x61, 0x34, 0x66, 0x37, 0x35, 0x3b, 0x3e, 0x4d, 0x51, 195 | 0x54, 0x54, 0x20, 0x57, 0x69, 0x6c, 0x6c, 0x20, 0x53, 0x65, 0x74, 0x74, 196 | 0x69, 0x6e, 0x67, 0x73, 0x3c, 0x2f, 0x73, 0x70, 0x61, 0x6e, 0x3e, 0x3c, 197 | 0x2f, 0x68, 0x34, 0x3e, 0x20, 0x57, 0x69, 0x6c, 0x6c, 0x20, 0x54, 0x6f, 198 | 0x70, 0x69, 0x63, 0x3a, 0x3c, 0x62, 0x72, 0x3e, 0x3c, 0x69, 0x6e, 0x70, 199 | 0x75, 0x74, 0x20, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 200 | 0x65, 0x72, 0x3d, 0x22, 0x4d, 0x51, 0x54, 0x54, 0x20, 0x57, 0x69, 0x6c, 201 | 0x6c, 0x20, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x22, 0x20, 0x6d, 0x61, 0x78, 202 | 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3d, 0x31, 0x32, 0x38, 0x20, 0x6e, 203 | 0x61, 0x6d, 0x65, 0x3d, 0x6d, 0x71, 0x74, 0x74, 0x57, 0x69, 0x6c, 0x6c, 204 | 0x54, 0x6f, 0x70, 0x69, 0x63, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x36, 205 | 0x34, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 0x25, 0x48, 0x45, 0x4c, 206 | 0x50, 0x45, 0x52, 0x5f, 0x4d, 0x51, 0x54, 0x54, 0x5f, 0x57, 0x49, 0x4c, 207 | 0x4c, 0x5f, 0x54, 0x4f, 0x50, 0x49, 0x43, 0x25, 0x3e, 0x3c, 0x62, 0x72, 208 | 0x3e, 0x20, 0x57, 0x69, 0x6c, 0x6c, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 209 | 0x67, 0x65, 0x3a, 0x3c, 0x62, 0x72, 0x3e, 0x3c, 0x69, 0x6e, 0x70, 0x75, 210 | 0x74, 0x20, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 211 | 0x72, 0x3d, 0x22, 0x4d, 0x51, 0x54, 0x54, 0x20, 0x57, 0x69, 0x6c, 0x6c, 212 | 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x20, 0x6d, 0x61, 213 | 0x78, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3d, 0x31, 0x32, 0x38, 0x20, 214 | 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x6d, 0x71, 0x74, 0x74, 0x57, 0x69, 0x6c, 215 | 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x73, 0x69, 0x7a, 216 | 0x65, 0x3d, 0x33, 0x32, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 0x25, 217 | 0x48, 0x45, 0x4c, 0x50, 0x45, 0x52, 0x5f, 0x4d, 0x51, 0x54, 0x54, 0x5f, 218 | 0x57, 0x49, 0x4c, 0x4c, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 219 | 0x25, 0x3e, 0x3c, 0x62, 0x72, 0x3e, 0x20, 0x57, 0x69, 0x6c, 0x6c, 0x20, 220 | 0x51, 0x6f, 0x53, 0x3a, 0x3c, 0x62, 0x72, 0x3e, 0x3c, 0x69, 0x6e, 0x70, 221 | 0x75, 0x74, 0x20, 0x6d, 0x61, 0x78, 0x3d, 0x32, 0x20, 0x6d, 0x69, 0x6e, 222 | 0x3d, 0x30, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x6d, 0x71, 0x74, 0x74, 223 | 0x57, 0x69, 0x6c, 0x6c, 0x51, 0x6f, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 224 | 0x3d, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x76, 0x61, 0x6c, 0x75, 225 | 0x65, 0x3d, 0x25, 0x48, 0x45, 0x4c, 0x50, 0x45, 0x52, 0x5f, 0x4d, 0x51, 226 | 0x54, 0x54, 0x5f, 0x57, 0x49, 0x4c, 0x4c, 0x5f, 0x51, 0x4f, 0x53, 0x25, 227 | 0x3e, 0x3c, 0x62, 0x72, 0x3e, 0x20, 0x57, 0x69, 0x6c, 0x6c, 0x20, 0x52, 228 | 0x65, 0x74, 0x61, 0x69, 0x6e, 0x3a, 0x3c, 0x62, 0x72, 0x3e, 0x3c, 0x73, 229 | 0x65, 0x6c, 0x65, 0x63, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x6d, 230 | 0x71, 0x74, 0x74, 0x57, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x74, 0x61, 0x69, 231 | 0x6e, 0x3e, 0x3c, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x25, 0x68, 232 | 0x65, 0x6c, 0x70, 0x65, 0x72, 0x5f, 0x6d, 0x71, 0x74, 0x74, 0x5f, 0x77, 233 | 0x69, 0x6c, 0x6c, 0x5f, 0x72, 0x65, 0x74, 0x61, 0x69, 0x6e, 0x5f, 0x30, 234 | 0x25, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 0x30, 0x3e, 0x46, 0x61, 235 | 0x6c, 0x73, 0x65, 0x3c, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x25, 236 | 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x5f, 0x6d, 0x71, 0x74, 0x74, 0x5f, 237 | 0x77, 0x69, 0x6c, 0x6c, 0x5f, 0x72, 0x65, 0x74, 0x61, 0x69, 0x6e, 0x5f, 238 | 0x31, 0x25, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 0x31, 0x3e, 0x54, 239 | 0x72, 0x75, 0x65, 0x3c, 0x2f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x3e, 240 | 0x3c, 0x62, 0x72, 0x3e, 0x3c, 0x70, 0x3e, 0x50, 0x72, 0x65, 0x73, 0x73, 241 | 0x20, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x75, 242 | 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x45, 0x53, 0x50, 0x38, 0x32, 0x36, 243 | 0x36, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x66, 0x69, 0x6c, 244 | 0x65, 0x3c, 0x2f, 0x70, 0x3e, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 245 | 0x74, 0x79, 0x70, 0x65, 0x3d, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x20, 246 | 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 247 | 0x3e, 0x3c, 0x2f, 0x66, 0x6f, 0x72, 0x6d, 0x3e, 0x3c, 0x70, 0x3e, 0x3c, 248 | 0x61, 0x20, 0x68, 0x72, 0x65, 0x66, 0x3d, 0x2f, 0x3e, 0x47, 0x6f, 0x20, 249 | 0x74, 0x6f, 0x20, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x53, 0x74, 250 | 0x61, 0x74, 0x75, 0x73, 0x20, 0x50, 0x61, 0x67, 0x65, 0x3c, 0x2f, 0x61, 251 | 0x3e, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0x3c, 0x2f, 0x64, 0x69, 0x76, 252 | 0x3e 253 | }; 254 | inline unsigned int static_config_html_len = 2977; 255 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . -------------------------------------------------------------------------------- /src/ESPHelper.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ESPHelper.cpp 3 | Copyright (c) 2019 ItKindaWorks Inc All right reserved. 4 | github.com/ItKindaWorks 5 | 6 | This file is part of ESPHelper 7 | 8 | ESPHelper is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | ESPHelper is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with ESPHelper. If not, see . 20 | */ 21 | 22 | 23 | #include "ESPHelper.h" 24 | #include 25 | 26 | 27 | 28 | 29 | void printNetInfo(const NetInfo *net, const char* header, bool printMQTT, bool printWill){ 30 | debugPrintln(header); 31 | debugPrint("Hostname: "); 32 | debugPrintln(net->getHostname()); 33 | debugPrint("SSID: "); 34 | debugPrintln(net->getSsid()); 35 | debugPrint("PASS: "); 36 | debugPrintln(net->getPass()); 37 | if (printMQTT) { 38 | debugPrint("MQTT Host: "); 39 | debugPrintln(net->getMqttHost()); 40 | debugPrint("MQTT User: "); 41 | debugPrintln(net->getMqttUser()); 42 | debugPrint("MQTT Pass: "); 43 | debugPrintln(net->getMqttPass()); 44 | debugPrint("MQTT Port: "); 45 | debugPrintln(net->getMqttPort()); 46 | } 47 | if (printWill) { 48 | debugPrint("Will Topic: "); 49 | debugPrintln(net->getMqttWillTopic()); 50 | debugPrint("Will Message: "); 51 | debugPrintln(net->getMqttWillMessage()); 52 | debugPrint("Will QoS: "); 53 | debugPrintln(net->getMqttWillQoS()); 54 | debugPrint("Will Retain: "); 55 | debugPrintln(net->getMqttWillRetain() ? "true" : "false"); 56 | } 57 | } 58 | 59 | /* 60 | empty initializer wrapper 61 | used for creating instances of ESPHelper that will be filled in later from a loaded config file 62 | 63 | input: NA 64 | output: NA 65 | */ 66 | ESPHelper::ESPHelper(){ 67 | _currentNet.setSsid(""); 68 | _currentNet.setPass(""); 69 | _currentNet.setMqttHost(""); 70 | _currentNet.setMqttUser(""); 71 | _currentNet.setMqttPass(""); 72 | _currentNet.setMqttPort(1883); 73 | _currentNet.setMqttWillTopic("defaultWillTopic"); 74 | _currentNet.setMqttWillMessage("offline"); 75 | _currentNet.setMqttWillQoS (0); 76 | _currentNet.setMqttWillRetain(true); 77 | init(); 78 | } 79 | 80 | 81 | /* 82 | initializer wrapper with single NetInfo network 83 | 84 | input: NetInfo ptr 85 | output: NA 86 | */ 87 | ESPHelper::ESPHelper(const NetInfo *startingNet, bool storeLocal){ 88 | if (startingNet) { 89 | startingNet->cloneTo(_currentNet, storeLocal); 90 | } 91 | //if the starting net is null then we set the default values and store locally 92 | else { 93 | _currentNet.setSsid(""); 94 | _currentNet.setPass(""); 95 | _currentNet.setMqttHost(""); 96 | _currentNet.setMqttUser(""); 97 | _currentNet.setMqttPass(""); 98 | _currentNet.setMqttPort(1883); 99 | _currentNet.setMqttWillTopic("defaultWillTopic"); 100 | _currentNet.setMqttWillMessage("offline"); 101 | _currentNet.setMqttWillQoS (0); 102 | _currentNet.setMqttWillRetain(true); 103 | } 104 | 105 | init(); 106 | 107 | } 108 | 109 | 110 | 111 | 112 | /* 113 | initialize the NetInfo data and reset wifi. set hopping and OTA to off 114 | 115 | input: 116 | char ptr for network SSID 117 | char ptr for network Password 118 | char ptr for MQTT broker IP (or hostname) 119 | char ptr for MQTT username 120 | char ptr for MQTT password 121 | int for MQTT port 122 | char ptr for MQTT last will topic 123 | char ptr for MQTT last will message 124 | int for MQTT will QOS 125 | int for MQTT will retain 126 | output: NA 127 | */ 128 | void ESPHelper::init(){ 129 | //diconnect from and previous wifi networks 130 | WiFi.softAPdisconnect(); 131 | WiFi.disconnect(); 132 | 133 | //validate various bits of network/MQTT info 134 | validateConfig(); 135 | } 136 | 137 | 138 | 139 | /* 140 | checks which parts of a NetInfo have been filled out 141 | and updates internal flags noting which are set 142 | 143 | input: NA 144 | output: NA 145 | */ 146 | void ESPHelper::validateConfig(){ 147 | //network pass 148 | if(_currentNet.getPass()[0] == '\0'){_passSet = false;} 149 | else{_passSet = true;} 150 | 151 | //ssid 152 | if(_currentNet.getSsid()[0] == '\0'){_ssidSet = false;} 153 | else{_ssidSet = true;} 154 | 155 | //mqtt host 156 | if(_currentNet.getMqttHost()[0] == '\0'){_mqttSet = false;} 157 | else{_mqttSet = true;} 158 | 159 | //mqtt port 160 | if(_currentNet.getMqttPort() == 0){_currentNet.setMqttPort(1883);} 161 | 162 | //mqtt username 163 | if(_currentNet.getMqttUser()[0] == '\0'){_mqttUserSet = false;} 164 | else{_mqttUserSet = true;} 165 | 166 | //mqtt password 167 | if(_currentNet.getMqttPass()[0] == '\0'){_mqttPassSet = false;} 168 | else{_mqttPassSet = true;} 169 | 170 | //Will Topic 171 | if(_currentNet.getMqttWillTopic()[0] == '\0'){_willTopicSet = false;} 172 | else{_willTopicSet = true;} 173 | 174 | //Will Message 175 | if(_currentNet.getMqttWillMessage()[0] == '\0'){_willMessageSet = false;} 176 | else{_willMessageSet = true;} 177 | 178 | } 179 | 180 | 181 | 182 | bool ESPHelper::begin(const NetInfo *startingNet, bool storeLocal){ 183 | // If a starting net is provided 184 | if (startingNet) { 185 | startingNet->cloneTo(_currentNet, storeLocal); 186 | } 187 | 188 | //if the starting net is null then we set the default values and store locally 189 | else { 190 | _currentNet.setSsid(""); 191 | _currentNet.setPass(""); 192 | _currentNet.setMqttHost(""); 193 | _currentNet.setMqttUser(""); 194 | _currentNet.setMqttPass(""); 195 | _currentNet.setMqttPort(1883); 196 | _currentNet.setMqttWillTopic("defaultWillTopic"); 197 | _currentNet.setMqttWillMessage("offline"); 198 | _currentNet.setMqttWillQoS (0); 199 | _currentNet.setMqttWillRetain(true); 200 | } 201 | 202 | init(); 203 | return begin(); 204 | } 205 | 206 | 207 | /* 208 | start the wifi & mqtt systems and attempt connection (currently blocking) 209 | return values from this function are somewhat legacy as data validation takes place elsewhere now 210 | 211 | input: NA 212 | output: 213 | true on: ssid set 214 | false on: ssid not set 215 | */ 216 | bool ESPHelper::begin(){ 217 | 218 | // Generate client name based on MAC address and last 8 bits of microsecond counter 219 | #ifdef ESP8266 220 | _clientName = "esp8266-"; 221 | #else 222 | _clientName = "esp32-"; 223 | #endif 224 | uint8_t mac[6]; 225 | WiFi.macAddress(mac); 226 | _clientName += macToStr(mac); 227 | 228 | //set the wifi mode to station 229 | WiFi.mode(WIFI_STA); 230 | 231 | //as long as the SSID has been set, then try to connect to the network 232 | if(_ssidSet){ 233 | 234 | // printNetInfo(&_currentNet, "Pre wifi begin", _mqttSet, _willTopicSet); 235 | if(_passSet){WiFi.begin(_currentNet.getSsid(), _currentNet.getPass());} 236 | else{WiFi.begin(_currentNet.getSsid());} 237 | 238 | WiFi.setAutoReconnect(true); 239 | WiFi.setSleep(false); 240 | 241 | 242 | //as long as an mqtt ip has been set create an instance of PubSub for client 243 | if(_mqttSet){ 244 | client.setServer(_currentNet.getMqttHost(), _currentNet.getMqttPort()); 245 | 246 | //set the mqtt message callback if needed 247 | if(_mqttCallbackSet){client.setCallback(_mqttCallback);} 248 | } 249 | 250 | //define a dummy instance of mqtt so that it is instantiated if no mqtt ip is set 251 | else{ 252 | client.setServer("192.0.2.0", _currentNet.getMqttPort()); 253 | } 254 | 255 | 256 | //set the mqtt client to use the secure client if available 257 | if(_useSecureClient){client.setClient(wifiClientSecure);} 258 | else{client.setClient(wifiClient);} 259 | 260 | 261 | //ota event handlers 262 | ArduinoOTA.onStart([]() {/* ota start code */}); 263 | ArduinoOTA.onEnd([]() { 264 | //give the arduino a bit of time to finish up any remaining network activity 265 | delay(500); 266 | //on ota end we disconnect from wifi cleanly before restarting. 267 | WiFi.softAPdisconnect(); 268 | WiFi.disconnect(); 269 | int timeout = 0; 270 | //max timeout of 2seconds before just dropping out and restarting 271 | while(WiFi.status() != WL_DISCONNECTED && timeout < 200){ 272 | delay(10); 273 | timeout++; 274 | } 275 | }); 276 | ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {/* ota progress code */}); 277 | ArduinoOTA.onError([](ota_error_t error) {/* ota error code */}); 278 | 279 | 280 | //initially attempt to connect to wifi when we begin (but only block for 2 seconds before timing out) 281 | int timeout = 0; //counter for begin connection attempts 282 | while (((!client.connected() && _mqttSet) || WiFi.status() != WL_CONNECTED) && timeout < 200 ) { //max 2 sec before timeout 283 | // printNetInfo(&_currentNet, "ESPHelper Begin reconnect loop", _mqttSet, _willTopicSet); 284 | reconnect(); 285 | delay(10); 286 | timeout++; 287 | } 288 | 289 | //attempt to start ota if needed 290 | OTA_begin(); 291 | 292 | //mark the system as started and return 293 | _hasBegun = true; 294 | return true; 295 | } 296 | 297 | //if no ssid was set even then dont try to begin and return false 298 | return false; 299 | } 300 | 301 | 302 | /* 303 | end the instance of ESPHelper (shutdown wifi, ota, mqtt) 304 | 305 | input: NA 306 | output: NA 307 | */ 308 | void ESPHelper::end(){ 309 | OTA_disable(); 310 | client.disconnect(); 311 | delay(20); 312 | WiFi.softAPdisconnect(); 313 | WiFi.disconnect(); 314 | 315 | int timeout = 0; 316 | while(WiFi.status() != WL_DISCONNECTED && timeout < 200){ 317 | delay(10); 318 | timeout++; 319 | } 320 | 321 | _connectionStatus = NO_CONNECTION; 322 | 323 | } 324 | 325 | 326 | 327 | 328 | /* 329 | enables the use of a secure (SSL) connection to an MQTT broker. 330 | (Make sure your mqtt port is set to one expecting a secure connection) 331 | 332 | input: char ptr string for SSL fingerprint 333 | output: NA 334 | */ 335 | void ESPHelper::useSecureClient(const char* fingerprint){ 336 | _fingerprint = fingerprint; 337 | 338 | //fall back to wifi only connection if it was previously at full connection 339 | //(because we just changed how the device is going to connect to the mqtt broker) 340 | if(setConnectionStatus() == FULL_CONNECTION){ 341 | _connectionStatus = WIFI_ONLY; 342 | } 343 | 344 | //if use of secure connection is set retroactivly (after begin), then disconnect and set the new client 345 | if(_hasBegun){ 346 | client.disconnect(); 347 | client.setClient(wifiClientSecure); 348 | } 349 | 350 | 351 | //flag use of secure client 352 | _useSecureClient = true; 353 | } 354 | 355 | 356 | /* 357 | enables and sets up broadcast mode rather than station mode. This allows users to create a network from the ESP 358 | and upload using OTA even if there is no network already present. This disables all MQTT connections 359 | 360 | input: 361 | char ptr for SSID to broadcast 362 | char ptr for network password 363 | IPAddress instance for the ESP IP 364 | 365 | output: NA 366 | */ 367 | void ESPHelper::broadcastMode(const char* ssid, const char* password, const IPAddress ip){ 368 | //disconnect from any previous wifi networks (max timeout of 2 seconds) 369 | WiFi.softAPdisconnect(); 370 | WiFi.disconnect(); 371 | int timeout = 0; 372 | while(WiFi.status() != WL_DISCONNECTED && timeout < 200){ 373 | delay(10); 374 | timeout++; 375 | } 376 | //set the mode for access point 377 | WiFi.mode(WIFI_AP); 378 | //config the AP 379 | WiFi.softAPConfig(ip, ip, IPAddress(255, 255, 255, 0)); 380 | //set the ssid and password 381 | WiFi.softAP(ssid, password); 382 | 383 | //run the wifi lost callback if we were previously connected to a network 384 | if(_wifiLostCallbackSet && _connectionStatus >= WIFI_ONLY){ 385 | _wifiLostCallback(); 386 | } 387 | 388 | //update the connection status 389 | _connectionStatus = BROADCAST; 390 | _broadcastIP = ip; 391 | strcpy(_broadcastSSID, ssid); 392 | strcpy(_broadcastPASS, password); 393 | 394 | 395 | 396 | } 397 | 398 | 399 | /* 400 | disable broadcast mode and reset to station mode 401 | (causes a call to begin - may want to change this in the future...) 402 | 403 | input: NA 404 | output: NA 405 | */ 406 | void ESPHelper::disableBroadcast(){ 407 | //disconnect from any previous wifi networks (max timeout of 2 seconds) 408 | WiFi.softAPdisconnect(); 409 | WiFi.disconnect(); 410 | int timeout = 0; 411 | while(WiFi.status() != WL_DISCONNECTED && timeout < 200){ 412 | delay(10); 413 | timeout++; 414 | } 415 | _connectionStatus = NO_CONNECTION; 416 | begin(); 417 | } 418 | 419 | 420 | /* 421 | main loop - should be called as often as possible - handles wifi/mqtt connection and mqtt handler 422 | 423 | input: NA 424 | output: 425 | true on: network/server connected 426 | false on: network or server disconnected 427 | */ 428 | int ESPHelper::loop(){ 429 | if(_ssidSet){ 430 | 431 | //check for good connections and attempt a reconnect if needed 432 | if (((_mqttSet && !client.connected()) || setConnectionStatus() < WIFI_ONLY) && _connectionStatus != BROADCAST) { 433 | reconnect(); 434 | } 435 | 436 | //run the wifi loop as long as the connection status is at a minimum of BROADCAST 437 | if(_connectionStatus >= BROADCAST){ 438 | 439 | //run the MQTT loop if we have a full connection 440 | if(_connectionStatus == FULL_CONNECTION){client.loop();} 441 | 442 | //check for whether we want to use OTA and whether the system is running 443 | if(_useOTA && _OTArunning) {ArduinoOTA.handle();} 444 | 445 | //if we want to use OTA but its not running yet, start it up. 446 | else if(_useOTA && !_OTArunning){ 447 | OTA_begin(); 448 | ArduinoOTA.handle(); 449 | } 450 | 451 | return _connectionStatus; 452 | } 453 | 454 | yield(); 455 | } 456 | 457 | //return -1 for no connection because of bad network info 458 | return -1; 459 | } 460 | 461 | 462 | /* 463 | subscribe to a speicifc topic (does not add to topic list) 464 | 465 | input: 466 | char ptr to a topic to subscribe to 467 | int for QOS of the subscription 468 | output: 469 | true on: subscription success 470 | false on: subscription failed (either from PubSub lib or network is disconnected) 471 | */ 472 | bool ESPHelper::subscribe(const char* topic, int qos){ 473 | if(_connectionStatus == FULL_CONNECTION){ 474 | //set the return value to the output of subscribe 475 | bool returnVal = client.subscribe(topic, qos); 476 | //Serial.printf("Subscribe to: %s - %s\n", topic, returnVal == true ? "Success" : "Failure"); 477 | //loop mqtt client 478 | client.loop(); 479 | return returnVal; 480 | } 481 | 482 | //if not fully connected return false 483 | else{return false;} 484 | } 485 | 486 | /* 487 | add a topic to the list of subscriptions and attempt to subscribe to the topic on the spot 488 | 489 | input: 490 | char ptr for a topic to subscibe to 491 | output: 492 | true on: subscription added to list (does not guarantee that the topic was subscribed to, only that it was added to the list) 493 | false on: subscription not added to list 494 | */ 495 | bool ESPHelper::addSubscription(const char* topic){ 496 | //default return value is false 497 | bool subscribed = false; 498 | 499 | //loop throough finding the next available slot for a subscription and add it 500 | for(int i = 0; i < MAX_SUBSCRIPTIONS; i++){ 501 | if(_subscriptions[i].isUsed == false){ 502 | _subscriptions[i].topic = topic; 503 | _subscriptions[i].isUsed = true; 504 | subscribed = true; 505 | break; 506 | } 507 | } 508 | 509 | //if added to the list, subscibe to the topic 510 | if(subscribed){subscribe(topic, _qos);} 511 | 512 | return subscribed; 513 | } 514 | 515 | 516 | /* 517 | loops through list of subscriptions and attempts to subscribe to all topics 518 | 519 | input: NA 520 | output: NA 521 | */ 522 | void ESPHelper::resubscribe(){ 523 | debugPrintln("Resubscribing to all topics"); 524 | for(int i = 0; i < MAX_SUBSCRIPTIONS; i++){ 525 | if(_subscriptions[i].isUsed){ 526 | debugPrint("Topic: "); debugPrintln(_subscriptions[i].topic); 527 | subscribe(_subscriptions[i].topic, _qos); 528 | yield(); 529 | } 530 | } 531 | } 532 | 533 | 534 | /* 535 | attempts to remove a topic from the topic list 536 | 537 | input: 538 | char ptr to a topic to subscribe to 539 | output: 540 | true on: subscription removed from list (does not guarantee that the topic was unsubscribed from, only that it was removed from the list) 541 | false on: topic was not found in list and therefore cannot be removed 542 | */ 543 | bool ESPHelper::removeSubscription(const char* topic){ 544 | bool returnVal = false; 545 | createSafeString(topicStr, MAX_TOPIC_LENGTH); 546 | 547 | //loop through all subscriptions 548 | for(int i = 0; i < MAX_SUBSCRIPTIONS; i++){ 549 | //if an element is used, check for it being the one we want to remove 550 | if(_subscriptions[i].isUsed){ 551 | createSafeString(subStr, MAX_TOPIC_LENGTH); 552 | subStr = _subscriptions[i].topic; 553 | if(subStr.equals(topicStr)){ 554 | //reset the used flag to false 555 | _subscriptions[i].isUsed = false; 556 | 557 | //unsubscribe 558 | client.unsubscribe(_subscriptions[i].topic); 559 | returnVal = true; 560 | break; 561 | } 562 | } 563 | } 564 | 565 | return returnVal; 566 | } 567 | 568 | 569 | /* 570 | manually unsubscribes from a topic (This is basically just a wrapper for the pubsubclient function) 571 | 572 | input: 573 | char ptr to topic to unsubscribe from 574 | output 575 | true on: sucessful unsubscription 576 | false on: could not unsubscribe (generally b/c it was not subscribed anyways) 577 | 578 | */ 579 | bool ESPHelper::unsubscribe(const char* topic){ 580 | return client.unsubscribe(topic); 581 | } 582 | 583 | 584 | /* 585 | publish to a specified topic 586 | 587 | input: 588 | char ptr to topic to publish to 589 | char ptr to the payload to be published 590 | output: NA 591 | */ 592 | void ESPHelper::publish(const char* topic, const char* payload){ 593 | publish(topic, payload, false); 594 | } 595 | 596 | 597 | /* 598 | publish to a specified topic with a given retain level 599 | 600 | input: 601 | char ptr to topic to publish to 602 | char ptr to the payload to be published 603 | bool whether the MQTT broker should retain the message 604 | output: NA 605 | */ 606 | void ESPHelper::publish(const char* topic, const char* payload, bool retain){ 607 | client.publish(topic, payload, retain); 608 | } 609 | 610 | 611 | 612 | bool ESPHelper::publishJson(const char* topic, JsonDocument& doc, bool retain){ 613 | 614 | const size_t MAX_CHUNK_SIZE = 128; // Define a suitable chunk size 615 | 616 | //figure out the correct size 617 | size_t dataSize = measureJsonPretty(doc); 618 | 619 | if(dataSize < 1023){ 620 | //create & fill 621 | uint8_t* buf = new uint8_t[dataSize+1]; 622 | if (!buf) { 623 | return false; // Handle memory allocation failure 624 | } 625 | size_t payloadLength = serializeJsonPretty(doc, buf, dataSize+1); 626 | if (payloadLength == 0) { 627 | delete[] buf; 628 | return false; // Handle serialization failure 629 | } 630 | size_t bytesSent = 0; 631 | 632 | // Start publishing 633 | if (!client.beginPublish(topic, dataSize, retain)) { 634 | delete[] buf; 635 | return false; 636 | } 637 | 638 | while (bytesSent < payloadLength) { 639 | size_t chunkSize = min(payloadLength - bytesSent, MAX_CHUNK_SIZE); 640 | size_t result = client.write((const uint8_t*)(buf + bytesSent), chunkSize); 641 | if (result != chunkSize) { 642 | delete[] buf; 643 | client.endPublish(); // Ensure to end publish on error 644 | return false; // Handle error 645 | } 646 | bytesSent += result; 647 | } 648 | 649 | //cleanup 650 | delete[] buf; 651 | buf = NULL; 652 | 653 | return client.endPublish(); 654 | } 655 | 656 | return false; 657 | } 658 | 659 | 660 | 661 | /* 662 | set the callback function for MQTT 663 | 664 | input: 665 | function ptr that matches the MQTT callback function signature in pubsubclient 666 | output: NA 667 | */ 668 | void ESPHelper::setMQTTCallback(MQTT_CALLBACK_SIGNATURE){ 669 | _mqttCallback = callback; 670 | 671 | //only set the callback if using mqtt AND the system has already been started. Otherwise just save it for later 672 | if(_hasBegun && _mqttSet) { 673 | client.setCallback(_mqttCallback); 674 | } 675 | _mqttCallbackSet = true; 676 | } 677 | 678 | 679 | /* 680 | legacy funtion - here for compatibility. Sets the callback function for MQTT (see function above) 681 | 682 | input: 683 | function ptr that matches the MQTT callback function signature in pubsubclient 684 | output: 685 | true (always) 686 | */ 687 | bool ESPHelper::setCallback(MQTT_CALLBACK_SIGNATURE){ 688 | setMQTTCallback(callback); 689 | return true; 690 | } 691 | 692 | 693 | /* 694 | sets a custom function to run when connection to wifi is established 695 | 696 | input: 697 | void function ptr with no params 698 | output: NA 699 | */ 700 | void ESPHelper::setWifiCallback(void (*callback)()){ 701 | _wifiCallback = callback; 702 | _wifiCallbackSet = true; 703 | } 704 | 705 | 706 | /* 707 | sets a custom function to run when connection to wifi is lost 708 | 709 | input: 710 | void function ptr with no params 711 | output: NA 712 | */ 713 | void ESPHelper::setWifiLostCallback(void (*callback)()){ 714 | _wifiLostCallback = callback; 715 | _wifiLostCallbackSet = true; 716 | } 717 | 718 | 719 | /* 720 | attempts to connect to wifi & mqtt server if not connected 721 | 722 | input: NA 723 | output: NA 724 | */ 725 | void ESPHelper::reconnect() { 726 | 727 | 728 | if(reconnectMetro.check() && _connectionStatus != BROADCAST && setConnectionStatus() != FULL_CONNECTION){ 729 | debugPrintln("Attempting WiFi Connection..."); 730 | //attempt to connect to the wifi if connection is lost 731 | if(WiFi.status() != WL_CONNECTED){ 732 | _connectionStatus = NO_CONNECTION; 733 | 734 | #ifdef ESP32 735 | reconnect(); 736 | #else 737 | //WiFi.reconnect(); 738 | #endif 739 | 740 | } 741 | 742 | // make sure we are connected to WIFI before attemping to reconnect to MQTT 743 | //----note---- maybe want to reset tryCount whenever we succeed at getting wifi connection? 744 | if(WiFi.status() == WL_CONNECTED){ 745 | //if the wifi previously wasnt connected but now is, run the callback 746 | if(_connectionStatus < WIFI_ONLY && _wifiCallbackSet){ 747 | _wifiCallback(); 748 | } 749 | 750 | 751 | debugPrintln("\n---WIFI Connected!---"); 752 | _connectionStatus = WIFI_ONLY; 753 | 754 | 755 | //attempt to connect to mqtt when we finally get connected to WiFi 756 | if(_mqttSet){ 757 | 758 | static int timeout = 0; //allow a max of 5 mqtt connection attempts before timing out 759 | if (!client.connected() && timeout < 5) { 760 | debugPrint("Attemping MQTT connection"); 761 | 762 | client.disconnect(); 763 | client.setServer(_currentNet.getMqttHost(), _currentNet.getMqttPort()); 764 | 765 | 766 | if(_useSecureClient){client.setClient(wifiClientSecure);} 767 | else{client.setClient(wifiClient);} 768 | 769 | 770 | int connected = 0; 771 | 772 | //connect to mqtt with user/pass 773 | if (_mqttUserSet && _willMessageSet && _willTopicSet) { 774 | debugPrintln(" - Using user & last will"); 775 | debugPrintln(String("\t Client Name: " + String(_clientName.c_str()))); 776 | debugPrintln(String("\t User Name: " + String(_currentNet.getMqttUser()))); 777 | debugPrintln(String("\t Password: " + String(_currentNet.getMqttPass()))); 778 | debugPrintln(String("\t Will Topic: " + String(_currentNet.getMqttWillTopic()))); 779 | debugPrintln(String("\t Will QOS: " + String(_currentNet.getMqttWillQoS()))); 780 | debugPrintln(String("\t Will Retain?: " + String(_currentNet.getMqttWillRetain()))); 781 | debugPrintln(String("\t Will Message: " + String(_currentNet.getMqttWillMessage()))); 782 | connected = client.connect( 783 | (char*) _clientName.c_str(), 784 | _currentNet.getMqttUser(), 785 | _currentNet.getMqttPass(), 786 | _currentNet.getMqttWillTopic(), 787 | _currentNet.getMqttWillQoS(), 788 | _currentNet.getMqttWillRetain(), 789 | _currentNet.getMqttWillMessage()); 790 | } 791 | 792 | //connect to mqtt without credentials 793 | else if (!_mqttUserSet && _willMessageSet && _willTopicSet) { 794 | debugPrintln(" - Using last will"); 795 | debugPrintln(String("\t Client Name: " + String(_clientName.c_str()))); 796 | debugPrintln(String("\t Will Topic: " + String(_currentNet.getMqttWillTopic()))); 797 | debugPrintln(String("\t Will QOS: " + String(_currentNet.getMqttWillQoS()))); 798 | debugPrintln(String("\t Will Retain?: " + String(_currentNet.getMqttWillRetain()))); 799 | debugPrintln(String("\t Will Message: " + String(_currentNet.getMqttWillMessage()))); 800 | connected = client.connect( 801 | (char*) _clientName.c_str(), 802 | _currentNet.getMqttWillTopic(), 803 | _currentNet.getMqttWillQoS(), 804 | _currentNet.getMqttWillRetain(), 805 | _currentNet.getMqttWillMessage() 806 | ); 807 | } else if (_mqttUserSet && !_willMessageSet) { 808 | debugPrintln(" - Using user"); 809 | debugPrintln(String("\t Client Name: " + String(_clientName.c_str()))); 810 | debugPrintln(String("\t User Name: " + String(_currentNet.getMqttUser()))); 811 | debugPrintln(String("\t Password: " + String(_currentNet.getMqttPass()))); 812 | connected = client.connect( 813 | (char*) _clientName.c_str(), 814 | _currentNet.getMqttUser(), 815 | _currentNet.getMqttPass() 816 | ); 817 | } else { 818 | debugPrintln(" - Using default"); 819 | debugPrintln(String("\t Client Name: " + String(_clientName.c_str()))); 820 | connected = client.connect((char*) _clientName.c_str()); 821 | } 822 | 823 | //if connected, subscribe to the topic(s) we want to be notified about 824 | if (connected) { 825 | debugPrintln(" -- Connected"); 826 | 827 | #if ESP_SDK_VERSION_MAJOR > 2 828 | //if using https, verify the fingerprint of the server before setting full connection (return on fail) 829 | if(_useSecureClient){ 830 | if (wifiClientSecure.verify(_fingerprint, _currentNet.mqttHost)) { 831 | debugPrintln("Certificate Matches - SUCESS"); 832 | } else { 833 | debugPrintln("Certificate Doesn't Match - FAIL"); 834 | return; 835 | } 836 | } 837 | #else 838 | if(_useSecureClient){debugPrintln("Certificate Not Supported on this SDK Version. Must use SDK 2.x.x");} 839 | #endif 840 | 841 | if(_mqttCallbackSet){ 842 | debugPrintln("Setting MQTT callback"); 843 | client.setCallback(_mqttCallback); 844 | } 845 | 846 | _connectionStatus = FULL_CONNECTION; 847 | resubscribe(); 848 | timeout = 0; 849 | } 850 | else{ 851 | debugPrintln(" -- Failed"); 852 | } 853 | timeout++; 854 | 855 | } 856 | else if (timeout >= 5) { 857 | debugPrintln(" -- Failed to connect to MQTT after 5 attempts. Giving up."); 858 | _connectionStatus = WIFI_ONLY; 859 | } 860 | } 861 | 862 | 863 | } 864 | 865 | //reset the reconnect metro 866 | reconnectMetro.reset(); 867 | } 868 | } 869 | 870 | 871 | /* 872 | internal function used to set _connectionStatus based on the WiFi & MQTT status 873 | 874 | input: NA 875 | output: NA 876 | */ 877 | int ESPHelper::setConnectionStatus(){ 878 | 879 | //assume no connection 880 | int returnVal = NO_CONNECTION; 881 | 882 | //make sure were not in broadcast mode 883 | if(_connectionStatus != BROADCAST){ 884 | 885 | //if connected to wifi set the mode to wifi only and run the callback if needed 886 | if(WiFi.status() == WL_CONNECTED){ 887 | if(_connectionStatus < WIFI_ONLY && _wifiCallbackSet){ //if the wifi previously wasnt connected but now is, run the callback 888 | _wifiCallback(); 889 | } 890 | returnVal = WIFI_ONLY; 891 | 892 | //if mqtt is connected as well then set the status to full connection 893 | if(client.connected()){ 894 | returnVal = FULL_CONNECTION; 895 | } 896 | } 897 | 898 | //assuming above fails, then wifi is not connected. 899 | //if the wifi is not connected and the wifi lost callback has been set, then call it 900 | else if(_connectionStatus >= WIFI_ONLY && _wifiLostCallbackSet){ 901 | _wifiLostCallback(); 902 | } 903 | } 904 | 905 | 906 | else{ 907 | returnVal = BROADCAST; 908 | } 909 | 910 | //set the connection status and return 911 | _connectionStatus = returnVal; 912 | return returnVal; 913 | } 914 | 915 | 916 | 917 | /* 918 | input: 919 | 920 | output: 921 | */ 922 | void ESPHelper::updateNetwork(){ 923 | debugPrintln("\tDisconnecting from WiFi"); 924 | WiFi.disconnect(); 925 | debugPrintln("\tAttempting to begin on new network"); 926 | 927 | //set the wifi mode 928 | WiFi.mode(WIFI_STA); 929 | 930 | //connect to the network 931 | if(_passSet && _ssidSet){WiFi.begin(_currentNet.getSsid(), _currentNet.getPass());} 932 | else if(_ssidSet){WiFi.begin(_currentNet.getSsid());} 933 | else{WiFi.begin("NO_SSID_SET");} 934 | 935 | WiFi.setSleep(false); 936 | //#ifdef ESP32 937 | WiFi.setAutoReconnect(true); 938 | //#endif 939 | 940 | debugPrintln("\tSetting new MQTT server"); 941 | //setup the mqtt broker info 942 | if(_mqttSet){client.setServer(_currentNet.getMqttHost(), _currentNet.getMqttPort());} 943 | else{client.setServer("192.0.2.0", 1883);} 944 | 945 | debugPrintln("\tDone - Ready for next reconnect attempt"); 946 | } 947 | 948 | 949 | /* 950 | generate unique MQTT name from MAC addr 951 | 952 | input: 953 | uint8* (array) representing the ESP mac address 954 | 955 | output: 956 | string version of that mac address # 957 | */ 958 | String ESPHelper::macToStr(const uint8_t* mac){ 959 | 960 | char buf[18]; 961 | snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X", 962 | mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 963 | return String(buf); 964 | } 965 | 966 | 967 | 968 | /* 969 | return the current NetInfo state 970 | 971 | input: NA 972 | output: 973 | NetInfo of the current network 974 | */ 975 | NetInfo* ESPHelper::getNetInfo(){ 976 | return &_currentNet; 977 | } 978 | 979 | 980 | /* 981 | return the current SSID 982 | 983 | input: NA 984 | output: 985 | char ptr referencing the current SSID 986 | */ 987 | const char* ESPHelper::getSSID(){ 988 | if(_ssidSet && _connectionStatus != BROADCAST){return _currentNet.getSsid();} 989 | else if(_connectionStatus == BROADCAST){return _broadcastSSID;} 990 | return "SSID NOT SET"; 991 | } 992 | 993 | 994 | /* 995 | set a new SSID - does not automatically disconnect from current network if already connected 996 | 997 | input: 998 | char ptr to the SSID to connect to 999 | output: NA 1000 | */ 1001 | void ESPHelper::setSSID(const char* ssid){ 1002 | _currentNet.setSsid(ssid); 1003 | _ssidSet = true; 1004 | } 1005 | 1006 | 1007 | /* 1008 | return the current network password 1009 | 1010 | input: NA 1011 | output: 1012 | char ptr to the current network pass 1013 | */ 1014 | const char* ESPHelper::getPASS(){ 1015 | if(_passSet && _connectionStatus != BROADCAST){return _currentNet.getPass();} 1016 | else if(_connectionStatus == BROADCAST){return _broadcastPASS;} 1017 | return "PASS NOT SET"; 1018 | } 1019 | 1020 | 1021 | /* 1022 | set a new network password - does not automatically disconnect from current network if already connected 1023 | 1024 | input: 1025 | char ptr to the new network pass to use 1026 | output: NA 1027 | */ 1028 | void ESPHelper::setPASS(const char* pass){ 1029 | _currentNet.setPass(pass); 1030 | _passSet = true; 1031 | } 1032 | 1033 | 1034 | /* 1035 | return the current MQTT server IP 1036 | 1037 | input: NA 1038 | output: 1039 | char ptr to the current MQTT IP or Hostname 1040 | */ 1041 | const char* ESPHelper::getMQTTIP(){ 1042 | if(_mqttSet){return _currentNet.getMqttHost();} 1043 | return "MQTT IP NOT SET"; 1044 | } 1045 | 1046 | 1047 | /* 1048 | set a new MQTT server IP or Hostname - does not automatically disconnect from current network/server if already connected 1049 | 1050 | input: 1051 | char ptr to a new MQTT IP or Hostname str 1052 | output: NA 1053 | */ 1054 | void ESPHelper::setMQTTIP(const char* mqttIP){ 1055 | _currentNet.setMqttHost(mqttIP); 1056 | _mqttSet = true; 1057 | } 1058 | 1059 | 1060 | /* 1061 | set a new MQTT server IP - does not automatically disconnect from current network/server if already connected 1062 | Includes MQTT user and Pass 1063 | 1064 | input: 1065 | char ptr to a new MQTT IP or Hostname str 1066 | char ptr to MQTT username 1067 | char ptr to MQTT password 1068 | output: NA 1069 | */ 1070 | void ESPHelper::setMQTTIP(const char* mqttIP, const char* mqttUser, const char* mqttPass){ 1071 | _currentNet.setMqttHost(mqttIP); 1072 | _currentNet.setMqttUser(mqttUser); 1073 | _currentNet.setMqttPass(mqttPass); 1074 | _mqttSet = true; 1075 | _mqttUserSet = true; 1076 | } 1077 | 1078 | 1079 | /* 1080 | set a new MQTT Will - does not automatically disconnect from current network/server if already connected 1081 | 1082 | input: 1083 | char ptr to MQTT will topic 1084 | char ptr to MQTT will payload 1085 | output: NA 1086 | */ 1087 | void ESPHelper::setWill(const char *willTopic, const char *willMessage){ 1088 | _currentNet.setMqttWillTopic(willTopic); 1089 | _currentNet.setMqttWillMessage(willMessage); 1090 | _willTopicSet = true; 1091 | _willMessageSet = true; 1092 | } 1093 | 1094 | 1095 | /* 1096 | set a new MQTT Will - does not automatically disconnect from current network/server if already connected 1097 | 1098 | input: 1099 | char ptr to MQTT will topic 1100 | char ptr to MQTT will payload 1101 | int to MQTT will QOS 1102 | int to MQTT will retain 1103 | output: NA 1104 | */ 1105 | void ESPHelper::setWill(const char *willTopic, const char *willMessage, const int willQoS, const bool willRetain){ 1106 | _currentNet.setMqttWillTopic(willTopic); 1107 | _currentNet.setMqttWillMessage(willMessage); 1108 | _currentNet.setMqttWillQoS(willQoS); 1109 | _currentNet.setMqttWillRetain(willRetain); 1110 | _willTopicSet = true; 1111 | _willMessageSet = true; 1112 | } 1113 | 1114 | 1115 | /* 1116 | return the QOS level for mqtt 1117 | 1118 | input: NA 1119 | output: 1120 | int for current MQTT QOS 1121 | */ 1122 | int ESPHelper::getMQTTQOS(){ 1123 | return _qos; 1124 | 1125 | } 1126 | 1127 | 1128 | /* 1129 | set the QOS level for mqtt 1130 | 1131 | input: 1132 | int for MQTT QOS level 1133 | output: NA 1134 | */ 1135 | void ESPHelper::setMQTTQOS(int qos){ 1136 | _qos = qos; 1137 | } 1138 | 1139 | 1140 | /* 1141 | return the local IP address of the ESP as a string 1142 | 1143 | input: NA 1144 | output: 1145 | String representing the current IP address of the ESP 1146 | */ 1147 | String ESPHelper::getIP(){ 1148 | if(_connectionStatus != BROADCAST){ 1149 | return WiFi.localIP().toString(); 1150 | } 1151 | else{ 1152 | return _broadcastIP.toString(); 1153 | } 1154 | 1155 | } 1156 | 1157 | 1158 | /* 1159 | return the local IP address of the ESP 1160 | 1161 | input: NA 1162 | output: 1163 | IPAddress instance representing the current IP address of the ESP 1164 | */ 1165 | IPAddress ESPHelper::getIPAddress(){ 1166 | if(_connectionStatus != BROADCAST){ 1167 | return WiFi.localIP(); 1168 | } 1169 | else{ 1170 | return _broadcastIP; 1171 | } 1172 | } 1173 | 1174 | 1175 | /* 1176 | get the current connection status of ESPHelper 1177 | 1178 | input: NA 1179 | output: 1180 | int for current ESPHelper connection status(refer to connStatus enum is sharedData.h) 1181 | */ 1182 | int ESPHelper::getStatus(){ 1183 | return _connectionStatus; 1184 | } 1185 | 1186 | 1187 | 1188 | 1189 | /* 1190 | DEBUG ONLY - print the subscribed topics list to the serial line 1191 | 1192 | input: NA 1193 | output: NA 1194 | */ 1195 | void ESPHelper::listSubscriptions(){ 1196 | for(int i = 0; i < MAX_SUBSCRIPTIONS; i++){ 1197 | if(_subscriptions[i].isUsed){ 1198 | debugPrintln(_subscriptions[i].topic); 1199 | } 1200 | } 1201 | } 1202 | 1203 | 1204 | 1205 | /* 1206 | enable use of OTA updates 1207 | 1208 | input: NA 1209 | output: NA 1210 | */ 1211 | void ESPHelper::OTA_enable(){ 1212 | _useOTA = true; 1213 | OTA_begin(); 1214 | } 1215 | 1216 | 1217 | /* 1218 | begin the OTA subsystem but with a check for connectivity and enabled use of OTA 1219 | 1220 | input: NA 1221 | output: NA 1222 | */ 1223 | void ESPHelper::OTA_begin(){ 1224 | if(_connectionStatus >= BROADCAST && _useOTA){ 1225 | ArduinoOTA.begin(); 1226 | _OTArunning = true; 1227 | } 1228 | } 1229 | 1230 | 1231 | /* 1232 | disable use of OTA updates 1233 | 1234 | input: NA 1235 | output: NA 1236 | */ 1237 | void ESPHelper::OTA_disable(){ 1238 | _useOTA = false; 1239 | _OTArunning = false; 1240 | } 1241 | 1242 | 1243 | /* 1244 | set a password for OTA updates 1245 | 1246 | input: 1247 | char ptr containing the OTA password to be used 1248 | output: NA 1249 | */ 1250 | void ESPHelper::OTA_setPassword(const char* pass){ 1251 | ArduinoOTA.setPassword(pass); 1252 | } 1253 | 1254 | 1255 | /* 1256 | set the hostname of the ESP for OTA uploads 1257 | 1258 | input: 1259 | char ptr containing the intended hostname 1260 | output: NA 1261 | */ 1262 | void ESPHelper::OTA_setHostname(const char* hostname){ 1263 | strcpy(_hostname, hostname); 1264 | ArduinoOTA.setHostname(_hostname); 1265 | } 1266 | 1267 | 1268 | /* 1269 | set the hostname of the ESP for OTA uploads and append the ESPHelper version number 1270 | 1271 | input: 1272 | char ptr containing the intended hostname 1273 | output: NA 1274 | */ 1275 | void ESPHelper::OTA_setHostnameWithVersion(const char* hostname){ 1276 | strcpy(_hostname, hostname); 1277 | strcat(_hostname, "----"); 1278 | strcat(_hostname, VERSION); 1279 | 1280 | ArduinoOTA.setHostname(_hostname); 1281 | } 1282 | 1283 | 1284 | /* 1285 | returns the current ESPHelper hostname 1286 | 1287 | input: NA 1288 | output: 1289 | char ptr containing the ESP hostname 1290 | */ 1291 | char* ESPHelper::getHostname(){ 1292 | return _hostname; 1293 | } 1294 | 1295 | 1296 | /* 1297 | returns internal pubsubclient ptr (use with caution) 1298 | 1299 | input: NA 1300 | output: 1301 | pubsubclient ptr 1302 | */ 1303 | PubSubClient* ESPHelper::getMQTTClient(){ 1304 | return &client; 1305 | } 1306 | 1307 | 1308 | /* 1309 | sets a new buffer size for mqtt messages in/out 1310 | 1311 | input: int bytes of how large the buffer should be 1312 | output: 1313 | true: success 1314 | false: failure 1315 | */ 1316 | bool ESPHelper::setMQTTBuffer(int size){ 1317 | #if PUB_SUB_VERSION >= 28 1318 | return client.setBufferSize(size); 1319 | #else 1320 | return false; 1321 | #endif 1322 | } 1323 | --------------------------------------------------------------------------------