├── pics ├── MQTT.png ├── Main.png ├── Save.png ├── Input.png └── Input_With_Scan.png ├── utils ├── restyle.sh └── astyle_library.conf ├── .codespellrc ├── .github ├── workflows │ ├── auto-github-actions.yml │ ├── report-size-deltas.yml │ └── spell-check.yml ├── dependabot.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── stale.yml ├── .gitignore ├── LICENSE ├── library.properties ├── CONTRIBUTING.md ├── examples ├── ESPAsync_WiFi │ ├── dynamicParams.h │ ├── Credentials.h │ ├── ESPAsync_WiFi.ino │ └── defines.h └── ESPAsync_WiFi_MQTT │ ├── dynamicParams.h │ ├── Credentials.h │ ├── defines.h │ └── ESPAsync_WiFi_MQTT.ino ├── library.json ├── keywords.txt ├── src └── ESPAsync_WiFiManager_Lite_Debug.h ├── platformio └── platformio.ini ├── changelog.md ├── esp32c3_ESPAsyncWebServer_Patch ├── WebAuthentication.cpp └── AsyncWebSocket.cpp └── esp32s2_WebServer_Patch ├── WebServer.h └── WebServer.cpp /pics/MQTT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khoih-prog/ESPAsync_WiFiManager_Lite/HEAD/pics/MQTT.png -------------------------------------------------------------------------------- /pics/Main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khoih-prog/ESPAsync_WiFiManager_Lite/HEAD/pics/Main.png -------------------------------------------------------------------------------- /pics/Save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khoih-prog/ESPAsync_WiFiManager_Lite/HEAD/pics/Save.png -------------------------------------------------------------------------------- /pics/Input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khoih-prog/ESPAsync_WiFiManager_Lite/HEAD/pics/Input.png -------------------------------------------------------------------------------- /pics/Input_With_Scan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khoih-prog/ESPAsync_WiFiManager_Lite/HEAD/pics/Input_With_Scan.png -------------------------------------------------------------------------------- /utils/restyle.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for dir in . ; do 4 | find $dir -type f \( -name "*.c" -o -name "*.h" -o -name "*.cpp" -o -name "*.ino" \) -exec astyle --suffix=none --options=./utils/astyle_library.conf \{\} \; 5 | done 6 | 7 | -------------------------------------------------------------------------------- /.codespellrc: -------------------------------------------------------------------------------- 1 | # See: https://github.com/codespell-project/codespell#using-a-config-file 2 | [codespell] 3 | # In the event of a false positive, add the problematic word, in all lowercase, to a comma-separated list here: 4 | ignore-words-list = , 5 | check-filenames = 6 | check-hidden = 7 | skip = ./.git,./src,./examples,./Packages_Patches,./LibraryPatches 8 | -------------------------------------------------------------------------------- /.github/workflows/auto-github-actions.yml: -------------------------------------------------------------------------------- 1 | name: auto-github-actions 2 | on: [push] 3 | jobs: 4 | check-bats-version: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v3 8 | - uses: actions/setup-node@v3 9 | with: 10 | node-version: '14' 11 | - run: npm install -g bats 12 | - run: bats -v 13 | -------------------------------------------------------------------------------- /.github/workflows/report-size-deltas.yml: -------------------------------------------------------------------------------- 1 | name: Report Size Deltas 2 | 3 | on: 4 | schedule: 5 | - cron: '*/5 * * * *' 6 | 7 | jobs: 8 | report: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Comment size deltas reports to PRs 13 | uses: arduino/report-size-deltas@v1 14 | with: 15 | # The name of the workflow artifact created by the "Compile Examples" workflow 16 | sketches-reports-source: sketches-reports 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Visual Studio Code settings 2 | .vscode 3 | 4 | # Prerequisites 5 | *.d 6 | 7 | # Compiled Object files 8 | *.slo 9 | *.lo 10 | *.o 11 | *.obj 12 | 13 | # Precompiled Headers 14 | *.gch 15 | *.pch 16 | 17 | # Compiled Dynamic libraries 18 | *.so 19 | *.dylib 20 | *.dll 21 | 22 | # Fortran module files 23 | *.mod 24 | *.smod 25 | 26 | # Compiled Static libraries 27 | *.lai 28 | *.la 29 | *.a 30 | *.lib 31 | 32 | # Executables 33 | *.exe 34 | *.out 35 | *.app 36 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # See: https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#about-the-dependabotyml-file 2 | version: 2 3 | 4 | updates: 5 | # Configure check for outdated GitHub Actions actions in workflows. 6 | # See: https://docs.github.com/en/github/administering-a-repository/keeping-your-actions-up-to-date-with-dependabot 7 | - package-ecosystem: github-actions 8 | directory: / # Check the repository's workflows under /.github/workflows/ 9 | schedule: 10 | interval: daily 11 | -------------------------------------------------------------------------------- /.github/workflows/spell-check.yml: -------------------------------------------------------------------------------- 1 | name: Spell Check 2 | 3 | on: 4 | pull_request: 5 | push: 6 | schedule: 7 | # run every Tuesday at 3 AM UTC 8 | - cron: "0 3 * * 2" 9 | workflow_dispatch: 10 | repository_dispatch: 11 | 12 | jobs: 13 | spellcheck: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v3 19 | 20 | # See: https://github.com/codespell-project/actions-codespell/blob/master/README.md 21 | - name: Spell check 22 | uses: codespell-project/actions-codespell@master 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | daysUntilStale: 60 4 | daysUntilClose: 14 5 | limitPerRun: 30 6 | staleLabel: stale 7 | exemptLabels: 8 | - pinned 9 | - security 10 | - "to be implemented" 11 | - "for reference" 12 | - "move to PR" 13 | - "enhancement" 14 | 15 | only: issues 16 | onlyLabels: [] 17 | exemptProjects: false 18 | exemptMilestones: false 19 | exemptAssignees: false 20 | 21 | markComment: > 22 | [STALE_SET] This issue has been automatically marked as stale because it has not had 23 | recent activity. It will be closed in 14 days if no further activity occurs. Thank you 24 | for your contributions. 25 | 26 | unmarkComment: > 27 | [STALE_CLR] This issue has been removed from the stale queue. Please ensure activity to keep it opening the future. 28 | 29 | closeComment: > 30 | [STALE_DEL] This stale issue has been automatically closed. Thank you for your contributions. 31 | 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Khoi Hoang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ESPAsync_WiFiManager_Lite 2 | version=1.10.5 3 | author=Khoi Hoang 4 | maintainer=Khoi Hoang 5 | license=MIT 6 | sentence=Light-Weight MultiWiFi/Credentials Async WiFiManager for ESP32 (including ESP32-S2, ESP32-S3 and ESP32-C3) and ESP8266 boards. Powerful-yet-simple-to-use feature to enable adding dynamic custom parameters. 7 | paragraph=Library using AsyncWebServer to configure MultiWiFi/Credentials at runtime for ESP32 (including ESP32-S2, ESP32-S3 and ESP32-C3) and ESP8266 boards. You can also specify DHCP HostName, static AP and STA IP. Use much less memory compared to full-fledge WiFiManager. Config Portal will be auto-adjusted to match the number of dynamic custom parameters. Optional default Credentials to be autoloaded into Config Portal to use or change instead of manually input. Credentials are saved in LittleFS, SPIFFS or EEPROM. New powerful-yet-simple-to-use feature to enable adding dynamic custom parameters from sketch and input using the same Config Portal. Double or MultiDetectDetector as well as Virtual Switches feature permits entering Config Portal as requested. Configurable Customs HTML Headers, including Customs Style, Customs Head Elements, CORS Header. Now using correct ESP32 chipIP 8 | category=Communication 9 | url=https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite 10 | architectures=esp8266,esp32 11 | depends=ESP_DoubleResetDetector,ESP_MultiResetDetector 12 | includes=ESPAsync_WiFiManager_Lite.h 13 | -------------------------------------------------------------------------------- /utils/astyle_library.conf: -------------------------------------------------------------------------------- 1 | # Code formatting rules for Arduino libraries, modified from for KH libraries: 2 | # 3 | # https://github.com/arduino/Arduino/blob/master/build/shared/examples_formatter.conf 4 | # 5 | 6 | # astyle --style=allman -s2 -t2 -C -S -xW -Y -M120 -f -p -xg -H -xb -c --xC120 -xL *.h *.cpp *.ino 7 | 8 | --mode=c 9 | --lineend=linux 10 | --style=allman 11 | 12 | # -r or -R 13 | #--recursive 14 | 15 | # -c => Converts tabs into spaces 16 | convert-tabs 17 | 18 | # -s2 => 2 spaces indentation 19 | --indent=spaces=2 20 | 21 | # -t2 => tab =2 spaces 22 | #--indent=tab=2 23 | 24 | # -C 25 | --indent-classes 26 | 27 | # -S 28 | --indent-switches 29 | 30 | # -xW 31 | --indent-preproc-block 32 | 33 | # -Y => indent classes, switches (and cases), comments starting at column 1 34 | --indent-col1-comments 35 | 36 | # -M120 => maximum of 120 spaces to indent a continuation line 37 | --max-continuation-indent=120 38 | 39 | # -xC120 => max‑code‑length will break a line if the code exceeds # characters 40 | --max-code-length=120 41 | 42 | # -f => 43 | --break-blocks 44 | 45 | # -p => put a space around operators 46 | --pad-oper 47 | 48 | # -xg => Insert space padding after commas 49 | --pad-comma 50 | 51 | # -H => put a space after if/for/while 52 | pad-header 53 | 54 | # -xb => Break one line headers (e.g. if/for/while) 55 | --break-one-line-headers 56 | 57 | # -c => Converts tabs into spaces 58 | #--convert-tabs 59 | 60 | # if you like one-liners, keep them 61 | #keep-one-line-statements 62 | 63 | # -xV 64 | --attach-closing-while 65 | 66 | #unpad-paren 67 | 68 | # -xp 69 | remove-comment-prefix 70 | 71 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to ESPAsync_WiFiManager_Lite 2 | 3 | ### Reporting Bugs 4 | 5 | Please report bugs in [ESPAsync_WiFiManager_Lite](https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite/issues/new) if you find them. 6 | 7 | However, before reporting a bug please check through the following: 8 | 9 | * [Existing Open Issues](https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite/issues) - someone might have already encountered this. 10 | 11 | If you don't find anything, please [open a new issue](https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite/issues/new). 12 | 13 | --- 14 | 15 | ### How to submit a bug report 16 | 17 | Please ensure to specify the following: 18 | 19 | * Arduino IDE version (e.g. 1.8.19) or Platform.io version 20 | * `ESP8266` or `ESP32` Core Version (e.g. ESP8266 core v3.1.1 or ESP32 v2.0.6) 21 | * Contextual information (e.g. what you were trying to achieve) 22 | * Simplest possible steps to reproduce 23 | * Anything that might be relevant in your opinion, such as: 24 | * Operating system (Windows, Ubuntu, etc.) and the output of `uname -a` 25 | * Network configuration 26 | 27 | Please be educated, civilized and constructive as you've always been. Disrespective posts against [GitHub Code of Conduct](https://docs.github.com/en/site-policy/github-terms/github-event-code-of-conduct) will be ignored and deleted. 28 | 29 | --- 30 | 31 | ### Example 32 | 33 | ``` 34 | Arduino IDE version: 1.8.19 35 | ESP8266 Core Version 3.1.1 36 | OS: Ubuntu 20.04 LTS 37 | Linux xy-Inspiron-3593 5.15.0-58-generic #64~20.04.1-Ubuntu SMP Fri Jan 6 16:42:31 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux 38 | 39 | Context: 40 | I encountered a crash while using this library 41 | 42 | Steps to reproduce: 43 | 1. ... 44 | 2. ... 45 | 3. ... 46 | 4. ... 47 | ``` 48 | 49 | --- 50 | 51 | ### Sending Feature Requests 52 | 53 | Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful. 54 | 55 | There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. 56 | 57 | --- 58 | 59 | ### Sending Pull Requests 60 | 61 | Pull Requests with changes and fixes are also welcome! 62 | 63 | Please use the `astyle` to reformat the updated library code as follows (demo for Ubuntu Linux) 64 | 65 | 1. Change directory to the library GitHub 66 | 67 | ``` 68 | xy@xy-Inspiron-3593:~$ cd Arduino/xy/ESPAsync_WiFiManager_Lite_GitHub/ 69 | xy@xy-Inspiron-3593:~/Arduino/xy/ESPAsync_WiFiManager_Lite_GitHub$ 70 | ``` 71 | 72 | 2. Issue astyle command 73 | 74 | ``` 75 | xy@xy-Inspiron-3593:~/Arduino/xy/ESPAsync_WiFiManager_Lite_GitHub$ bash utils/restyle.sh 76 | ``` 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /examples/ESPAsync_WiFi/dynamicParams.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | dynamicParams.h 3 | For ESP8266 / ESP32 boards 4 | 5 | ESPAsync_WiFiManager_Lite (https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite) is a library 6 | for the ESP32/ESP8266 boards to enable store Credentials in EEPROM/SPIFFS/LittleFS for easy 7 | configuration/reconfiguration and autoconnect/autoreconnect of WiFi and other services without Hardcoding. 8 | 9 | Built by Khoi Hoang https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite 10 | Licensed under MIT license 11 | *****************************************************************************************************************************/ 12 | 13 | #ifndef dynamicParams_h 14 | #define dynamicParams_h 15 | 16 | #include "defines.h" 17 | 18 | // USE_DYNAMIC_PARAMETERS defined in defined.h 19 | 20 | /////////////// Start dynamic Credentials /////////////// 21 | 22 | //Defined in 23 | /************************************** 24 | #define MAX_ID_LEN 5 25 | #define MAX_DISPLAY_NAME_LEN 16 26 | 27 | typedef struct 28 | { 29 | char id [MAX_ID_LEN + 1]; 30 | char displayName [MAX_DISPLAY_NAME_LEN + 1]; 31 | char *pdata; 32 | uint8_t maxlen; 33 | } MenuItem; 34 | **************************************/ 35 | 36 | #if USE_DYNAMIC_PARAMETERS 37 | 38 | #define MAX_BLYNK_SERVER_LEN 34 39 | #define MAX_BLYNK_TOKEN_LEN 34 40 | 41 | char Blynk_Server1 [MAX_BLYNK_SERVER_LEN + 1] = "account.duckdns.org"; 42 | char Blynk_Token1 [MAX_BLYNK_TOKEN_LEN + 1] = "token1"; 43 | 44 | char Blynk_Server2 [MAX_BLYNK_SERVER_LEN + 1] = "account.ddns.net"; 45 | char Blynk_Token2 [MAX_BLYNK_TOKEN_LEN + 1] = "token2"; 46 | 47 | #define MAX_BLYNK_PORT_LEN 6 48 | char Blynk_Port [MAX_BLYNK_PORT_LEN + 1] = "8080"; 49 | 50 | #define MAX_MQTT_SERVER_LEN 34 51 | char MQTT_Server [MAX_MQTT_SERVER_LEN + 1] = "mqtt.duckdns.org"; 52 | 53 | MenuItem myMenuItems [] = 54 | { 55 | { "sv1", "Blynk Server1", Blynk_Server1, MAX_BLYNK_SERVER_LEN }, 56 | { "tk1", "Token1", Blynk_Token1, MAX_BLYNK_TOKEN_LEN }, 57 | { "sv2", "Blynk Server2", Blynk_Server2, MAX_BLYNK_SERVER_LEN }, 58 | { "tk2", "Token2", Blynk_Token2, MAX_BLYNK_TOKEN_LEN }, 59 | { "prt", "Port", Blynk_Port, MAX_BLYNK_PORT_LEN }, 60 | { "mqt", "MQTT Server", MQTT_Server, MAX_MQTT_SERVER_LEN }, 61 | }; 62 | 63 | uint16_t NUM_MENU_ITEMS = sizeof(myMenuItems) / sizeof(MenuItem); //MenuItemSize; 64 | 65 | #else 66 | 67 | MenuItem myMenuItems [] = {}; 68 | 69 | uint16_t NUM_MENU_ITEMS = 0; 70 | 71 | #endif //USE_DYNAMIC_PARAMETERS 72 | 73 | 74 | #endif //dynamicParams_h 75 | -------------------------------------------------------------------------------- /examples/ESPAsync_WiFi_MQTT/dynamicParams.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | dynamicParams.h 3 | For ESP8266 / ESP32 boards 4 | 5 | ESPAsync_WiFiManager_Lite (https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite) is a library 6 | for the ESP32/ESP8266 boards to enable store Credentials in EEPROM/SPIFFS/LittleFS for easy 7 | configuration/reconfiguration and autoconnect/autoreconnect of WiFi and other services without Hardcoding. 8 | 9 | Built by Khoi Hoang https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite 10 | Licensed under MIT license 11 | *****************************************************************************************************************************/ 12 | 13 | #ifndef dynamicParams_h 14 | #define dynamicParams_h 15 | 16 | #include "defines.h" 17 | 18 | // USE_DYNAMIC_PARAMETERS defined in defined.h 19 | 20 | /////////////// Start dynamic Credentials /////////////// 21 | 22 | //Defined in 23 | /************************************** 24 | #define MAX_ID_LEN 5 25 | #define MAX_DISPLAY_NAME_LEN 16 26 | 27 | typedef struct 28 | { 29 | char id [MAX_ID_LEN + 1]; 30 | char displayName [MAX_DISPLAY_NAME_LEN + 1]; 31 | char *pdata; 32 | uint8_t maxlen; 33 | } MenuItem; 34 | **************************************/ 35 | 36 | #if USE_DYNAMIC_PARAMETERS 37 | 38 | #define AIO_SERVER_LEN 20 39 | #define AIO_SERVERPORT_LEN 6 40 | #define AIO_USERNAME_LEN 20 41 | #define AIO_KEY_LEN 40 42 | 43 | // 44 | #define AIO_PUB_TOPIC_LEN 40 45 | #define AIO_SUB_TOPIC_LEN 40 46 | 47 | char AIO_SERVER [AIO_SERVER_LEN + 1] = "io.adafruit.com"; 48 | char AIO_SERVERPORT [AIO_SERVERPORT_LEN + 1] = "1883"; //1883, or 8883 for SSL 49 | char AIO_USERNAME [AIO_USERNAME_LEN + 1] = "private"; 50 | char AIO_KEY [AIO_KEY_LEN + 1] = "private"; 51 | 52 | char AIO_PUB_TOPIC [AIO_PUB_TOPIC_LEN + 1] = "/feeds/Temperature"; 53 | char AIO_SUB_TOPIC [AIO_SUB_TOPIC_LEN + 1] = "/feeds/LED_Control"; 54 | 55 | MenuItem myMenuItems [] = 56 | { 57 | { "svr", "AIO_SERVER", AIO_SERVER, AIO_SERVER_LEN }, 58 | { "prt", "AIO_SERVERPORT", AIO_SERVERPORT, AIO_SERVERPORT_LEN }, 59 | { "usr", "AIO_USERNAME", AIO_USERNAME, AIO_USERNAME_LEN }, 60 | { "key", "AIO_KEY", AIO_KEY, AIO_KEY_LEN }, 61 | { "pub", "AIO_PUB_TOPIC", AIO_PUB_TOPIC, AIO_PUB_TOPIC_LEN }, 62 | { "sub", "AIO_SUB_TOPIC", AIO_SUB_TOPIC, AIO_SUB_TOPIC_LEN }, 63 | }; 64 | 65 | uint16_t NUM_MENU_ITEMS = sizeof(myMenuItems) / sizeof(MenuItem); //MenuItemSize; 66 | 67 | #else 68 | 69 | MenuItem myMenuItems [] = {}; 70 | 71 | uint16_t NUM_MENU_ITEMS = 0; 72 | 73 | #endif //USE_DYNAMIC_PARAMETERS 74 | 75 | 76 | #endif //dynamicParams_h 77 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ESPAsync_WiFiManager_Lite", 3 | "version": "1.10.5", 4 | "keywords": "wifi, wi-fi, Async, Async-WebServer, Async-WiFiManager, Communication, MultiWiFi, multi-wifi, ESP32, ESP32-S2, ESP32-S3, esp32-c3, ESP8266, Credentials, config-portal, DoubleReset, MultiReset, Detector, dynamic-params, dynamic, customs-header", 5 | "description": "Library using AsyncWebServer to configure MultiWiFi/Credentials at runtime for ESP32 (including ESP32-S2, ESP32-S3 and ESP32-C3) and ESP8266 boards. You can also specify DHCP HostName, static AP and STA IP. Use much less memory compared to full-fledge WiFiManager. Config Portal will be auto-adjusted to match the number of dynamic custom parameters. Optional default Credentials to be autoloaded into Config Portal to use or change instead of manually input. Credentials are saved in LittleFS, SPIFFS or EEPROM. New powerful-yet-simple-to-use feature to enable adding dynamic custom parameters from sketch and input using the same Config Portal. Double or MultiDetectDetector as well as Virtual Switches feature permits entering Config Portal as requested. Configurable Customs HTML Headers, including Customs Style, Customs Head Elements, CORS Header. Now using correct ESP32 chipIP", 6 | "authors": 7 | { 8 | "name": "Khoi Hoang", 9 | "url": "https://github.com/khoih-prog", 10 | "maintainer": true 11 | }, 12 | "repository": 13 | { 14 | "type": "git", 15 | "url": "https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite" 16 | }, 17 | "homepage": "https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite", 18 | "export": { 19 | "exclude": [ 20 | "linux", 21 | "extras", 22 | "tests" 23 | ] 24 | }, 25 | "dependencies": 26 | [ 27 | { 28 | "name": "AsyncTCP", 29 | "version": "https://github.com/me-no-dev/AsyncTCP", 30 | "platforms": [ "espressif32"] 31 | }, 32 | { 33 | "name": "ESPAsyncTCP", 34 | "version": "https://github.com/khoih-prog/ESPAsyncTCP", 35 | "platforms": [ "espressif8266"] 36 | }, 37 | { 38 | "name": "ESP Async WebServer", 39 | "version": "https://github.com/khoih-prog/ESPAsyncWebServer", 40 | "platforms": ["espressif8266", "espressif32"] 41 | }, 42 | { 43 | "owner": "me-no-dev", 44 | "name": "ESPAsyncUDP", 45 | "version": "0.0.0-alpha+sha.697c75a025", 46 | "platforms": ["espressif8266"] 47 | }, 48 | { 49 | "name": "ESPAsyncDNSServer", 50 | "version": "https://github.com/khoih-prog/ESPAsyncDNSServer", 51 | "platforms": ["espressif8266", "espressif32"] 52 | }, 53 | { 54 | "owner": "khoih-prog", 55 | "name": "ESP_DoubleResetDetector", 56 | "version": "^1.3.2", 57 | "platforms": ["espressif8266", "espressif32"] 58 | }, 59 | { 60 | "owner": "khoih-prog", 61 | "name": "ESP_MultiResetDetector", 62 | "version": "^1.3.2", 63 | "platforms": ["espressif8266", "espressif32"] 64 | } 65 | ], 66 | "frameworks": "*", 67 | "platforms": ["espressif8266", "espressif32"], 68 | "examples": "examples/*/*/*.ino", 69 | "headers": "ESPAsync_WiFiManager_Lite.h" 70 | } 71 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | #################################################### 2 | # Syntax Coloring Map For ESPAsync_WiFiManager_Lite 3 | #################################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | ESPAsync_WiFiManager_Lite KEYWORD1 10 | ESP_WM_LITE_Configuration KEYWORD1 11 | MenuItem KEYWORD1 12 | WiFi_Credentials KEYWORD1 13 | 14 | 15 | ####################################### 16 | # Methods and Functions (KEYWORD2) 17 | ####################################### 18 | 19 | connectWiFi KEYWORD2 20 | begin KEYWORD2 21 | run KEYWORD2 22 | setHostname 23 | setConfigPortalIP KEYWORD2 24 | setConfigPortalChannel KEYWORD2 25 | setConfigPortal KEYWORD2 26 | setSTAStaticIPConfig KEYWORD2 27 | getWiFiSSID KEYWORD2 28 | getWiFiPW KEYWORD2 29 | getWiFiStatus KEYWORD2 30 | getFullConfigData KEYWORD2 31 | localIP KEYWORD2 32 | clearConfigData KEYWORD2 33 | isConfigDataValid KEYWORD2 34 | isConfigMode KEYWORD2 35 | resetFunc KEYWORD2 36 | resetAndEnterConfigPortal KEYWORD2 37 | resetAndEnterConfigPortalPersistent KEYWORD2 38 | IPAddressToString KEYWORD2 39 | setCustomsStyle KEYWORD2 40 | getCustomsStyle KEYWORD2 41 | setCustomsHeadElement KEYWORD2 42 | getCustomsHeadElement KEYWORD2 43 | setCORSHeader KEYWORD2 44 | getCORSHeader KEYWORD2 45 | extLoadDynamicData KEYWORD2 46 | extSaveDynamicData KEYWORD2 47 | 48 | ####################################### 49 | 50 | getChipID KEYWORD2 51 | getChipOUI KEYWORD2 52 | 53 | ESP_getChipId KEYWORD2 54 | ESP_getChipOUI KEYWORD2 55 | 56 | ####################################### 57 | # Constants (LITERAL1) 58 | ####################################### 59 | 60 | # LITERAL1 61 | 62 | ESP_ASYNC_WIFI_MANAGER_LITE_VERSION LITERAL1 63 | 64 | ESP_ASYNC_WIFI_MANAGER_LITE_VERSION_MAJOR LITERAL1 65 | ESP_ASYNC_WIFI_MANAGER_LITE_VERSION_MINOR LITERAL1 66 | ESP_ASYNC_WIFI_MANAGER_LITE_VERSION_PATCH LITERAL1 67 | ESP_ASYNC_WIFI_MANAGER_LITE_VERSION_INT LITERAL1 68 | 69 | CONFIG_DATA_SIZE LITERAL1 70 | NUM_MENU_ITEMS LITERAL1 71 | ESP_WM_LITE_HTML_HEAD LITERAL1 72 | ESP_WM_LITE_HTML_HEAD_START LITERAL1 73 | ESP_WM_LITE_HTML_HEAD_STYLE LITERAL1 74 | ESP_WM_LITE_HTML_HEAD_END LITERAL1 75 | ESP_WM_LITE_FLDSET_START LITERAL1 76 | ESP_WM_LITE_FLDSET_END LITERAL1 77 | ESP_WM_LITE_HTML_PARAM LITERAL1 78 | ESP_WM_LITE_HTML_BUTTON LITERAL1 79 | ESP_WM_LITE_HTML_SCRIPT LITERAL1 80 | ESP_WM_LITE_HTML_SCRIPT_ITEM LITERAL1 81 | ESP_WM_LITE_HTML_SCRIPT_END LITERAL1 82 | ESP_WM_LITE_HTML_END LITERAL1 83 | 84 | WM_HTTP_HEAD_CL LITERAL1 85 | WM_HTTP_HEAD_TEXT_HTML LITERAL1 86 | WM_HTTP_HEAD_TEXT_PLAIN LITERAL1 87 | WM_HTTP_CACHE_CONTROL LITERAL1 88 | WM_HTTP_NO_STORE LITERAL1 89 | WM_HTTP_PRAGMA LITERAL1 90 | WM_HTTP_NO_CACHE LITERAL1 91 | WM_HTTP_EXPIRES LITERAL1 92 | WM_HTTP_CORS LITERAL1 93 | WM_HTTP_CORS_ALLOW_ALL LITERAL1 94 | 95 | LED_ON LITERAL1 96 | LED_OFF LITERAL1 97 | 98 | USING_BOARD_NAME LITERAL1 99 | 100 | FORCED_CONFIG_PORTAL_FLAG_DATA LITERAL1 101 | FORCED_PERS_CONFIG_PORTAL_FLAG_DATA LITERAL1 102 | FORCED_CONFIG_PORTAL_FLAG_DATA_SIZE LITERAL1 103 | 104 | -------------------------------------------------------------------------------- /examples/ESPAsync_WiFi/Credentials.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | Credentials.h 3 | For ESP8266 / ESP32 boards 4 | 5 | ESPAsync_WiFiManager_Lite (https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite) is a library 6 | for the ESP32/ESP8266 boards to enable store Credentials in EEPROM/SPIFFS/LittleFS for easy 7 | configuration/reconfiguration and autoconnect/autoreconnect of WiFi and other services without Hardcoding. 8 | 9 | Built by Khoi Hoang https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite 10 | Licensed under MIT license 11 | *****************************************************************************************************************************/ 12 | 13 | #ifndef Credentials_h 14 | #define Credentials_h 15 | 16 | #include "defines.h" 17 | 18 | /// Start Default Config Data ////////////////// 19 | 20 | /* 21 | #define SSID_MAX_LEN 32 22 | //From v1.0.3, WPA2 passwords can be up to 63 characters long. 23 | #define PASS_MAX_LEN 64 24 | 25 | typedef struct 26 | { 27 | char wifi_ssid[SSID_MAX_LEN]; 28 | char wifi_pw [PASS_MAX_LEN]; 29 | } WiFi_Credentials; 30 | 31 | #define NUM_WIFI_CREDENTIALS 2 32 | 33 | // Configurable items besides fixed Header, just add board_name 34 | #define NUM_CONFIGURABLE_ITEMS ( ( 2 * NUM_WIFI_CREDENTIALS ) + 1 ) 35 | //////////////// 36 | 37 | typedef struct Configuration 38 | { 39 | char header [16]; 40 | WiFi_Credentials WiFi_Creds [NUM_WIFI_CREDENTIALS]; 41 | char board_name [24]; 42 | int checkSum; 43 | } ESP_WM_LITE_Configuration; 44 | */ 45 | 46 | #define TO_LOAD_DEFAULT_CONFIG_DATA false 47 | 48 | #if TO_LOAD_DEFAULT_CONFIG_DATA 49 | 50 | // This feature is primarily used in development to force a known set of values as Config Data 51 | // It will NOT force the Config Portal to activate. Use DRD or erase Config Data with ESPAsync_WiFiManager.clearConfigData() 52 | 53 | // Used mostly for development and debugging. FORCES default values to be loaded each run. 54 | // Config Portal data input will be ignored and overridden by DEFAULT_CONFIG_DATA 55 | //bool LOAD_DEFAULT_CONFIG_DATA = true; 56 | 57 | // Used mostly once debugged. Assumes good data already saved in device. 58 | // Config Portal data input will be override DEFAULT_CONFIG_DATA 59 | bool LOAD_DEFAULT_CONFIG_DATA = false; 60 | 61 | 62 | ESP_WM_LITE_Configuration defaultConfig = 63 | { 64 | //char header[16], dummy, not used 65 | #if ESP8266 66 | "ESP8266_Async", 67 | #else 68 | "ESP32_Async", 69 | #endif 70 | 71 | // WiFi_Credentials WiFi_Creds [NUM_WIFI_CREDENTIALS]; 72 | // WiFi_Credentials.wifi_ssid and WiFi_Credentials.wifi_pw 73 | "SSID1", "password1", 74 | "SSID2", "password2", 75 | //char board_name [24]; 76 | 77 | #if ESP8266 78 | "ESP8266_Async-Control", 79 | #else 80 | "ESP32_Async-Control", 81 | #endif 82 | 83 | // terminate the list 84 | //int checkSum, dummy, not used 85 | 0 86 | /////////// End Default Config Data ///////////// 87 | }; 88 | 89 | #else 90 | 91 | bool LOAD_DEFAULT_CONFIG_DATA = false; 92 | 93 | ESP_WM_LITE_Configuration defaultConfig; 94 | 95 | #endif // TO_LOAD_DEFAULT_CONFIG_DATA 96 | 97 | /////////// End Default Config Data ///////////// 98 | 99 | 100 | #endif //Credentials_h 101 | -------------------------------------------------------------------------------- /examples/ESPAsync_WiFi_MQTT/Credentials.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | Credentials.h 3 | For ESP8266 / ESP32 boards 4 | 5 | ESPAsync_WiFiManager_Lite (https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite) is a library 6 | for the ESP32/ESP8266 boards to enable store Credentials in EEPROM/SPIFFS/LittleFS for easy 7 | configuration/reconfiguration and autoconnect/autoreconnect of WiFi and other services without Hardcoding. 8 | 9 | Built by Khoi Hoang https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite 10 | Licensed under MIT license 11 | *****************************************************************************************************************************/ 12 | 13 | #ifndef Credentials_h 14 | #define Credentials_h 15 | 16 | #include "defines.h" 17 | 18 | /// Start Default Config Data ////////////////// 19 | 20 | /* 21 | #define SSID_MAX_LEN 32 22 | //From v1.0.3, WPA2 passwords can be up to 63 characters long. 23 | #define PASS_MAX_LEN 64 24 | 25 | typedef struct 26 | { 27 | char wifi_ssid[SSID_MAX_LEN]; 28 | char wifi_pw [PASS_MAX_LEN]; 29 | } WiFi_Credentials; 30 | 31 | #define NUM_WIFI_CREDENTIALS 2 32 | 33 | // Configurable items besides fixed Header, just add board_name 34 | #define NUM_CONFIGURABLE_ITEMS ( ( 2 * NUM_WIFI_CREDENTIALS ) + 1 ) 35 | //////////////// 36 | 37 | typedef struct Configuration 38 | { 39 | char header [16]; 40 | WiFi_Credentials WiFi_Creds [NUM_WIFI_CREDENTIALS]; 41 | char board_name [24]; 42 | int checkSum; 43 | } ESP_WM_LITE_Configuration; 44 | */ 45 | 46 | #define TO_LOAD_DEFAULT_CONFIG_DATA false 47 | 48 | #if TO_LOAD_DEFAULT_CONFIG_DATA 49 | 50 | // This feature is primarily used in development to force a known set of values as Config Data 51 | // It will NOT force the Config Portal to activate. Use DRD or erase Config Data with ESPAsync_WiFiManager.clearConfigData() 52 | 53 | // Used mostly for development and debugging. FORCES default values to be loaded each run. 54 | // Config Portal data input will be ignored and overridden by DEFAULT_CONFIG_DATA 55 | //bool LOAD_DEFAULT_CONFIG_DATA = true; 56 | 57 | // Used mostly once debugged. Assumes good data already saved in device. 58 | // Config Portal data input will be override DEFAULT_CONFIG_DATA 59 | bool LOAD_DEFAULT_CONFIG_DATA = false; 60 | 61 | 62 | ESP_WM_LITE_Configuration defaultConfig = 63 | { 64 | //char header[16], dummy, not used 65 | #if ESP8266 66 | "ESP8266_Async", 67 | #else 68 | "ESP32_Async", 69 | #endif 70 | 71 | // WiFi_Credentials WiFi_Creds [NUM_WIFI_CREDENTIALS]; 72 | // WiFi_Credentials.wifi_ssid and WiFi_Credentials.wifi_pw 73 | "SSID1", "password1", 74 | "SSID2", "password2", 75 | //char board_name [24]; 76 | 77 | #if ESP8266 78 | "ESP8266_Async-Control", 79 | #else 80 | "ESP32_Async-Control", 81 | #endif 82 | 83 | // terminate the list 84 | //int checkSum, dummy, not used 85 | 0 86 | /////////// End Default Config Data ///////////// 87 | }; 88 | 89 | #else 90 | 91 | bool LOAD_DEFAULT_CONFIG_DATA = false; 92 | 93 | ESP_WM_LITE_Configuration defaultConfig; 94 | 95 | #endif // TO_LOAD_DEFAULT_CONFIG_DATA 96 | 97 | /////////// End Default Config Data ///////////// 98 | 99 | 100 | #endif //Credentials_h 101 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Describe the bug 11 | 12 | A clear and concise description of what the bug is. 13 | 14 | ### Steps to Reproduce 15 | 16 | Steps to reproduce the behavior. Including the [MRE](https://stackoverflow.com/help/minimal-reproducible-example) sketches 17 | 18 | ### Expected behavior 19 | 20 | A clear and concise description of what you expected to happen. 21 | 22 | ### Actual behavior 23 | 24 | A clear and concise description of what you expected to happen. 25 | 26 | ### Debug and AT-command log (if applicable) 27 | 28 | A clear and concise description of what you expected to happen. 29 | 30 | ### Screenshots 31 | 32 | If applicable, add screenshots to help explain your problem. 33 | 34 | --- 35 | 36 | ### Information 37 | 38 | Please ensure to specify the following: 39 | 40 | * Arduino IDE version (e.g. 1.8.19) or Platform.io version 41 | * `ESP8266` or `ESP32` Core Version (e.g. ESP8266 core v3.1.1 or ESP32 v2.0.6) 42 | * Contextual information (e.g. what you were trying to achieve) 43 | * Simplest possible steps to reproduce 44 | * Anything that might be relevant in your opinion, such as: 45 | * Operating system (Windows, Ubuntu, etc.) and the output of `uname -a` 46 | * Network configuration 47 | 48 | Please be educated, civilized and constructive as you've always been. Disrespective posts against [GitHub Code of Conduct](https://docs.github.com/en/site-policy/github-terms/github-event-code-of-conduct) will be ignored and deleted. 49 | 50 | --- 51 | 52 | ### Example 53 | 54 | ``` 55 | Arduino IDE version: 1.8.19 56 | ESP8266 Core Version 3.1.1 57 | OS: Ubuntu 20.04 LTS 58 | Linux xy-Inspiron-3593 5.15.0-58-generic #64~20.04.1-Ubuntu SMP Fri Jan 6 16:42:31 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux 59 | 60 | Context: 61 | I encountered a crash while using this library 62 | 63 | Steps to reproduce: 64 | 1. ... 65 | 2. ... 66 | 3. ... 67 | 4. ... 68 | ``` 69 | 70 | --- 71 | 72 | ### Sending Feature Requests 73 | 74 | Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful. 75 | 76 | There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. 77 | 78 | --- 79 | 80 | ### Sending Pull Requests 81 | 82 | Pull Requests with changes and fixes are also welcome! 83 | 84 | Please use the `astyle` to reformat the updated library code as follows (demo for Ubuntu Linux) 85 | 86 | 1. Change directory to the library GitHub 87 | 88 | ``` 89 | xy@xy-Inspiron-3593:~$ cd Arduino/xy/ESPAsync_WiFiManager_Lite_GitHub/ 90 | xy@xy-Inspiron-3593:~/Arduino/xy/ESPAsync_WiFiManager_Lite_GitHub$ 91 | ``` 92 | 93 | 2. Issue astyle command 94 | 95 | ``` 96 | xy@xy-Inspiron-3593:~/Arduino/xy/ESPAsync_WiFiManager_Lite_GitHub$ bash utils/restyle.sh 97 | ``` 98 | 99 | 100 | Context: 101 | I encountered a crash while using this library 102 | 103 | Steps to reproduce: 104 | 1. ... 105 | 2. ... 106 | 3. ... 107 | 4. ... 108 | ``` 109 | 110 | --- 111 | 112 | ### Sending Feature Requests 113 | 114 | Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful. 115 | 116 | There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. 117 | 118 | --- 119 | 120 | ### Sending Pull Requests 121 | 122 | Pull Requests with changes and fixes are also welcome! 123 | 124 | Please use the `astyle` to reformat the updated library code as follows (demo for Ubuntu Linux) 125 | 126 | 1. Change directory to the library GitHub 127 | 128 | ``` 129 | xy@xy-Inspiron-3593:~$ cd Arduino/xy/ESPAsync_WiFiManager_Lite_GitHub/ 130 | xy@xy-Inspiron-3593:~/Arduino/xy/ESPAsync_WiFiManager_Lite_GitHub$ 131 | ``` 132 | 133 | 2. Issue astyle command 134 | 135 | ``` 136 | xy@xy-Inspiron-3593:~/Arduino/xy/ESPAsync_WiFiManager_Lite_GitHub$ bash utils/restyle.sh 137 | ``` 138 | 139 | -------------------------------------------------------------------------------- /examples/ESPAsync_WiFi/ESPAsync_WiFi.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ESPAsync_WiFi.ino 3 | For ESP8266 / ESP32 boards 4 | 5 | ESPAsync_WiFiManager_Lite (https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite) is a library 6 | for the ESP32/ESP8266 boards to enable store Credentials in EEPROM/SPIFFS/LittleFS for easy 7 | configuration/reconfiguration and autoconnect/autoreconnect of WiFi and other services without Hardcoding. 8 | 9 | Built by Khoi Hoang https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite 10 | Licensed under MIT license 11 | *****************************************************************************************************************************/ 12 | 13 | #include "defines.h" 14 | #include "Credentials.h" 15 | #include "dynamicParams.h" 16 | 17 | ESPAsync_WiFiManager_Lite* ESPAsync_WiFiManager; 18 | 19 | void heartBeatPrint() 20 | { 21 | static int num = 1; 22 | 23 | if (WiFi.status() == WL_CONNECTED) 24 | Serial.print("H"); // H means connected to WiFi 25 | else 26 | { 27 | if (ESPAsync_WiFiManager->isConfigMode()) 28 | Serial.print("C"); // C means in Config Mode 29 | else 30 | Serial.print("F"); // F means not connected to WiFi 31 | } 32 | 33 | if (num == 80) 34 | { 35 | Serial.println(); 36 | num = 1; 37 | } 38 | else if (num++ % 10 == 0) 39 | { 40 | Serial.print(F(" ")); 41 | } 42 | } 43 | 44 | void check_status() 45 | { 46 | static unsigned long checkstatus_timeout = 0; 47 | 48 | //KH 49 | #define HEARTBEAT_INTERVAL 20000L 50 | // Print hearbeat every HEARTBEAT_INTERVAL (20) seconds. 51 | if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) 52 | { 53 | heartBeatPrint(); 54 | checkstatus_timeout = millis() + HEARTBEAT_INTERVAL; 55 | } 56 | } 57 | 58 | #if USING_CUSTOMS_STYLE 59 | const char NewCustomsStyle[] PROGMEM = 60 | ""; 62 | #endif 63 | 64 | void setup() 65 | { 66 | // Debug console 67 | Serial.begin(115200); 68 | while (!Serial); 69 | 70 | delay(200); 71 | 72 | Serial.print(F("\nStarting ESPAsync_WiFi using ")); 73 | Serial.print(FS_Name); 74 | Serial.print(F(" on ")); 75 | Serial.println(ARDUINO_BOARD); 76 | Serial.println(ESP_ASYNC_WIFI_MANAGER_LITE_VERSION); 77 | 78 | #if USING_MRD 79 | Serial.println(ESP_MULTI_RESET_DETECTOR_VERSION); 80 | #else 81 | Serial.println(ESP_DOUBLE_RESET_DETECTOR_VERSION); 82 | #endif 83 | 84 | ESPAsync_WiFiManager = new ESPAsync_WiFiManager_Lite(); 85 | String AP_SSID = "your_customized_ssid"; 86 | String AP_PWD = "your_customized_pwd"; 87 | 88 | // Set customized AP SSID and PWD 89 | ESPAsync_WiFiManager->setConfigPortal(AP_SSID, AP_PWD); 90 | 91 | // Optional to change default AP IP(192.168.4.1) and channel(10) 92 | //ESPAsync_WiFiManager->setConfigPortalIP(IPAddress(192, 168, 120, 1)); 93 | ESPAsync_WiFiManager->setConfigPortalChannel(0); 94 | 95 | #if USING_CUSTOMS_STYLE 96 | ESPAsync_WiFiManager->setCustomsStyle(NewCustomsStyle); 97 | #endif 98 | 99 | #if USING_CUSTOMS_HEAD_ELEMENT 100 | ESPAsync_WiFiManager->setCustomsHeadElement(PSTR("")); 101 | #endif 102 | 103 | #if USING_CORS_FEATURE 104 | ESPAsync_WiFiManager->setCORSHeader(PSTR("Your Access-Control-Allow-Origin")); 105 | #endif 106 | 107 | // Set customized DHCP HostName 108 | ESPAsync_WiFiManager->begin(HOST_NAME); 109 | //Or use default Hostname "ESP_XXXXXX" 110 | //ESPAsync_WiFiManager->begin(); 111 | } 112 | 113 | #if USE_DYNAMIC_PARAMETERS 114 | void displayCredentials() 115 | { 116 | Serial.println(F("\nYour stored Credentials :")); 117 | 118 | for (uint16_t i = 0; i < NUM_MENU_ITEMS; i++) 119 | { 120 | Serial.print(myMenuItems[i].displayName); 121 | Serial.print(F(" = ")); 122 | Serial.println(myMenuItems[i].pdata); 123 | } 124 | } 125 | 126 | void displayCredentialsInLoop() 127 | { 128 | static bool displayedCredentials = false; 129 | 130 | if (!displayedCredentials) 131 | { 132 | for (int i = 0; i < NUM_MENU_ITEMS; i++) 133 | { 134 | if (!strlen(myMenuItems[i].pdata)) 135 | { 136 | break; 137 | } 138 | 139 | if ( i == (NUM_MENU_ITEMS - 1) ) 140 | { 141 | displayedCredentials = true; 142 | displayCredentials(); 143 | } 144 | } 145 | } 146 | } 147 | 148 | #endif 149 | 150 | void loop() 151 | { 152 | ESPAsync_WiFiManager->run(); 153 | check_status(); 154 | 155 | #if USE_DYNAMIC_PARAMETERS 156 | displayCredentialsInLoop(); 157 | #endif 158 | } 159 | -------------------------------------------------------------------------------- /examples/ESPAsync_WiFi/defines.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | defines.h 3 | For ESP8266 / ESP32 boards 4 | 5 | ESPAsync_WiFiManager_Lite (https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite) is a library 6 | for the ESP32/ESP8266 boards to enable store Credentials in EEPROM/SPIFFS/LittleFS for easy 7 | configuration/reconfiguration and autoconnect/autoreconnect of WiFi and other services without Hardcoding. 8 | 9 | Built by Khoi Hoang https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite 10 | Licensed under MIT license 11 | *****************************************************************************************************************************/ 12 | 13 | #ifndef defines_h 14 | #define defines_h 15 | 16 | #if !( ESP8266 || ESP32) 17 | #error This code is intended to run only on the ESP8266/ESP32 boards ! Please check your Tools->Board setting. 18 | #endif 19 | 20 | /* Comment this out to disable prints and save space */ 21 | #define ESP_WM_LITE_DEBUG_OUTPUT Serial 22 | 23 | #define _ESP_WM_LITE_LOGLEVEL_ 2 24 | 25 | // use builtin LED to show configuration mode 26 | #define USE_LED_BUILTIN true 27 | 28 | #define USING_MRD true 29 | 30 | #if USING_MRD 31 | #define MULTIRESETDETECTOR_DEBUG true 32 | 33 | // Number of seconds after reset during which a 34 | // subseqent reset will be considered a double reset. 35 | #define MRD_TIMEOUT 10 36 | 37 | // RTC Memory Address for the DoubleResetDetector to use 38 | #define MRD_ADDRESS 0 39 | 40 | #if (_ESP_WM_LITE_LOGLEVEL_ > 3) 41 | #warning Using MULTI_RESETDETECTOR 42 | #endif 43 | #else 44 | #define DOUBLERESETDETECTOR_DEBUG true 45 | 46 | // Number of seconds after reset during which a 47 | // subseqent reset will be considered a double reset. 48 | #define DRD_TIMEOUT 10 49 | 50 | // RTC Memory Address for the DoubleResetDetector to use 51 | #define DRD_ADDRESS 0 52 | 53 | #if (_ESP_WM_LITE_LOGLEVEL_ > 3) 54 | #warning Using DOUBLE_RESETDETECTOR 55 | #endif 56 | #endif 57 | 58 | ///////////////////////////////////////////// 59 | 60 | // LittleFS has higher priority than SPIFFS 61 | #if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 2) ) 62 | #define USE_LITTLEFS true 63 | #define USE_SPIFFS false 64 | #elif defined(ARDUINO_ESP32C3_DEV) 65 | // For core v1.0.6-, ESP32-C3 only supporting SPIFFS and EEPROM. To use v2.0.0+ for LittleFS 66 | #define USE_LITTLEFS false 67 | #define USE_SPIFFS true 68 | #else 69 | #define USE_LITTLEFS true 70 | #define USE_SPIFFS false 71 | #endif 72 | 73 | ///////////////////////////////////////////// 74 | 75 | // Add customs headers from v1.2.0 76 | #define USING_CUSTOMS_STYLE true 77 | #define USING_CUSTOMS_HEAD_ELEMENT true 78 | #define USING_CORS_FEATURE true 79 | 80 | ///////////////////////////////////////////// 81 | 82 | // Force some params 83 | #define TIMEOUT_RECONNECT_WIFI 10000L 84 | 85 | // Permit running CONFIG_TIMEOUT_RETRYTIMES_BEFORE_RESET times before reset hardware 86 | // to permit user another chance to config. Only if Config Data is valid. 87 | // If Config Data is invalid, this has no effect as Config Portal will persist 88 | #define RESET_IF_CONFIG_TIMEOUT true 89 | 90 | // Permitted range of user-defined CONFIG_TIMEOUT_RETRYTIMES_BEFORE_RESET between 2-100 91 | #define CONFIG_TIMEOUT_RETRYTIMES_BEFORE_RESET 5 92 | 93 | // Config Timeout 120s (default 60s). Applicable only if Config Data is Valid 94 | #define CONFIG_TIMEOUT 120000L 95 | 96 | ///////////////////////////////////////////// 97 | 98 | // Permit input only one set of WiFi SSID/PWD. The other can be "NULL or "blank" 99 | // Default is false (if not defined) => must input 2 sets of SSID/PWD 100 | #define REQUIRE_ONE_SET_SSID_PW true //false 101 | 102 | // Max times to try WiFi per loop() iteration. To avoid blocking issue in loop() 103 | // Default 1 if not defined, and minimum 1. 104 | #define MAX_NUM_WIFI_RECON_TRIES_PER_LOOP 2 105 | 106 | // Default no interval between recon WiFi if lost 107 | // Max permitted interval will be 10mins 108 | // Uncomment to use. Be careful, WiFi reconnect will be delayed if using this method 109 | // Only use whenever urgent tasks in loop() can't be delayed. But if so, it's better you have to rewrite your code, e.g. using higher priority tasks. 110 | #define WIFI_RECON_INTERVAL 30000 111 | 112 | ///////////////////////////////////////////// 113 | 114 | // Permit reset hardware if no WiFi to permit user another chance to access Config Portal. 115 | #define RESET_IF_NO_WIFI false 116 | 117 | ///////////////////////////////////////////// 118 | 119 | #define USE_DYNAMIC_PARAMETERS true 120 | 121 | ///////////////////////////////////////////// 122 | 123 | #define SCAN_WIFI_NETWORKS true 124 | 125 | // To be able to manually input SSID, not from a scanned SSID lists 126 | #define MANUAL_SSID_INPUT_ALLOWED true 127 | 128 | // From 2-15 129 | #define MAX_SSID_IN_LIST 8 130 | 131 | ///////////////////////////////////////////// 132 | 133 | // Optional, to use Board Name in Menu 134 | #define USING_BOARD_NAME true 135 | 136 | ///////////////////////////////////////////// 137 | 138 | #include 139 | 140 | #if ESP8266 141 | #define HOST_NAME "ESP8266Async-Control" 142 | #else 143 | #define HOST_NAME "ESP32Async-Control" 144 | #endif 145 | 146 | #ifdef LED_BUILTIN 147 | #define LED_PIN LED_BUILTIN 148 | #else 149 | #define LED_PIN 13 150 | #endif 151 | 152 | #endif //defines_h 153 | -------------------------------------------------------------------------------- /examples/ESPAsync_WiFi_MQTT/defines.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | defines.h 3 | For ESP8266 / ESP32 boards 4 | 5 | ESPAsync_WiFiManager_Lite (https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite) is a library 6 | for the ESP32/ESP8266 boards to enable store Credentials in EEPROM/SPIFFS/LittleFS for easy 7 | configuration/reconfiguration and autoconnect/autoreconnect of WiFi and other services without Hardcoding. 8 | 9 | Built by Khoi Hoang https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite 10 | Licensed under MIT license 11 | *****************************************************************************************************************************/ 12 | 13 | #ifndef defines_h 14 | #define defines_h 15 | 16 | #if !( ESP8266 || ESP32) 17 | #error This code is intended to run only on the ESP8266/ESP32 boards ! Please check your Tools->Board setting. 18 | #endif 19 | 20 | /* Comment this out to disable prints and save space */ 21 | #define ESP_WM_LITE_DEBUG_OUTPUT Serial 22 | 23 | #define _ESP_WM_LITE_LOGLEVEL_ 2 24 | 25 | // use builtin LED to show configuration mode 26 | #define USE_LED_BUILTIN true 27 | 28 | #define USING_MRD true 29 | 30 | #if USING_MRD 31 | #define MULTIRESETDETECTOR_DEBUG true 32 | 33 | // Number of seconds after reset during which a 34 | // subseqent reset will be considered a double reset. 35 | #define MRD_TIMEOUT 10 36 | 37 | // RTC Memory Address for the DoubleResetDetector to use 38 | #define MRD_ADDRESS 0 39 | 40 | #if (_ESP_WM_LITE_LOGLEVEL_ > 3) 41 | #warning Using MULTI_RESETDETECTOR 42 | #endif 43 | #else 44 | #define DOUBLERESETDETECTOR_DEBUG true 45 | 46 | // Number of seconds after reset during which a 47 | // subseqent reset will be considered a double reset. 48 | #define DRD_TIMEOUT 10 49 | 50 | // RTC Memory Address for the DoubleResetDetector to use 51 | #define DRD_ADDRESS 0 52 | 53 | #if (_ESP_WM_LITE_LOGLEVEL_ > 3) 54 | #warning Using DOUBLE_RESETDETECTOR 55 | #endif 56 | #endif 57 | 58 | ///////////////////////////////////////////// 59 | 60 | // LittleFS has higher priority than SPIFFS 61 | #if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 2) ) 62 | #define USE_LITTLEFS true 63 | #define USE_SPIFFS false 64 | #elif defined(ARDUINO_ESP32C3_DEV) 65 | // For core v1.0.6-, ESP32-C3 only supporting SPIFFS and EEPROM. To use v2.0.0+ for LittleFS 66 | #define USE_LITTLEFS false 67 | #define USE_SPIFFS true 68 | #else 69 | #define USE_LITTLEFS true 70 | #define USE_SPIFFS false 71 | #endif 72 | 73 | ///////////////////////////////////////////// 74 | 75 | // Add customs headers from v1.2.0 76 | #define USING_CUSTOMS_STYLE true 77 | #define USING_CUSTOMS_HEAD_ELEMENT true 78 | #define USING_CORS_FEATURE true 79 | 80 | ///////////////////////////////////////////// 81 | 82 | // Force some params 83 | #define TIMEOUT_RECONNECT_WIFI 10000L 84 | 85 | // Permit running CONFIG_TIMEOUT_RETRYTIMES_BEFORE_RESET times before reset hardware 86 | // to permit user another chance to config. Only if Config Data is valid. 87 | // If Config Data is invalid, this has no effect as Config Portal will persist 88 | #define RESET_IF_CONFIG_TIMEOUT true 89 | 90 | // Permitted range of user-defined CONFIG_TIMEOUT_RETRYTIMES_BEFORE_RESET between 2-100 91 | #define CONFIG_TIMEOUT_RETRYTIMES_BEFORE_RESET 5 92 | 93 | // Config Timeout 120s (default 60s). Applicable only if Config Data is Valid 94 | #define CONFIG_TIMEOUT 120000L 95 | 96 | ///////////////////////////////////////////// 97 | 98 | // Permit input only one set of WiFi SSID/PWD. The other can be "NULL or "blank" 99 | // Default is false (if not defined) => must input 2 sets of SSID/PWD 100 | #define REQUIRE_ONE_SET_SSID_PW true //false 101 | 102 | // Max times to try WiFi per loop() iteration. To avoid blocking issue in loop() 103 | // Default 1 if not defined, and minimum 1. 104 | #define MAX_NUM_WIFI_RECON_TRIES_PER_LOOP 2 105 | 106 | // Default no interval between recon WiFi if lost 107 | // Max permitted interval will be 10mins 108 | // Uncomment to use. Be careful, WiFi reconnect will be delayed if using this method 109 | // Only use whenever urgent tasks in loop() can't be delayed. But if so, it's better you have to rewrite your code, e.g. using higher priority tasks. 110 | #define WIFI_RECON_INTERVAL 30000 111 | 112 | ///////////////////////////////////////////// 113 | 114 | // Permit reset hardware if no WiFi to permit user another chance to access Config Portal. 115 | #define RESET_IF_NO_WIFI false 116 | 117 | ///////////////////////////////////////////// 118 | 119 | #define USE_DYNAMIC_PARAMETERS true 120 | 121 | ///////////////////////////////////////////// 122 | 123 | #define SCAN_WIFI_NETWORKS true 124 | 125 | // To be able to manually input SSID, not from a scanned SSID lists 126 | #define MANUAL_SSID_INPUT_ALLOWED true 127 | 128 | // From 2-15 129 | #define MAX_SSID_IN_LIST 8 130 | 131 | ///////////////////////////////////////////// 132 | 133 | // Optional, to use Board Name in Menu 134 | #define USING_BOARD_NAME true 135 | 136 | ///////////////////////////////////////////// 137 | 138 | #include 139 | 140 | #if ESP8266 141 | #define HOST_NAME "ESP8266Async-Control" 142 | #else 143 | #define HOST_NAME "ESP32Async-Control" 144 | #endif 145 | 146 | #ifdef LED_BUILTIN 147 | #define LED_PIN LED_BUILTIN 148 | #else 149 | #define LED_PIN 13 150 | #endif 151 | 152 | #endif //defines_h 153 | -------------------------------------------------------------------------------- /src/ESPAsync_WiFiManager_Lite_Debug.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ESPAsync_WiFiManager_Lite_Debug.h 3 | For ESP8266 / ESP32 boards 4 | 5 | ESPAsync_WiFiManager_Lite (https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite) is a library 6 | for the ESP32/ESP8266 boards to enable store Credentials in EEPROM/SPIFFS/LittleFS for easy 7 | configuration/reconfiguration and autoconnect/autoreconnect of WiFi and other services without Hardcoding. 8 | 9 | Built by Khoi Hoang https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite 10 | Licensed under MIT license 11 | 12 | Version: 1.10.5 13 | 14 | Version Modified By Date Comments 15 | ------- ----------- ---------- ----------- 16 | 1.0.0 K Hoang 09/02/2021 Initial coding for ESP32/ESP8266 17 | 1.1.0 K Hoang 12/02/2021 Add support to new ESP32-S2 18 | 1.2.0 K Hoang 22/02/2021 Add customs HTML header feature. Fix bug. 19 | 1.3.0 K Hoang 12/04/2021 Fix invalid "blank" Config Data treated as Valid. Fix EEPROM_SIZE bug 20 | 1.4.0 K Hoang 21/04/2021 Add support to new ESP32-C3 using SPIFFS or EEPROM 21 | 1.5.0 Michael H 24/04/2021 Enable scan of WiFi networks for selection in Configuration Portal 22 | 1.5.1 K Hoang 10/10/2021 Update `platform.ini` and `library.json` 23 | 1.6.0 K Hoang 26/11/2021 Auto detect ESP32 core and use either built-in LittleFS or LITTLEFS library. Fix bug. 24 | 1.7.0 K Hoang 09/01/2022 Fix the blocking issue in loop() with configurable WIFI_RECON_INTERVAL 25 | 1.8.0 K Hoang 10/02/2022 Add support to new ESP32-S3 26 | 1.8.1 K Hoang 11/02/2022 Add LittleFS support to ESP32-C3. Use core LittleFS instead of Lorol's LITTLEFS for v2.0.0+ 27 | 1.8.2 K Hoang 21/02/2022 Optional Board_Name in Menu. Optimize code by using passing by reference 28 | 1.9.0 K Hoang 09/09/2022 Fix ESP32 chipID and add getChipOUI() 29 | 1.9.1 K Hoang 28/12/2022 Add Captive Portal using AsyncDNSServer 30 | 1.10.1 K Hoang 12/01/2023 Added public methods to load and save dynamic data. Bump up to v1.10.1 31 | 1.10.2 K Hoang 15/01/2023 Add Config Portal scaling support to mobile devices 32 | 1.10.3 K Hoang 19/01/2023 Fix compiler error if EEPROM is used 33 | 1.10.5 K Hoang 29/01/2023 Using PROGMEM for strings. Sync with ESP_WiFiManager_Lite v1.10.5 34 | *****************************************************************************************************************************/ 35 | 36 | #ifndef ESPAsync_WiFiManager_Lite_Debug_h 37 | #define ESPAsync_WiFiManager_Lite_Debug_h 38 | 39 | #ifdef ESP_WM_LITE_DEBUG_OUTPUT 40 | #define DBG_PORT_ESP_WML ESP_WM_LITE_DEBUG_OUTPUT 41 | #else 42 | #define DBG_PORT_ESP_WML Serial 43 | #endif 44 | 45 | // Change _ESP_WM_LITE_LOGLEVEL_ to set tracing and logging verbosity 46 | // 0: DISABLED: no logging 47 | // 1: ERROR: errors 48 | // 2: WARN: errors and warnings 49 | // 3: INFO: errors, warnings and informational (default) 50 | // 4: DEBUG: errors, warnings, informational and debug 51 | 52 | #ifndef _ESP_WM_LITE_LOGLEVEL_ 53 | #define _ESP_WM_LITE_LOGLEVEL_ 0 54 | #endif 55 | 56 | const char ESP_WML_MARK[] = "[WML] "; 57 | 58 | #define ESP_WML_PRINT_MARK DBG_PORT_ESP_WML.print(ESP_WML_MARK) 59 | 60 | #define ESP_WML_PRINT DBG_PORT_ESP_WML.print 61 | #define ESP_WML_PRINTLN DBG_PORT_ESP_WML.println 62 | 63 | 64 | /////////////////////////////////////////// 65 | 66 | #define ESP_WML_LOGERROR0(x) if(_ESP_WM_LITE_LOGLEVEL_>0) { ESP_WML_PRINT(x); } 67 | #define ESP_WML_LOGERROR(x) if(_ESP_WM_LITE_LOGLEVEL_>0) { ESP_WML_PRINT_MARK; ESP_WML_PRINTLN(x); } 68 | #define ESP_WML_LOGERROR1(x,y) if(_ESP_WM_LITE_LOGLEVEL_>0) { ESP_WML_PRINT_MARK; ESP_WML_PRINT(x); ESP_WML_PRINTLN(y); } 69 | #define ESP_WML_LOGERROR2(x,y,z) if(_ESP_WM_LITE_LOGLEVEL_>0) { ESP_WML_PRINT_MARK; ESP_WML_PRINT(x); ESP_WML_PRINT(y); ESP_WML_PRINTLN(z); } 70 | #define ESP_WML_LOGERROR3(x,y,z,w) if(_ESP_WM_LITE_LOGLEVEL_>0) { ESP_WML_PRINT_MARK; ESP_WML_PRINT(x); ESP_WML_PRINT(y); ESP_WML_PRINT(z); ESP_WML_PRINTLN(w); } 71 | #define ESP_WML_LOGERROR5(x,y,z,w,xx,yy) if(_ESP_WM_LITE_LOGLEVEL_>0) { ESP_WML_PRINT_MARK; ESP_WML_PRINT(x); ESP_WML_PRINT(y); ESP_WML_PRINT(z); ESP_WML_PRINT(w); ESP_WML_PRINT(xx); ESP_WML_PRINTLN(yy); } 72 | 73 | /////////////////////////////////////////// 74 | 75 | #define ESP_WML_LOGWARN0(x) if(_ESP_WM_LITE_LOGLEVEL_>1) { ESP_WML_PRINT(x); } 76 | #define ESP_WML_LOGWARN(x) if(_ESP_WM_LITE_LOGLEVEL_>1) { ESP_WML_PRINT_MARK; ESP_WML_PRINTLN(x); } 77 | #define ESP_WML_LOGWARN1(x,y) if(_ESP_WM_LITE_LOGLEVEL_>1) { ESP_WML_PRINT_MARK; ESP_WML_PRINT(x); ESP_WML_PRINTLN(y); } 78 | #define ESP_WML_LOGWARN2(x,y,z) if(_ESP_WM_LITE_LOGLEVEL_>1) { ESP_WML_PRINT_MARK; ESP_WML_PRINT(x); ESP_WML_PRINT(y); ESP_WML_PRINTLN(z); } 79 | #define ESP_WML_LOGWARN3(x,y,z,w) if(_ESP_WM_LITE_LOGLEVEL_>1) { ESP_WML_PRINT_MARK; ESP_WML_PRINT(x); ESP_WML_PRINT(y); ESP_WML_PRINT(z); ESP_WML_PRINTLN(w); } 80 | #define ESP_WML_LOGWARN5(x,y,z,w,xx,yy) if(_ESP_WM_LITE_LOGLEVEL_>1) { ESP_WML_PRINT_MARK; ESP_WML_PRINT(x); ESP_WML_PRINT(y); ESP_WML_PRINT(z); ESP_WML_PRINT(w); ESP_WML_PRINT(xx); ESP_WML_PRINTLN(yy); } 81 | 82 | /////////////////////////////////////////// 83 | 84 | #define ESP_WML_LOGINFO0(x) if(_ESP_WM_LITE_LOGLEVEL_>2) { ESP_WML_PRINT(x); } 85 | #define ESP_WML_LOGINFO(x) if(_ESP_WM_LITE_LOGLEVEL_>2) { ESP_WML_PRINT_MARK; ESP_WML_PRINTLN(x); } 86 | #define ESP_WML_LOGINFO1(x,y) if(_ESP_WM_LITE_LOGLEVEL_>2) { ESP_WML_PRINT_MARK; ESP_WML_PRINT(x); ESP_WML_PRINTLN(y); } 87 | #define ESP_WML_LOGINFO2(x,y,z) if(_ESP_WM_LITE_LOGLEVEL_>3) { ESP_WML_PRINT_MARK; ESP_WML_PRINT(x); ESP_WML_PRINT(y); ESP_WML_PRINTLN(z); } 88 | #define ESP_WML_LOGINFO3(x,y,z,w) if(_ESP_WM_LITE_LOGLEVEL_>3) { ESP_WML_PRINT_MARK; ESP_WML_PRINT(x); ESP_WML_PRINT(y); ESP_WML_PRINT(z); ESP_WML_PRINTLN(w); } 89 | #define ESP_WML_LOGINFO5(x,y,z,w,xx,yy) if(_ESP_WM_LITE_LOGLEVEL_>2) { ESP_WML_PRINT_MARK; ESP_WML_PRINT(x); ESP_WML_PRINT(y); ESP_WML_PRINT(z); ESP_WML_PRINT(w); ESP_WML_PRINT(xx); ESP_WML_PRINTLN(yy); } 90 | 91 | /////////////////////////////////////////// 92 | 93 | #define ESP_WML_LOGDEBUG0(x) if(_ESP_WM_LITE_LOGLEVEL_>3) { ESP_WML_PRINT(x); } 94 | #define ESP_WML_LOGDEBUG(x) if(_ESP_WM_LITE_LOGLEVEL_>3) { ESP_WML_PRINT_MARK; ESP_WML_PRINTLN(x); } 95 | #define ESP_WML_LOGDEBUG1(x,y) if(_ESP_WM_LITE_LOGLEVEL_>3) { ESP_WML_PRINT_MARK; ESP_WML_PRINT(x); ESP_WML_PRINTLN(y); } 96 | #define ESP_WML_LOGDEBUG2(x,y,z) if(_ESP_WM_LITE_LOGLEVEL_>3) { ESP_WML_PRINT_MARK; ESP_WML_PRINT(x); ESP_WML_PRINT(y); ESP_WML_PRINTLN(z); } 97 | #define ESP_WML_LOGDEBUG3(x,y,z,w) if(_ESP_WM_LITE_LOGLEVEL_>3) { ESP_WML_PRINT_MARK; ESP_WML_PRINT(x); ESP_WML_PRINT(y); ESP_WML_PRINT(z); ESP_WML_PRINTLN(w); } 98 | #define ESP_WML_LOGDEBUG5(x,y,z,w,xx,yy) if(_ESP_WM_LITE_LOGLEVEL_>3) { ESP_WML_PRINT_MARK; ESP_WML_PRINT(x); ESP_WML_PRINT(y); ESP_WML_PRINT(z); ESP_WML_PRINT(w); ESP_WML_PRINT(xx); ESP_WML_PRINTLN(yy); } 99 | 100 | /////////////////////////////////////////// 101 | 102 | #endif //ESPAsync_WiFiManager_Lite_Debug_h 103 | -------------------------------------------------------------------------------- /platformio/platformio.ini: -------------------------------------------------------------------------------- 1 | ;PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [platformio] 12 | ; ============================================================ 13 | ; chose environment: 14 | ; ESP8266 15 | ; ESP32 16 | ; esp32s2 17 | ; esp32s3 18 | ; esp32c3 19 | 20 | ; ============================================================ 21 | default_envs = ESP8266 22 | ;default_envs = ESP32 23 | 24 | [env] 25 | ; ============================================================ 26 | ; Serial configuration 27 | ; choose upload speed, serial-monitor speed 28 | ; ============================================================ 29 | upload_speed = 921600 30 | ;upload_port = COM11 31 | ;monitor_speed = 9600 32 | ;monitor_port = COM11 33 | 34 | ; ============================================================ 35 | ; Checks for the compatibility with frameworks and dev/platforms 36 | lib_compat_mode = strict 37 | ;lib_ldf_mode = chain+ 38 | ;lib_ldf_mode = deep+ 39 | 40 | ; ============================================================ 41 | lib_deps = 42 | ; PlatformIO 4.x 43 | ; ESP Async WebServer@>=1.2.3 44 | ; https://github.com/khoih-prog/ESPAsyncWebServer 45 | ; ESPAsyncTCP@>=1.2.2 46 | ; https://github.com/khoih-prog/ESPAsyncTCP 47 | ; AsyncTCP@>=1.1.1 48 | ; ESP_DoubleResetDetector@>=1.3.2 49 | ; ESP_MultiResetDetector@>=1.3.2 50 | ; ESPAsyncDNSServer@>=1.0.0 51 | ; https://github.com/khoih-prog/ESPAsyncDNSServer 52 | ; LittleFS_esp32@>=1.0.6 53 | ; PlatformIO 5.x 54 | ; me-no-dev/ESP Async WebServer@>=1.2.3 55 | https://github.com/khoih-prog/ESPAsyncWebServer 56 | ; me-no-dev/ESPAsyncTCP@>=1.2.2 57 | https://github.com/khoih-prog/ESPAsyncTCP 58 | me-no-dev/AsyncTCP@>=1.1.1 59 | khoih-prog/ESP_DoubleResetDetector@>=1.3.2 60 | khoih-prog/ESP_MultiResetDetector@>=1.3.2 61 | ; devyte/ESPAsyncDNSServer@>=1.0.0 62 | https://github.com/khoih-prog/ESPAsyncDNSServer 63 | ; lorol/LittleFS_esp32@>=1.0.6 64 | 65 | ; ============================================================ 66 | build_flags = 67 | ; set your debug output (default=Serial) 68 | -D DEBUG_ESP_PORT=Serial 69 | ; comment the following line to enable WiFi debugging 70 | -D NDEBUG 71 | 72 | ; comment the following line to use EEPROM 73 | -D USE_LITTLEFS=true 74 | 75 | ; ============================================================ 76 | ; ============================================================ 77 | [env:ESP8266] 78 | platform = espressif8266 79 | framework = arduino 80 | ; ============================================================ 81 | ; Board configuration 82 | ; choose your board by uncommenting one of the following lines 83 | ; ============================================================ 84 | ;board = gen4iod 85 | ;board = huzzah 86 | ;board = oak 87 | ;board = esp_wroom_02 88 | ;board = espduino 89 | ;board = espectro 90 | ;board = espino 91 | ;board = espresso_lite_v1 92 | ;board = espresso_lite_v2 93 | ;board = esp12e 94 | ;board = esp01_1m 95 | ;board = esp01 96 | ;board = esp07 97 | ;board = esp8285 98 | ;board = heltec_wifi_kit_8 99 | ;board = inventone 100 | ;board = nodemcu 101 | board = nodemcuv2 102 | ;board = modwifi 103 | ;board = phoenix_v1 104 | ;board = phoenix_v2 105 | ;board = sparkfunBlynk 106 | ;board = thing 107 | ;board = thingdev 108 | ;board = esp210 109 | ;board = espinotee 110 | ;board = d1 111 | ;board = d1_mini 112 | ;board = d1_mini_lite 113 | ;board = d1_mini_pro 114 | ;board = wifi_slot 115 | ;board = wifiduino 116 | ;board = wifinfo 117 | ;board = wio_link 118 | ;board = wio_node 119 | ;board = xinabox_cw01 120 | ;board = esp32doit-devkit-v1 121 | 122 | ; ============================================================ 123 | ; ============================================================ 124 | [env:ESP32] 125 | platform = espressif32 126 | framework = arduino 127 | 128 | ; ============================================================ 129 | ; Board configuration 130 | ; choose your board by uncommenting one of the following lines 131 | ; ============================================================ 132 | ;board = esp32cam 133 | ;board = alksesp32 134 | ;board = featheresp32 135 | ;board = espea32 136 | ;board = bpi-bit 137 | ;board = d-duino-32 138 | board = esp32doit-devkit-v1 139 | ;board = pocket_32 140 | ;board = fm-devkit 141 | ;board = pico32 142 | ;board = esp32-evb 143 | ;board = esp32-gateway 144 | ;board = esp32-pro 145 | ;board = esp32-poe 146 | ;board = oroca_edubot 147 | ;board = onehorse32dev 148 | ;board = lopy 149 | ;board = lopy4 150 | ;board = wesp32 151 | ;board = esp32thing 152 | ;board = sparkfun_lora_gateway_1-channel 153 | ;board = ttgo-lora32-v1 154 | ;board = ttgo-t-beam 155 | ;board = turta_iot_node 156 | ;board = lolin_d32 157 | ;board = lolin_d32_pro 158 | ;board = lolin32 159 | ;board = wemosbat 160 | ;board = widora-air 161 | ;board = xinabox_cw02 162 | ;board = iotbusio 163 | ;board = iotbusproteus 164 | ;board = nina_w10 165 | 166 | ; ============================================================ 167 | ; ============================================================ 168 | [env:esp32s2] 169 | platform = espressif32 170 | framework = arduino 171 | 172 | ; toolchain download links see 173 | ; refer "name": "xtensa-esp32s2-elf-gcc","version": "gcc8_4_0-esp-2021r1" section of 174 | ; https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json 175 | ; e.g. Windows: https://github.com/espressif/crosstool-NG/releases/download/esp-2021r1/xtensa-esp32s2-elf-gcc8_4_0-esp-2021r1-win32.zip 176 | platform_packages = 177 | toolchain-xtensa32s2@file://C:\Users\Max\Downloads\xtensa-esp32s2-elf 178 | framework-arduinoespressif32@https://github.com/espressif/arduino-esp32.git#a4118ea88987c28aac3a49bcb9cc5d6c0acc6f3f 179 | platformio/tool-esptoolpy @ ~1.30100 180 | board = esp32dev 181 | board_build.mcu = esp32s2 182 | board_build.partitions = huge_app.csv 183 | board_build.variant = esp32s2 184 | board_build.f_cpu = 240000000L 185 | board_build.f_flash = 80000000L 186 | board_build.flash_mode = qio 187 | board_build.arduino.ldscript = esp32s2_out.ld 188 | build_unflags = 189 | -DARDUINO_ESP32_DEV 190 | -DARDUINO_VARIANT="esp32" 191 | build_flags = 192 | -DARDUINO_ESP32S2_DEV 193 | -DARDUINO_VARIANT="esp32s2" 194 | 195 | ; ============================================================ 196 | ; ============================================================ 197 | [env:esp32s3] 198 | platform = espressif32 199 | framework = arduino 200 | 201 | board = esp32-s3-devkitc-1 202 | board_build.mcu = esp32s3 203 | board_build.partitions = huge_app.csv 204 | board_build.variant = esp32s3 205 | board_build.f_cpu = 240000000L 206 | board_build.f_flash = 80000000L 207 | board_build.flash_mode = qio 208 | board_build.arduino.ldscript = esp32s3_out.ld 209 | build_unflags = 210 | -DARDUINO_ESP32_DEV 211 | -DARDUINO_VARIANT="esp32" 212 | build_flags = 213 | -DARDUINO_ESP32S3_DEV 214 | -DARDUINO_VARIANT="esp32s3" 215 | 216 | ; ============================================================ 217 | ; ============================================================ 218 | [env:esp32sc3] 219 | platform = espressif32 220 | framework = arduino 221 | 222 | board = esp32-c3-devkitm-1 223 | board_build.mcu = esp32c3 224 | board_build.partitions = huge_app.csv 225 | board_build.variant = esp32c3 226 | board_build.f_cpu = 160000000L 227 | board_build.f_flash = 80000000L 228 | board_build.flash_mode = qio 229 | board_build.arduino.ldscript = esp32c3_out.ld 230 | build_unflags = 231 | -DARDUINO_ESP32_DEV 232 | -DARDUINO_VARIANT="esp32" 233 | build_flags = 234 | -DARDUINO_ESP32S3_DEV 235 | -DARDUINO_VARIANT="esp32c3" 236 | 237 | ; ============================================================ 238 | ; ============================================================ 239 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | ## ESPAsync_WiFiManager_Lite Library (Light Weight Credentials / WiFiManager for ESP32/ESP8266) 2 | 3 | [![arduino-library-badge](https://www.ardu-badge.com/badge/ESPAsync_WiFiManager_Lite.svg?)](https://www.ardu-badge.com/ESPAsync_WiFiManager_Lite) 4 | [![GitHub release](https://img.shields.io/github/release/khoih-prog/ESPAsync_WiFiManager_Lite.svg)](https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite/releases) 5 | [![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite/blob/main/LICENSE) 6 | [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](#Contributing) 7 | [![GitHub issues](https://img.shields.io/github/issues/khoih-prog/ESPAsync_WiFiManager_Lite.svg)](http://github.com/khoih-prog/ESPAsync_WiFiManager_Lite/issues) 8 | 9 | 10 | Donate to my libraries using BuyMeACoffee 11 | 12 | 13 | --- 14 | --- 15 | 16 | ## Table of Contents 17 | 18 | * [Changelog](#changelog) 19 | * [Release v1.10.5](#release-v1105) 20 | * [Release v1.10.3](#release-v1103) 21 | * [Release v1.10.2](#release-v1102) 22 | * [Release v1.10.1](#release-v1101) 23 | * [Release v1.9.1](#release-v191) 24 | * [Release v1.9.0](#release-v190) 25 | * [Release v1.8.2](#release-v182) 26 | * [Release v1.8.1](#release-v181) 27 | * [Release v1.8.0](#release-v180) 28 | * [Major Release v1.7.0](#major-release-v170) 29 | * [Release v1.6.0](#release-v160) 30 | * [Release v1.5.1](#release-v151) 31 | * [Major Release v1.5.0](#major-release-v150) 32 | * [Release v1.4.0](#release-v140) 33 | * [Release v1.3.0](#release-v130) 34 | * [Release v1.2.0](#release-v120) 35 | * [Release v1.1.0](#release-v110) 36 | * [Release v1.0.0](#release-v100) 37 | 38 | --- 39 | --- 40 | 41 | 42 | ## Changelog 43 | 44 | ### Release v1.10.5 45 | 46 | 1. Using PROGMEM for strings in examples. Check [Example fixes #32](https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite/pull/32) 47 | 2. Using PROGMEM for HTML strings. Check [implement PROGMEM strings, alignment with ESP_WiFiManager_Lite #31](https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite/pull/31) 48 | 3. Default LittleFS for ESP8266 in examples 49 | 4. Improve `README.md` so that links can be used in other sites, such as `PIO` 50 | 5. Sync with `ESP_WiFiManager_Lite` v1.10.5 51 | 52 | ### Release v1.10.3 53 | 54 | 1. Fix compiler error if EEPROM is used. Check [fix compiler error if EEPROM is used #29](https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite/pull/29) 55 | 2. Fix compiler error in PIO. Check [Unable to compile basic example for NodeMCU-32s #28](https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite/issues/28) 56 | 57 | ### Release v1.10.2 58 | 59 | 1. Add Config Portal scaling support to mobile devices. Check [Mobile scale #26](https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite/pull/26) 60 | 61 | ### Release v1.10.1 62 | 63 | 1. Add public methods to load and save dynamic data. Check [added public methods to load and save dynamic data #24](https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite/pull/24) 64 | 2. Bump up to `v1.10.1` to sync with [ESP_WiFiManager_Lite](https://github.com/khoih-prog/ESP_WiFiManager_Lite) 65 | 66 | ### Release v1.9.1 67 | 68 | 1. Add `Captive Portal` using AsyncDNSServer. Check [Captive portal #22](https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite/pull/22) 69 | 2. Cleanup reset code. Check [cleanup reset code #23](https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite/pull/23) 70 | 3. Periodical code clean-up 71 | 72 | ### Release v1.9.0 73 | 74 | 1. Fix ESP32 chipID. Check [Help for storing variables in memory (non-volatile) #87](https://github.com/khoih-prog/ESP_WiFiManager/discussions/87#discussioncomment-3593028) 75 | 2. Add ESP32 getChipID() and getChipOUI() functions 76 | 3. Remove dependency on `LittleFS_esp32` library to prevent PIO error when using new ESP32 core v1.0.6+ 77 | 4. Remove unavailable items from depends field of `library.properties`, such as `ESP Async WebServer`,`ESP AsyncTCP` and `AsyncTCP`. Check 78 | - [**Arduino Library Registry** #147](https://github.com/me-no-dev/AsyncTCP/issues/147) 79 | - [**Arduino library manager** #139](https://github.com/me-no-dev/ESPAsyncTCP/issues/139) 80 | - [**Available via Arduino Library Manager?** #158](https://github.com/me-no-dev/ESPAsyncTCP/issues/158) 81 | - [**Add ESPAsyncTCP to the Arduino Library Manager** #168](https://github.com/me-no-dev/ESPAsyncTCP/issues/168) 82 | 83 | 84 | ### Release v1.8.2 85 | 86 | 1. Optimize code by using passing by `reference` instead of by `value` 87 | 2. Optional `Board_Name` in Menu. Check [option to remove board name from web page #25](https://github.com/khoih-prog/WiFiManager_NINA_Lite/issues/25) 88 | 3. Add function `isConfigMode()` to signal system is in Config Portal mode. 89 | 90 | ### Release v1.8.1 91 | 92 | 1. Add LittleFS support to `ESP32-C3`. 93 | 2. Use `ESP32-core's LittleFS` library instead of `Lorol's LITTLEFS` library for ESP32 core v2.0.0+ 94 | 95 | ### Release v1.8.0 96 | 97 | 1. Add support to `ESP32-S3` (`ESP32S3_DEV, ESP32_S3_BOX, UM TINYS3, UM PROS3, UM FEATHERS3`, etc.) using [ESP32 core, esp32-s3-support branch, v2.0.2+](https://github.com/espressif/arduino-esp32/tree/esp32-s3-support) 98 | 99 | ### Major Release v1.7.0 100 | 101 | 1. Fix ESP8266 bug not easy to connect to Config Portal for ESP8266 core v3.0.0+ 102 | 2. Fix the blocking issue in loop(). Check [retries block the main loop #18](https://github.com/khoih-prog/WiFiManager_NINA_Lite/issues/18) 103 | 3. Configurable `WIFI_RECON_INTERVAL`. Check [retries block the main loop #18](https://github.com/khoih-prog/WiFiManager_NINA_Lite/issues/18#issuecomment-1006197561) 104 | 4. Optimize library code by using `reference-passing` instead of `value-passing` 105 | 5. Clean up 106 | 107 | ### Release v1.6.0 108 | 109 | 1. Auto detect ESP32 core and use either built-in LittleFS or [LITTLEFS](https://github.com/lorol/LITTLEFS) library. 110 | 2. Fix bug returning IP `255.255.255.255` in core v2.0.0+ when using `hostname` 111 | 112 | ### Release v1.5.1 113 | 114 | 1. Update `platform.ini` and `library.json` to use original `khoih-prog` instead of `khoih.prog` after PIO fix 115 | 116 | ### Major Release v1.5.0 117 | 118 | 1. Enable scan of WiFi networks for selection in Configuration Portal. Check [PR for v1.3.0 - Enable scan of WiFi networks #10](https://github.com/khoih-prog/WiFiManager_NINA_Lite/pull/10). Now you can select optional **SCAN_WIFI_NETWORKS**, **MANUAL_SSID_INPUT_ALLOWED** to be able to manually input SSID, not only from a scanned SSID lists and **MAX_SSID_IN_LIST** (from 2-15) 119 | 2. Minor enhancement to not display garbage when data is invalid 120 | 121 | ### Release v1.4.0 122 | 123 | 1. Add **LittleFS and SPIFFS** support to new **ESP32-S2** boards (**Arduino ESP32C3_DEV**). Check [HOWTO Install esp32 core for ESP32-S2 (Saola, AI-Thinker ESP-12K) and ESP32-C3 boards into Arduino IDE](#howto-install-esp32-core-for-esp32-s2-saola-ai-thinker-esp-12k-and-esp32-c3-boards-into-arduino-ide). 124 | 2. Add **EEPROM and SPIFFS** support to new **ESP32-C3** boards (**Arduino ESP32C3_DEV**). Check [HOWTO Install esp32 core for ESP32-S2 (Saola, AI-Thinker ESP-12K) and ESP32-C3 boards into Arduino IDE](#howto-install-esp32-core-for-esp32-s2-saola-ai-thinker-esp-12k-and-esp32-c3-boards-into-arduino-ide). 125 | 126 | ### Release v1.3.0 127 | 128 | 1. Fix invalid "blank" Config Data treated as Valid. 129 | 2. Permit optionally inputting one set of WiFi SSID/PWD by using `REQUIRE_ONE_SET_SSID_PW == true` 130 | 3. Enforce WiFi PWD minimum length of 8 chars 131 | 4. Fix bug where EEPROM_SIZE truncated by DRD/MRD to 512, resulting lost and corrupted data. 132 | 133 | ### Release v1.2.0 134 | 135 | 1. Configurable **Customs HTML Headers**, including Customs Style, Customs Head Elements, CORS Header. 136 | 2. Fix Config Portal Bug. 137 | 3. Update examples 138 | 139 | ### Release v1.1.0 140 | 141 | 1. Add support to **ESP32-S2 (ESP32-S2 Saola and AI-Thinker ESP-12K)** 142 | 2. Add [**Instructions to install ESP32-S2 core**](#howto-install-esp32-s2-core-for-esp32-s2-saola-ai-thinker-esp-12k-boards-into-arduino-ide) 143 | 144 | ### Release v1.0.0 145 | 146 | 1. Initial release to support ESP32 and ESP8266 to use the better **asynchronous** [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer). 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /esp32c3_ESPAsyncWebServer_Patch/WebAuthentication.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Asynchronous WebServer library for Espressif MCUs 3 | 4 | Copyright (c) 2016 Hristo Gochkov. All rights reserved. 5 | This file is part of the esp8266 core for Arduino environment. 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library 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 GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | #include "WebAuthentication.h" 22 | #include 23 | #ifdef ESP32 24 | #include "mbedtls/md5.h" 25 | #include "mbedtls/version.h" 26 | #else 27 | #include "md5.h" 28 | #endif 29 | 30 | 31 | // Basic Auth hash = base64("username:password") 32 | 33 | bool checkBasicAuthentication(const char * hash, const char * username, const char * password) 34 | { 35 | if (username == NULL || password == NULL || hash == NULL) 36 | return false; 37 | 38 | size_t toencodeLen = strlen(username) + strlen(password) + 1; 39 | size_t encodedLen = base64_encode_expected_len(toencodeLen); 40 | 41 | if (strlen(hash) != encodedLen) 42 | return false; 43 | 44 | char *toencode = new char[toencodeLen + 1]; 45 | 46 | if (toencode == NULL) 47 | { 48 | return false; 49 | } 50 | 51 | char *encoded = new char[base64_encode_expected_len(toencodeLen) + 1]; 52 | 53 | if (encoded == NULL) 54 | { 55 | delete[] toencode; 56 | return false; 57 | } 58 | 59 | sprintf(toencode, "%s:%s", username, password); 60 | 61 | if (base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0) 62 | { 63 | delete[] toencode; 64 | delete[] encoded; 65 | return true; 66 | } 67 | 68 | delete[] toencode; 69 | delete[] encoded; 70 | return false; 71 | } 72 | 73 | static bool getMD5(uint8_t * data, uint16_t len, char * output) //33 bytes or more 74 | { 75 | #ifdef ESP32 76 | mbedtls_md5_context _ctx; 77 | #else 78 | md5_context_t _ctx; 79 | #endif 80 | uint8_t i; 81 | uint8_t * _buf = (uint8_t*)malloc(16); 82 | 83 | if (_buf == NULL) 84 | return false; 85 | 86 | memset(_buf, 0x00, 16); 87 | #ifdef ESP32 88 | mbedtls_md5_init(&_ctx); 89 | 90 | // KH 91 | #if (MBEDTLS_VERSION_NUMBER < 0x02070000) 92 | #warning MBEDTLS_VERSION_NUMBER < 2.7.0 93 | // Superseded from v2.7.0 94 | mbedtls_md5_starts(&_ctx); 95 | mbedtls_md5_update(&_ctx, data, len); 96 | mbedtls_md5_finish(&_ctx, _buf); 97 | #else 98 | #warning MBEDTLS_VERSION_NUMBER >= 2.7.0 99 | mbedtls_md5_starts_ret(&_ctx); 100 | mbedtls_md5_update_ret(&_ctx, data, len); 101 | mbedtls_md5_finish_ret(&_ctx, _buf); 102 | #endif 103 | ////// 104 | 105 | #else 106 | MD5Init(&_ctx); 107 | MD5Update(&_ctx, data, len); 108 | MD5Final(_buf, &_ctx); 109 | #endif 110 | 111 | for (i = 0; i < 16; i++) 112 | { 113 | sprintf(output + (i * 2), "%02x", _buf[i]); 114 | } 115 | 116 | free(_buf); 117 | return true; 118 | } 119 | 120 | static String genRandomMD5() 121 | { 122 | #ifdef ESP8266 123 | uint32_t r = RANDOM_REG32; 124 | #else 125 | uint32_t r = rand(); 126 | #endif 127 | char * out = (char*)malloc(33); 128 | 129 | if (out == NULL || !getMD5((uint8_t*)(&r), 4, out)) 130 | return ""; 131 | 132 | String res = String(out); 133 | free(out); 134 | return res; 135 | } 136 | 137 | static String stringMD5(const String& in) 138 | { 139 | char * out = (char*)malloc(33); 140 | 141 | if (out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out)) 142 | return ""; 143 | 144 | String res = String(out); 145 | free(out); 146 | return res; 147 | } 148 | 149 | String generateDigestHash(const char * username, const char * password, const char * realm) 150 | { 151 | if (username == NULL || password == NULL || realm == NULL) 152 | { 153 | return ""; 154 | } 155 | 156 | char * out = (char*)malloc(33); 157 | String res = String(username); 158 | res.concat(":"); 159 | res.concat(realm); 160 | res.concat(":"); 161 | String in = res; 162 | in.concat(password); 163 | 164 | if (out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out)) 165 | return ""; 166 | 167 | res.concat(out); 168 | free(out); 169 | return res; 170 | } 171 | 172 | String requestDigestAuthentication(const char * realm) 173 | { 174 | String header = "realm=\""; 175 | 176 | if (realm == NULL) 177 | header.concat("asyncesp"); 178 | else 179 | header.concat(realm); 180 | 181 | header.concat( "\", qop=\"auth\", nonce=\""); 182 | header.concat(genRandomMD5()); 183 | header.concat("\", opaque=\""); 184 | header.concat(genRandomMD5()); 185 | header.concat("\""); 186 | return header; 187 | } 188 | 189 | bool checkDigestAuthentication(const char * header, const char * method, const char * username, const char * password, 190 | const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri) 191 | { 192 | if (username == NULL || password == NULL || header == NULL || method == NULL) 193 | { 194 | //os_printf("AUTH FAIL: missing required fields\n"); 195 | return false; 196 | } 197 | 198 | String myHeader = String(header); 199 | int nextBreak = myHeader.indexOf(","); 200 | 201 | if (nextBreak < 0) 202 | { 203 | //os_printf("AUTH FAIL: no variables\n"); 204 | return false; 205 | } 206 | 207 | String myUsername = String(); 208 | String myRealm = String(); 209 | String myNonce = String(); 210 | String myUri = String(); 211 | String myResponse = String(); 212 | String myQop = String(); 213 | String myNc = String(); 214 | String myCnonce = String(); 215 | 216 | myHeader += ", "; 217 | 218 | do 219 | { 220 | String avLine = myHeader.substring(0, nextBreak); 221 | avLine.trim(); 222 | myHeader = myHeader.substring(nextBreak + 1); 223 | nextBreak = myHeader.indexOf(","); 224 | 225 | int eqSign = avLine.indexOf("="); 226 | 227 | if (eqSign < 0) 228 | { 229 | //os_printf("AUTH FAIL: no = sign\n"); 230 | return false; 231 | } 232 | 233 | String varName = avLine.substring(0, eqSign); 234 | avLine = avLine.substring(eqSign + 1); 235 | 236 | if (avLine.startsWith("\"")) 237 | { 238 | avLine = avLine.substring(1, avLine.length() - 1); 239 | } 240 | 241 | if (varName.equals("username")) 242 | { 243 | if (!avLine.equals(username)) 244 | { 245 | //os_printf("AUTH FAIL: username\n"); 246 | return false; 247 | } 248 | 249 | myUsername = avLine; 250 | } 251 | else if (varName.equals("realm")) 252 | { 253 | if (realm != NULL && !avLine.equals(realm)) 254 | { 255 | //os_printf("AUTH FAIL: realm\n"); 256 | return false; 257 | } 258 | 259 | myRealm = avLine; 260 | } 261 | else if (varName.equals("nonce")) 262 | { 263 | if (nonce != NULL && !avLine.equals(nonce)) 264 | { 265 | //os_printf("AUTH FAIL: nonce\n"); 266 | return false; 267 | } 268 | 269 | myNonce = avLine; 270 | } 271 | else if (varName.equals("opaque")) 272 | { 273 | if (opaque != NULL && !avLine.equals(opaque)) 274 | { 275 | //os_printf("AUTH FAIL: opaque\n"); 276 | return false; 277 | } 278 | } 279 | else if (varName.equals("uri")) 280 | { 281 | if (uri != NULL && !avLine.equals(uri)) 282 | { 283 | //os_printf("AUTH FAIL: uri\n"); 284 | return false; 285 | } 286 | 287 | myUri = avLine; 288 | } 289 | else if (varName.equals("response")) 290 | { 291 | myResponse = avLine; 292 | } 293 | else if (varName.equals("qop")) 294 | { 295 | myQop = avLine; 296 | } 297 | else if (varName.equals("nc")) 298 | { 299 | myNc = avLine; 300 | } 301 | else if (varName.equals("cnonce")) 302 | { 303 | myCnonce = avLine; 304 | } 305 | } while (nextBreak > 0); 306 | 307 | String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ":" + myRealm + ":" + String(password)); 308 | String ha2 = String(method) + ":" + myUri; 309 | String response = ha1 + ":" + myNonce + ":" + myNc + ":" + myCnonce + ":" + myQop + ":" + stringMD5(ha2); 310 | 311 | if (myResponse.equals(stringMD5(response))) 312 | { 313 | //os_printf("AUTH SUCCESS\n"); 314 | return true; 315 | } 316 | 317 | //os_printf("AUTH FAIL: password\n"); 318 | return false; 319 | } 320 | -------------------------------------------------------------------------------- /esp32s2_WebServer_Patch/WebServer.h: -------------------------------------------------------------------------------- 1 | /* 2 | WebServer.h - Dead simple web-server. 3 | Supports only one simultaneous client, knows how to handle GET and POST. 4 | 5 | Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library 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 GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) 21 | */ 22 | 23 | 24 | #ifndef WEBSERVER_H 25 | #define WEBSERVER_H 26 | 27 | #include 28 | #include 29 | #include 30 | #include "HTTP_Method.h" 31 | #include "Uri.h" 32 | 33 | enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END, 34 | UPLOAD_FILE_ABORTED 35 | }; 36 | enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE }; 37 | enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH }; 38 | 39 | #define HTTP_DOWNLOAD_UNIT_SIZE 1436 40 | 41 | #ifndef HTTP_UPLOAD_BUFLEN 42 | #define HTTP_UPLOAD_BUFLEN 1436 43 | #endif 44 | 45 | #define HTTP_MAX_DATA_WAIT 5000 //ms to wait for the client to send the request 46 | #define HTTP_MAX_POST_WAIT 5000 //ms to wait for POST data to arrive 47 | #define HTTP_MAX_SEND_WAIT 5000 //ms to wait for data chunk to be ACKed 48 | #define HTTP_MAX_CLOSE_WAIT 2000 //ms to wait for the client to close the connection 49 | 50 | #define CONTENT_LENGTH_UNKNOWN ((size_t) -1) 51 | #define CONTENT_LENGTH_NOT_SET ((size_t) -2) 52 | 53 | class WebServer; 54 | 55 | typedef struct 56 | { 57 | HTTPUploadStatus status; 58 | String filename; 59 | String name; 60 | String type; 61 | size_t totalSize; // file size 62 | size_t currentSize; // size of data currently in buf 63 | uint8_t buf[HTTP_UPLOAD_BUFLEN]; 64 | } HTTPUpload; 65 | 66 | #include "detail/RequestHandler.h" 67 | 68 | namespace fs 69 | { 70 | class FS; 71 | } 72 | 73 | class WebServer 74 | { 75 | public: 76 | WebServer(IPAddress addr, int port = 80); 77 | WebServer(int port = 80); 78 | virtual ~WebServer(); 79 | 80 | virtual void begin(); 81 | virtual void begin(uint16_t port); 82 | virtual void handleClient(); 83 | 84 | virtual void close(); 85 | void stop(); 86 | 87 | bool authenticate(const char * username, const char * password); 88 | void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = NULL, 89 | const String& authFailMsg = String("") ); 90 | 91 | typedef std::function THandlerFunction; 92 | void on(const Uri &uri, THandlerFunction handler); 93 | void on(const Uri &uri, HTTPMethod method, THandlerFunction fn); 94 | void on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); 95 | void addHandler(RequestHandler* handler); 96 | void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL ); 97 | void onNotFound(THandlerFunction fn); //called when handler is not assigned 98 | void onFileUpload(THandlerFunction fn); //handle file uploads 99 | 100 | String uri() 101 | { 102 | return _currentUri; 103 | } 104 | HTTPMethod method() 105 | { 106 | return _currentMethod; 107 | } 108 | virtual WiFiClient client() 109 | { 110 | return _currentClient; 111 | } 112 | HTTPUpload& upload() 113 | { 114 | return *_currentUpload; 115 | } 116 | 117 | String pathArg(unsigned int i); // get request path argument by number 118 | String arg(String name); // get request argument value by name 119 | String arg(int i); // get request argument value by number 120 | String argName(int i); // get request argument name by number 121 | int args(); // get arguments count 122 | bool hasArg(String name); // check if argument exists 123 | void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); // set the request headers to collect 124 | String header(String name); // get request header value by name 125 | String header(int i); // get request header value by number 126 | String headerName(int i); // get request header name by number 127 | int headers(); // get header count 128 | bool hasHeader(String name); // check if header exists 129 | 130 | String hostHeader(); // get request host header if available or empty String if not 131 | 132 | // send response to the client 133 | // code - HTTP response code, can be 200 or 404 134 | // content_type - HTTP content type, like "text/plain" or "image/png" 135 | // content - actual content body 136 | void send(int code, const char* content_type = NULL, const String& content = String("")); 137 | void send(int code, char* content_type, const String& content); 138 | void send(int code, const String& content_type, const String& content); 139 | void send_P(int code, PGM_P content_type, PGM_P content); 140 | void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength); 141 | 142 | void enableDelay(boolean value); 143 | void enableCORS(boolean value = true); 144 | void enableCrossOrigin(boolean value = true); 145 | 146 | void setContentLength(const size_t contentLength); 147 | void sendHeader(const String& name, const String& value, bool first = false); 148 | void sendContent(const String& content); 149 | void sendContent(const char* content, size_t contentLength); 150 | void sendContent_P(PGM_P content); 151 | void sendContent_P(PGM_P content, size_t size); 152 | 153 | static String urlDecode(const String& text); 154 | 155 | template 156 | size_t streamFile(T &file, const String& contentType) 157 | { 158 | _streamFileCore(file.size(), file.name(), contentType); 159 | return _currentClient.write(file); 160 | } 161 | 162 | protected: 163 | virtual size_t _currentClientWrite(const char* b, size_t l) 164 | { 165 | return _currentClient.write( b, l ); 166 | } 167 | virtual size_t _currentClientWrite_P(PGM_P b, size_t l) 168 | { 169 | return _currentClient.write_P( b, l ); 170 | } 171 | void _addRequestHandler(RequestHandler* handler); 172 | void _handleRequest(); 173 | void _finalizeResponse(); 174 | bool _parseRequest(WiFiClient& client); 175 | void _parseArguments(String data); 176 | static String _responseCodeToString(int code); 177 | bool _parseForm(WiFiClient& client, String boundary, uint32_t len); 178 | bool _parseFormUploadAborted(); 179 | void _uploadWriteByte(uint8_t b); 180 | int _uploadReadByte(WiFiClient& client); 181 | void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength); 182 | bool _collectHeader(const char* headerName, const char* headerValue); 183 | 184 | void _streamFileCore(const size_t fileSize, const String & fileName, const String & contentType); 185 | 186 | String _getRandomHexString(); 187 | // for extracting Auth parameters 188 | String _extractParam(String& authReq, const String& param, const char delimit = '"'); 189 | 190 | struct RequestArgument 191 | { 192 | String key; 193 | String value; 194 | }; 195 | 196 | boolean _corsEnabled; 197 | WiFiServer _server; 198 | 199 | WiFiClient _currentClient; 200 | HTTPMethod _currentMethod; 201 | String _currentUri; 202 | uint8_t _currentVersion; 203 | HTTPClientStatus _currentStatus; 204 | unsigned long _statusChange; 205 | bool _nullDelay; 206 | 207 | RequestHandler* _currentHandler; 208 | RequestHandler* _firstHandler; 209 | RequestHandler* _lastHandler; 210 | THandlerFunction _notFoundHandler; 211 | THandlerFunction _fileUploadHandler; 212 | 213 | int _currentArgCount; 214 | RequestArgument* _currentArgs; 215 | int _postArgsLen; 216 | RequestArgument* _postArgs; 217 | 218 | std::unique_ptr _currentUpload; 219 | 220 | int _headerKeysCount; 221 | RequestArgument* _currentHeaders; 222 | size_t _contentLength; 223 | String _responseHeaders; 224 | 225 | String _hostHeader; 226 | bool _chunked; 227 | 228 | String _snonce; // Store noance and opaque for future comparison 229 | String _sopaque; 230 | String _srealm; // Store the Auth realm between Calls 231 | 232 | }; 233 | 234 | 235 | #endif //ESP8266WEBSERVER_H 236 | -------------------------------------------------------------------------------- /examples/ESPAsync_WiFi_MQTT/ESPAsync_WiFi_MQTT.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ESPAsync_WiFi_MQTT.ino 3 | For ESP8266 / ESP32 boards 4 | 5 | ESPAsync_WiFiManager_Lite (https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite) is a library 6 | for the ESP32/ESP8266 boards to enable store Credentials in EEPROM/SPIFFS/LittleFS for easy 7 | configuration/reconfiguration and autoconnect/autoreconnect of WiFi and other services without Hardcoding. 8 | 9 | Built by Khoi Hoang https://github.com/khoih-prog/ESPAsync_WiFiManager_Lite 10 | Licensed under MIT license 11 | *****************************************************************************************************************************/ 12 | 13 | /**************************************************************************************************************************** 14 | You have to modify file ./libraries/Adafruit_MQTT_Library/Adafruit_MQTT.cpp as follows to avoid dtostrf error 15 | 16 | //#if defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000) || \ 17 | // defined(ARDUINO_ARCH_SAMD) 18 | #if !( ESP32 || ESP8266 || defined(CORE_TEENSY) || defined(STM32F1) || defined(STM32F2) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) ) 19 | static char *dtostrf(double val, signed char width, unsigned char prec, char *sout) 20 | { 21 | char fmt[20]; 22 | sprintf(fmt, "%%%d.%df", width, prec); 23 | sprintf(sout, fmt, val); 24 | return sout; 25 | } 26 | #endif 27 | *****************************************************************************************************************************/ 28 | 29 | #include "defines.h" 30 | #include "Credentials.h" 31 | #include "dynamicParams.h" 32 | 33 | #define LOCAL_DEBUG true //false 34 | 35 | #include "Adafruit_MQTT.h" //https://github.com/adafruit/Adafruit_MQTT_Library 36 | #include "Adafruit_MQTT_Client.h" //https://github.com/adafruit/Adafruit_MQTT_Library 37 | 38 | // Create a WiFiClient class to connect to the MQTT server 39 | WiFiClient *client = NULL; 40 | 41 | Adafruit_MQTT_Client *mqtt = NULL; 42 | Adafruit_MQTT_Publish *Temperature = NULL; 43 | Adafruit_MQTT_Subscribe *LED_Control = NULL; 44 | 45 | // You have to get from a sensor. Here is just an example 46 | uint32_t measuredTemp = 5; 47 | 48 | ESPAsync_WiFiManager_Lite* ESPAsync_WiFiManager; 49 | 50 | void heartBeatPrint() 51 | { 52 | static int num = 1; 53 | 54 | if (WiFi.status() == WL_CONNECTED) 55 | Serial.print("H"); // H means connected to WiFi 56 | else 57 | { 58 | if (ESPAsync_WiFiManager->isConfigMode()) 59 | Serial.print("C"); // C means in Config Mode 60 | else 61 | Serial.print("F"); // F means not connected to WiFi 62 | } 63 | 64 | if (num == 40) 65 | { 66 | Serial.println(); 67 | num = 1; 68 | } 69 | else if (num++ % 5 == 0) 70 | { 71 | Serial.print(" "); 72 | } 73 | } 74 | 75 | void publishMQTT() 76 | { 77 | MQTT_connect(); 78 | 79 | if (Temperature->publish(measuredTemp)) 80 | { 81 | //Serial.println(F("Failed to send value to Temperature feed!")); 82 | Serial.print(F("T")); // T means publishing OK 83 | } 84 | else 85 | { 86 | //Serial.println(F("Value to Temperature feed sucessfully sent!")); 87 | Serial.print(F("F")); // F means publishing failure 88 | } 89 | } 90 | 91 | void subscribeMQTT() 92 | { 93 | Adafruit_MQTT_Subscribe *subscription; 94 | 95 | MQTT_connect(); 96 | 97 | while ((subscription = mqtt->readSubscription(5000))) 98 | { 99 | if (subscription == LED_Control) 100 | { 101 | Serial.print(F("\nGot: ")); 102 | Serial.println((char *)LED_Control->lastread); 103 | 104 | if (!strcmp((char*) LED_Control->lastread, "ON")) 105 | { 106 | digitalWrite(LED_PIN, HIGH); 107 | } 108 | else 109 | { 110 | digitalWrite(LED_PIN, LOW); 111 | } 112 | } 113 | } 114 | } 115 | 116 | void check_status() 117 | { 118 | static unsigned long checkstatus_timeout = 0; 119 | 120 | //KH 121 | #define HEARTBEAT_INTERVAL 5000L 122 | // Print WiFi hearbeat, Publish MQTT Topic every HEARTBEAT_INTERVAL (5) seconds. 123 | if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) 124 | { 125 | if (WiFi.status() == WL_CONNECTED) 126 | { 127 | // MQTT related jobs 128 | publishMQTT(); 129 | subscribeMQTT(); 130 | } 131 | 132 | heartBeatPrint(); 133 | checkstatus_timeout = millis() + HEARTBEAT_INTERVAL; 134 | } 135 | } 136 | 137 | void deleteOldInstances() 138 | { 139 | // Delete previous instances 140 | if (mqtt) 141 | { 142 | delete mqtt; 143 | mqtt = NULL; 144 | Serial.println(F("Deleting old MQTT object")); 145 | } 146 | 147 | if (Temperature) 148 | { 149 | delete Temperature; 150 | Temperature = NULL; 151 | Serial.println(F("Deleting old Temperature object")); 152 | } 153 | } 154 | 155 | #define USE_GLOBAL_TOPIC true 156 | 157 | #if USE_GLOBAL_TOPIC 158 | String completePubTopic; 159 | String completeSubTopic; 160 | #endif 161 | 162 | void createNewInstances() 163 | { 164 | if (!client) 165 | { 166 | client = new WiFiClient; 167 | 168 | if (client) 169 | { 170 | Serial.println(F("\nCreating new WiFi client object OK")); 171 | } 172 | else 173 | Serial.println(F("\nCreating new WiFi client object failed")); 174 | } 175 | 176 | // Create new instances from new data 177 | if (!mqtt) 178 | { 179 | // Setup the MQTT client class by passing in the WiFi client and MQTT server and login details. 180 | mqtt = new Adafruit_MQTT_Client(client, AIO_SERVER, atoi(AIO_SERVERPORT), AIO_USERNAME, AIO_KEY); 181 | 182 | if (mqtt) 183 | { 184 | Serial.println(F("Creating new MQTT object OK")); 185 | Serial.print(F("AIO_SERVER = ")); 186 | Serial.print(AIO_SERVER); 187 | Serial.print(F(", AIO_SERVERPORT = ")); 188 | Serial.println(AIO_SERVERPORT); 189 | Serial.print(F("AIO_USERNAME = ")); 190 | Serial.print(AIO_USERNAME); 191 | Serial.print(F(", AIO_KEY = ")); 192 | Serial.println(AIO_KEY); 193 | } 194 | else 195 | Serial.println(F("Creating new MQTT object failed")); 196 | } 197 | 198 | if (!Temperature) 199 | { 200 | #if USE_GLOBAL_TOPIC 201 | completePubTopic = String(AIO_USERNAME) + String(AIO_PUB_TOPIC); 202 | #else 203 | // Must be static or global 204 | static String completePubTopic = String(AIO_USERNAME) + String(AIO_PUB_TOPIC); 205 | #endif 206 | 207 | Temperature = new Adafruit_MQTT_Publish(mqtt, completePubTopic.c_str()); 208 | Serial.print(F("Creating new MQTT_Pub_Topic, Temperature = ")); 209 | Serial.println(completePubTopic); 210 | 211 | if (Temperature) 212 | { 213 | Serial.println(F("Creating new Temperature object OK")); 214 | Serial.print(F("Temperature MQTT_Pub_Topic = ")); 215 | Serial.println(completePubTopic); 216 | } 217 | else 218 | Serial.println(F("Creating new Temperature object failed")); 219 | } 220 | 221 | if (!LED_Control) 222 | { 223 | #if USE_GLOBAL_TOPIC 224 | completeSubTopic = String(AIO_USERNAME) + String(AIO_SUB_TOPIC); 225 | #else 226 | // Must be static or global 227 | static String completeSubTopic = String(AIO_USERNAME) + String(AIO_SUB_TOPIC); 228 | #endif 229 | 230 | LED_Control = new Adafruit_MQTT_Subscribe(mqtt, completeSubTopic.c_str()); 231 | 232 | Serial.print(F("Creating new AIO_SUB_TOPIC, LED_Control = ")); 233 | Serial.println(completeSubTopic); 234 | 235 | if (LED_Control) 236 | { 237 | Serial.println(F("Creating new LED_Control object OK")); 238 | Serial.print(F("LED_Control AIO_SUB_TOPIC = ")); 239 | Serial.println(completeSubTopic); 240 | 241 | mqtt->subscribe(LED_Control); 242 | } 243 | else 244 | Serial.println(F("Creating new LED_Control object failed")); 245 | } 246 | } 247 | 248 | void MQTT_connect() 249 | { 250 | int8_t ret; 251 | 252 | createNewInstances(); 253 | 254 | // Return if already connected 255 | if (mqtt->connected()) 256 | { 257 | return; 258 | } 259 | 260 | #if LOCAL_DEBUG 261 | Serial.println(F("\nConnecting to WiFi MQTT (3 attempts)...")); 262 | #endif 263 | 264 | uint8_t attempt = 3; 265 | 266 | while ( (ret = mqtt->connect()) ) 267 | { 268 | // connect will return 0 for connected 269 | Serial.println(mqtt->connectErrorString(ret)); 270 | 271 | #if LOCAL_DEBUG 272 | Serial.println(F("Another attemtpt to connect to MQTT in 5 seconds...")); 273 | #endif 274 | 275 | mqtt->disconnect(); 276 | delay(5000); // wait 5 seconds 277 | attempt--; 278 | 279 | if (attempt == 0) 280 | { 281 | Serial.println(F("WiFi MQTT connection failed. Continuing with program...")); 282 | return; 283 | } 284 | } 285 | 286 | #if LOCAL_DEBUG 287 | Serial.println(F("WiFi MQTT connection successful!")); 288 | #endif 289 | } 290 | 291 | #if USING_CUSTOMS_STYLE 292 | const char NewCustomsStyle[] PROGMEM = 293 | ""; 295 | #endif 296 | 297 | void setup() 298 | { 299 | // Debug console 300 | Serial.begin(115200); 301 | while (!Serial); 302 | 303 | pinMode(LED_PIN, OUTPUT); 304 | 305 | delay(200); 306 | 307 | Serial.print(F("\nStarting ESPAsync_WiFi_MQTT using ")); 308 | Serial.print(FS_Name); 309 | Serial.print(F(" on ")); 310 | Serial.println(ARDUINO_BOARD); 311 | Serial.println(ESP_ASYNC_WIFI_MANAGER_LITE_VERSION); 312 | 313 | #if USING_MRD 314 | Serial.println(ESP_MULTI_RESET_DETECTOR_VERSION); 315 | #else 316 | Serial.println(ESP_DOUBLE_RESET_DETECTOR_VERSION); 317 | #endif 318 | 319 | ESPAsync_WiFiManager = new ESPAsync_WiFiManager_Lite(); 320 | 321 | String AP_SSID = "your_customized_ssid"; 322 | String AP_PWD = "your_customized_pwd"; 323 | 324 | // Set customized AP SSID and PWD 325 | ESPAsync_WiFiManager->setConfigPortal(AP_SSID, AP_PWD); 326 | 327 | // Optional to change default AP IP(192.168.4.1) and channel(10) 328 | //ESPAsync_WiFiManager->setConfigPortalIP(IPAddress(192, 168, 120, 1)); 329 | ESPAsync_WiFiManager->setConfigPortalChannel(0); 330 | 331 | #if USING_CUSTOMS_STYLE 332 | ESPAsync_WiFiManager->setCustomsStyle(NewCustomsStyle); 333 | #endif 334 | 335 | #if USING_CUSTOMS_HEAD_ELEMENT 336 | ESPAsync_WiFiManager->setCustomsHeadElement(PSTR("")); 337 | #endif 338 | 339 | #if USING_CORS_FEATURE 340 | ESPAsync_WiFiManager->setCORSHeader(PSTR("Your Access-Control-Allow-Origin")); 341 | #endif 342 | 343 | // Set customized DHCP HostName 344 | ESPAsync_WiFiManager->begin(HOST_NAME); 345 | //Or use default Hostname "ESP_XXXXXX" 346 | //ESPAsync_WiFiManager->begin(); 347 | } 348 | 349 | #if USE_DYNAMIC_PARAMETERS 350 | void displayCredentials() 351 | { 352 | Serial.println(F("\nYour stored Credentials :")); 353 | 354 | for (uint16_t i = 0; i < NUM_MENU_ITEMS; i++) 355 | { 356 | Serial.print(myMenuItems[i].displayName); 357 | Serial.print(F(" = ")); 358 | Serial.println(myMenuItems[i].pdata); 359 | } 360 | } 361 | 362 | void displayCredentialsInLoop() 363 | { 364 | static bool displayedCredentials = false; 365 | 366 | if (!displayedCredentials) 367 | { 368 | for (int i = 0; i < NUM_MENU_ITEMS; i++) 369 | { 370 | if (!strlen(myMenuItems[i].pdata)) 371 | { 372 | break; 373 | } 374 | 375 | if ( i == (NUM_MENU_ITEMS - 1) ) 376 | { 377 | displayedCredentials = true; 378 | displayCredentials(); 379 | } 380 | } 381 | } 382 | } 383 | 384 | #endif 385 | 386 | void loop() 387 | { 388 | ESPAsync_WiFiManager->run(); 389 | check_status(); 390 | 391 | #if USE_DYNAMIC_PARAMETERS 392 | displayCredentialsInLoop(); 393 | #endif 394 | } 395 | -------------------------------------------------------------------------------- /esp32s2_WebServer_Patch/WebServer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | WebServer.cpp - Dead simple web-server. 3 | Supports only one simultaneous client, knows how to handle GET and POST. 4 | 5 | Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library 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 GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) 21 | */ 22 | 23 | // KH, Using "WebServer.handleClient delay" (https://github.com/espressif/arduino-esp32/pull/4350) 24 | 25 | #include 26 | #include 27 | #include 28 | #include "WiFiServer.h" 29 | #include "WiFiClient.h" 30 | #include "WebServer.h" 31 | #include "FS.h" 32 | #include "detail/RequestHandlersImpl.h" 33 | #include "mbedtls/md5.h" 34 | 35 | 36 | static const char AUTHORIZATION_HEADER[] = "Authorization"; 37 | static const char qop_auth[] = "qop=\"auth\""; 38 | static const char WWW_Authenticate[] = "WWW-Authenticate"; 39 | static const char Content_Length[] = "Content-Length"; 40 | 41 | 42 | WebServer::WebServer(IPAddress addr, int port) 43 | : _corsEnabled(false) 44 | , _server(addr, port) 45 | , _currentMethod(HTTP_ANY) 46 | , _currentVersion(0) 47 | , _currentStatus(HC_NONE) 48 | , _statusChange(0) 49 | , _nullDelay(true) 50 | , _currentHandler(nullptr) 51 | , _firstHandler(nullptr) 52 | , _lastHandler(nullptr) 53 | , _currentArgCount(0) 54 | , _currentArgs(nullptr) 55 | , _postArgsLen(0) 56 | , _postArgs(nullptr) 57 | , _headerKeysCount(0) 58 | , _currentHeaders(nullptr) 59 | , _contentLength(0) 60 | , _chunked(false) 61 | { 62 | } 63 | 64 | WebServer::WebServer(int port) 65 | : _corsEnabled(false) 66 | , _server(port) 67 | , _currentMethod(HTTP_ANY) 68 | , _currentVersion(0) 69 | , _currentStatus(HC_NONE) 70 | , _statusChange(0) 71 | , _nullDelay(true) 72 | , _currentHandler(nullptr) 73 | , _firstHandler(nullptr) 74 | , _lastHandler(nullptr) 75 | , _currentArgCount(0) 76 | , _currentArgs(nullptr) 77 | , _postArgsLen(0) 78 | , _postArgs(nullptr) 79 | , _headerKeysCount(0) 80 | , _currentHeaders(nullptr) 81 | , _contentLength(0) 82 | , _chunked(false) 83 | { 84 | } 85 | 86 | WebServer::~WebServer() 87 | { 88 | _server.close(); 89 | 90 | if (_currentHeaders) 91 | delete[]_currentHeaders; 92 | 93 | RequestHandler* handler = _firstHandler; 94 | 95 | while (handler) 96 | { 97 | RequestHandler* next = handler->next(); 98 | delete handler; 99 | handler = next; 100 | } 101 | } 102 | 103 | void WebServer::begin() 104 | { 105 | close(); 106 | _server.begin(); 107 | _server.setNoDelay(true); 108 | } 109 | 110 | void WebServer::begin(uint16_t port) 111 | { 112 | close(); 113 | _server.begin(port); 114 | _server.setNoDelay(true); 115 | } 116 | 117 | String WebServer::_extractParam(String& authReq, const String& param, const char delimit) 118 | { 119 | int _begin = authReq.indexOf(param); 120 | 121 | if (_begin == -1) 122 | return ""; 123 | 124 | return authReq.substring(_begin + param.length(), authReq.indexOf(delimit, _begin + param.length())); 125 | } 126 | 127 | static String md5str(String &in) 128 | { 129 | char out[33] = {0}; 130 | mbedtls_md5_context _ctx; 131 | uint8_t i; 132 | uint8_t * _buf = (uint8_t*)malloc(16); 133 | 134 | if (_buf == NULL) 135 | return String(out); 136 | 137 | memset(_buf, 0x00, 16); 138 | mbedtls_md5_init(&_ctx); 139 | mbedtls_md5_starts(&_ctx); 140 | mbedtls_md5_update(&_ctx, (const uint8_t *)in.c_str(), in.length()); 141 | mbedtls_md5_finish(&_ctx, _buf); 142 | 143 | for (i = 0; i < 16; i++) 144 | { 145 | sprintf(out + (i * 2), "%02x", _buf[i]); 146 | } 147 | 148 | out[32] = 0; 149 | free(_buf); 150 | return String(out); 151 | } 152 | 153 | bool WebServer::authenticate(const char * username, const char * password) 154 | { 155 | if (hasHeader(FPSTR(AUTHORIZATION_HEADER))) 156 | { 157 | String authReq = header(FPSTR(AUTHORIZATION_HEADER)); 158 | 159 | if (authReq.startsWith(F("Basic"))) 160 | { 161 | authReq = authReq.substring(6); 162 | authReq.trim(); 163 | char toencodeLen = strlen(username) + strlen(password) + 1; 164 | char *toencode = new char[toencodeLen + 1]; 165 | 166 | if (toencode == NULL) 167 | { 168 | authReq = ""; 169 | return false; 170 | } 171 | 172 | char *encoded = new char[base64_encode_expected_len(toencodeLen) + 1]; 173 | 174 | if (encoded == NULL) 175 | { 176 | authReq = ""; 177 | delete[] toencode; 178 | return false; 179 | } 180 | 181 | sprintf(toencode, "%s:%s", username, password); 182 | 183 | if (base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equalsConstantTime(encoded)) 184 | { 185 | authReq = ""; 186 | delete[] toencode; 187 | delete[] encoded; 188 | return true; 189 | } 190 | 191 | delete[] toencode; 192 | delete[] encoded; 193 | } 194 | else if (authReq.startsWith(F("Digest"))) 195 | { 196 | authReq = authReq.substring(7); 197 | log_v("%s", authReq.c_str()); 198 | String _username = _extractParam(authReq, F("username=\""), '\"'); 199 | 200 | if (!_username.length() || _username != String(username)) 201 | { 202 | authReq = ""; 203 | return false; 204 | } 205 | 206 | // extracting required parameters for RFC 2069 simpler Digest 207 | String _realm = _extractParam(authReq, F("realm=\""), '\"'); 208 | String _nonce = _extractParam(authReq, F("nonce=\""), '\"'); 209 | String _uri = _extractParam(authReq, F("uri=\""), '\"'); 210 | String _response = _extractParam(authReq, F("response=\""), '\"'); 211 | String _opaque = _extractParam(authReq, F("opaque=\""), '\"'); 212 | 213 | if ((!_realm.length()) || (!_nonce.length()) || (!_uri.length()) || (!_response.length()) || (!_opaque.length())) 214 | { 215 | authReq = ""; 216 | return false; 217 | } 218 | 219 | if ((_opaque != _sopaque) || (_nonce != _snonce) || (_realm != _srealm)) 220 | { 221 | authReq = ""; 222 | return false; 223 | } 224 | 225 | // parameters for the RFC 2617 newer Digest 226 | String _nc, _cnonce; 227 | 228 | if (authReq.indexOf(FPSTR(qop_auth)) != -1) 229 | { 230 | _nc = _extractParam(authReq, F("nc="), ','); 231 | _cnonce = _extractParam(authReq, F("cnonce=\""), '\"'); 232 | } 233 | 234 | String _H1 = md5str(String(username) + ':' + _realm + ':' + String(password)); 235 | log_v("Hash of user:realm:pass=%s", _H1.c_str()); 236 | String _H2 = ""; 237 | 238 | if (_currentMethod == HTTP_GET) 239 | { 240 | _H2 = md5str(String(F("GET:")) + _uri); 241 | } 242 | else if (_currentMethod == HTTP_POST) 243 | { 244 | _H2 = md5str(String(F("POST:")) + _uri); 245 | } 246 | else if (_currentMethod == HTTP_PUT) 247 | { 248 | _H2 = md5str(String(F("PUT:")) + _uri); 249 | } 250 | else if (_currentMethod == HTTP_DELETE) 251 | { 252 | _H2 = md5str(String(F("DELETE:")) + _uri); 253 | } 254 | else 255 | { 256 | _H2 = md5str(String(F("GET:")) + _uri); 257 | } 258 | 259 | log_v("Hash of GET:uri=%s", _H2.c_str()); 260 | String _responsecheck = ""; 261 | 262 | if (authReq.indexOf(FPSTR(qop_auth)) != -1) 263 | { 264 | _responsecheck = md5str(_H1 + ':' + _nonce + ':' + _nc + ':' + _cnonce + F(":auth:") + _H2); 265 | } 266 | else 267 | { 268 | _responsecheck = md5str(_H1 + ':' + _nonce + ':' + _H2); 269 | } 270 | 271 | log_v("The Proper response=%s", _responsecheck.c_str()); 272 | 273 | if (_response == _responsecheck) 274 | { 275 | authReq = ""; 276 | return true; 277 | } 278 | } 279 | 280 | authReq = ""; 281 | } 282 | 283 | return false; 284 | } 285 | 286 | String WebServer::_getRandomHexString() 287 | { 288 | char buffer[33]; // buffer to hold 32 Hex Digit + /0 289 | int i; 290 | 291 | for (i = 0; i < 4; i++) 292 | { 293 | sprintf (buffer + (i * 8), "%08x", esp_random()); 294 | } 295 | 296 | return String(buffer); 297 | } 298 | 299 | void WebServer::requestAuthentication(HTTPAuthMethod mode, const char* realm, const String& authFailMsg) 300 | { 301 | if (realm == NULL) 302 | { 303 | _srealm = String(F("Login Required")); 304 | } 305 | else 306 | { 307 | _srealm = String(realm); 308 | } 309 | 310 | if (mode == BASIC_AUTH) 311 | { 312 | sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Basic realm=\"")) + _srealm + String(F("\""))); 313 | } 314 | else 315 | { 316 | _snonce = _getRandomHexString(); 317 | _sopaque = _getRandomHexString(); 318 | sendHeader(String(FPSTR(WWW_Authenticate)), 319 | String(F("Digest realm=\"")) + _srealm + String(F("\", qop=\"auth\", nonce=\"")) + _snonce + String( 320 | F("\", opaque=\"")) + _sopaque + String(F("\""))); 321 | } 322 | 323 | using namespace mime; 324 | send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg); 325 | } 326 | 327 | void WebServer::on(const Uri &uri, WebServer::THandlerFunction handler) 328 | { 329 | on(uri, HTTP_ANY, handler); 330 | } 331 | 332 | void WebServer::on(const Uri &uri, HTTPMethod method, WebServer::THandlerFunction fn) 333 | { 334 | on(uri, method, fn, _fileUploadHandler); 335 | } 336 | 337 | void WebServer::on(const Uri &uri, HTTPMethod method, WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn) 338 | { 339 | _addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method)); 340 | } 341 | 342 | void WebServer::addHandler(RequestHandler* handler) 343 | { 344 | _addRequestHandler(handler); 345 | } 346 | 347 | void WebServer::_addRequestHandler(RequestHandler* handler) 348 | { 349 | if (!_lastHandler) 350 | { 351 | _firstHandler = handler; 352 | _lastHandler = handler; 353 | } 354 | else 355 | { 356 | _lastHandler->next(handler); 357 | _lastHandler = handler; 358 | } 359 | } 360 | 361 | void WebServer::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) 362 | { 363 | _addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header)); 364 | } 365 | 366 | void WebServer::handleClient() 367 | { 368 | if (_currentStatus == HC_NONE) 369 | { 370 | WiFiClient client = _server.available(); 371 | 372 | if (!client) 373 | { 374 | if (_nullDelay) 375 | { 376 | delay(1); 377 | } 378 | 379 | return; 380 | } 381 | 382 | log_v("New client"); 383 | 384 | _currentClient = client; 385 | _currentStatus = HC_WAIT_READ; 386 | _statusChange = millis(); 387 | } 388 | 389 | bool keepCurrentClient = false; 390 | bool callYield = false; 391 | 392 | if (_currentClient.connected()) 393 | { 394 | switch (_currentStatus) 395 | { 396 | case HC_NONE: 397 | // No-op to avoid C++ compiler warning 398 | break; 399 | 400 | case HC_WAIT_READ: 401 | 402 | // Wait for data from client to become available 403 | if (_currentClient.available()) 404 | { 405 | if (_parseRequest(_currentClient)) 406 | { 407 | // because HTTP_MAX_SEND_WAIT is expressed in milliseconds, 408 | // it must be divided by 1000 409 | _currentClient.setTimeout(HTTP_MAX_SEND_WAIT / 1000); 410 | _contentLength = CONTENT_LENGTH_NOT_SET; 411 | _handleRequest(); 412 | 413 | // Fix for issue with Chrome based browsers: https://github.com/espressif/arduino-esp32/issues/3652 414 | // if (_currentClient.connected()) { 415 | // _currentStatus = HC_WAIT_CLOSE; 416 | // _statusChange = millis(); 417 | // keepCurrentClient = true; 418 | // } 419 | } 420 | } 421 | else // !_currentClient.available() 422 | { 423 | if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) 424 | { 425 | keepCurrentClient = true; 426 | } 427 | 428 | callYield = true; 429 | } 430 | 431 | break; 432 | 433 | case HC_WAIT_CLOSE: 434 | 435 | // Wait for client to close the connection 436 | if (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT) 437 | { 438 | keepCurrentClient = true; 439 | callYield = true; 440 | } 441 | } 442 | } 443 | 444 | if (!keepCurrentClient) 445 | { 446 | _currentClient = WiFiClient(); 447 | _currentStatus = HC_NONE; 448 | _currentUpload.reset(); 449 | } 450 | 451 | if (callYield) 452 | { 453 | yield(); 454 | } 455 | } 456 | 457 | void WebServer::close() 458 | { 459 | _server.close(); 460 | _currentStatus = HC_NONE; 461 | 462 | if (!_headerKeysCount) 463 | collectHeaders(0, 0); 464 | } 465 | 466 | void WebServer::stop() 467 | { 468 | close(); 469 | } 470 | 471 | void WebServer::sendHeader(const String& name, const String& value, bool first) 472 | { 473 | String headerLine = name; 474 | headerLine += F(": "); 475 | headerLine += value; 476 | headerLine += "\r\n"; 477 | 478 | if (first) 479 | { 480 | _responseHeaders = headerLine + _responseHeaders; 481 | } 482 | else 483 | { 484 | _responseHeaders += headerLine; 485 | } 486 | } 487 | 488 | void WebServer::setContentLength(const size_t contentLength) 489 | { 490 | _contentLength = contentLength; 491 | } 492 | 493 | void WebServer::enableDelay(boolean value) 494 | { 495 | _nullDelay = value; 496 | } 497 | 498 | void WebServer::enableCORS(boolean value) 499 | { 500 | _corsEnabled = value; 501 | } 502 | 503 | void WebServer::enableCrossOrigin(boolean value) 504 | { 505 | enableCORS(value); 506 | } 507 | 508 | void WebServer::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) 509 | { 510 | response = String(F("HTTP/1.")) + String(_currentVersion) + ' '; 511 | response += String(code); 512 | response += ' '; 513 | response += _responseCodeToString(code); 514 | response += "\r\n"; 515 | 516 | using namespace mime; 517 | 518 | if (!content_type) 519 | content_type = mimeTable[html].mimeType; 520 | 521 | sendHeader(String(F("Content-Type")), String(FPSTR(content_type)), true); 522 | 523 | if (_contentLength == CONTENT_LENGTH_NOT_SET) 524 | { 525 | sendHeader(String(FPSTR(Content_Length)), String(contentLength)); 526 | } 527 | else if (_contentLength != CONTENT_LENGTH_UNKNOWN) 528 | { 529 | sendHeader(String(FPSTR(Content_Length)), String(_contentLength)); 530 | } 531 | else if (_contentLength == CONTENT_LENGTH_UNKNOWN && _currentVersion) //HTTP/1.1 or above client 532 | { 533 | //let's do chunked 534 | _chunked = true; 535 | sendHeader(String(F("Accept-Ranges")), String(F("none"))); 536 | sendHeader(String(F("Transfer-Encoding")), String(F("chunked"))); 537 | } 538 | 539 | if (_corsEnabled) 540 | { 541 | sendHeader(String(FPSTR("Access-Control-Allow-Origin")), String("*")); 542 | } 543 | 544 | sendHeader(String(F("Connection")), String(F("close"))); 545 | 546 | response += _responseHeaders; 547 | response += "\r\n"; 548 | _responseHeaders = ""; 549 | } 550 | 551 | void WebServer::send(int code, const char* content_type, const String& content) 552 | { 553 | String header; 554 | // Can we assume the following? 555 | //if(code == 200 && content.length() == 0 && _contentLength == CONTENT_LENGTH_NOT_SET) 556 | // _contentLength = CONTENT_LENGTH_UNKNOWN; 557 | _prepareHeader(header, code, content_type, content.length()); 558 | _currentClientWrite(header.c_str(), header.length()); 559 | 560 | if (content.length()) 561 | sendContent(content); 562 | } 563 | 564 | void WebServer::send_P(int code, PGM_P content_type, PGM_P content) 565 | { 566 | size_t contentLength = 0; 567 | 568 | if (content != NULL) 569 | { 570 | contentLength = strlen_P(content); 571 | } 572 | 573 | String header; 574 | char type[64]; 575 | memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type)); 576 | _prepareHeader(header, code, (const char* )type, contentLength); 577 | _currentClientWrite(header.c_str(), header.length()); 578 | sendContent_P(content); 579 | } 580 | 581 | void WebServer::send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength) 582 | { 583 | String header; 584 | char type[64]; 585 | memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type)); 586 | _prepareHeader(header, code, (const char* )type, contentLength); 587 | sendContent(header); 588 | sendContent_P(content, contentLength); 589 | } 590 | 591 | void WebServer::send(int code, char* content_type, const String& content) 592 | { 593 | send(code, (const char*)content_type, content); 594 | } 595 | 596 | void WebServer::send(int code, const String& content_type, const String& content) 597 | { 598 | send(code, (const char*)content_type.c_str(), content); 599 | } 600 | 601 | void WebServer::sendContent(const String& content) 602 | { 603 | sendContent(content.c_str(), content.length()); 604 | } 605 | 606 | void WebServer::sendContent(const char* content, size_t contentLength) 607 | { 608 | const char * footer = "\r\n"; 609 | 610 | if (_chunked) 611 | { 612 | char * chunkSize = (char *)malloc(11); 613 | 614 | if (chunkSize) 615 | { 616 | sprintf(chunkSize, "%x%s", contentLength, footer); 617 | _currentClientWrite(chunkSize, strlen(chunkSize)); 618 | free(chunkSize); 619 | } 620 | } 621 | 622 | _currentClientWrite(content, contentLength); 623 | 624 | if (_chunked) 625 | { 626 | _currentClient.write(footer, 2); 627 | 628 | if (contentLength == 0) 629 | { 630 | _chunked = false; 631 | } 632 | } 633 | } 634 | 635 | void WebServer::sendContent_P(PGM_P content) 636 | { 637 | sendContent_P(content, strlen_P(content)); 638 | } 639 | 640 | void WebServer::sendContent_P(PGM_P content, size_t size) 641 | { 642 | const char * footer = "\r\n"; 643 | 644 | if (_chunked) 645 | { 646 | char * chunkSize = (char *)malloc(11); 647 | 648 | if (chunkSize) 649 | { 650 | sprintf(chunkSize, "%x%s", size, footer); 651 | _currentClientWrite(chunkSize, strlen(chunkSize)); 652 | free(chunkSize); 653 | } 654 | } 655 | 656 | _currentClientWrite_P(content, size); 657 | 658 | if (_chunked) 659 | { 660 | _currentClient.write(footer, 2); 661 | 662 | if (size == 0) 663 | { 664 | _chunked = false; 665 | } 666 | } 667 | } 668 | 669 | 670 | void WebServer::_streamFileCore(const size_t fileSize, const String & fileName, const String & contentType) 671 | { 672 | using namespace mime; 673 | setContentLength(fileSize); 674 | 675 | if (fileName.endsWith(String(FPSTR(mimeTable[gz].endsWith))) && 676 | contentType != String(FPSTR(mimeTable[gz].mimeType)) && 677 | contentType != String(FPSTR(mimeTable[none].mimeType))) 678 | { 679 | sendHeader(F("Content-Encoding"), F("gzip")); 680 | } 681 | 682 | send(200, contentType, ""); 683 | } 684 | 685 | String WebServer::pathArg(unsigned int i) 686 | { 687 | if (_currentHandler != nullptr) 688 | return _currentHandler->pathArg(i); 689 | 690 | return ""; 691 | } 692 | 693 | String WebServer::arg(String name) 694 | { 695 | for (int j = 0; j < _postArgsLen; ++j) 696 | { 697 | if ( _postArgs[j].key == name ) 698 | return _postArgs[j].value; 699 | } 700 | 701 | for (int i = 0; i < _currentArgCount; ++i) 702 | { 703 | if ( _currentArgs[i].key == name ) 704 | return _currentArgs[i].value; 705 | } 706 | 707 | return ""; 708 | } 709 | 710 | String WebServer::arg(int i) 711 | { 712 | if (i < _currentArgCount) 713 | return _currentArgs[i].value; 714 | 715 | return ""; 716 | } 717 | 718 | String WebServer::argName(int i) 719 | { 720 | if (i < _currentArgCount) 721 | return _currentArgs[i].key; 722 | 723 | return ""; 724 | } 725 | 726 | int WebServer::args() 727 | { 728 | return _currentArgCount; 729 | } 730 | 731 | bool WebServer::hasArg(String name) 732 | { 733 | for (int j = 0; j < _postArgsLen; ++j) 734 | { 735 | if (_postArgs[j].key == name) 736 | return true; 737 | } 738 | 739 | for (int i = 0; i < _currentArgCount; ++i) 740 | { 741 | if (_currentArgs[i].key == name) 742 | return true; 743 | } 744 | 745 | return false; 746 | } 747 | 748 | 749 | String WebServer::header(String name) 750 | { 751 | for (int i = 0; i < _headerKeysCount; ++i) 752 | { 753 | if (_currentHeaders[i].key.equalsIgnoreCase(name)) 754 | return _currentHeaders[i].value; 755 | } 756 | 757 | return ""; 758 | } 759 | 760 | void WebServer::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) 761 | { 762 | _headerKeysCount = headerKeysCount + 1; 763 | 764 | if (_currentHeaders) 765 | delete[]_currentHeaders; 766 | 767 | _currentHeaders = new RequestArgument[_headerKeysCount]; 768 | _currentHeaders[0].key = FPSTR(AUTHORIZATION_HEADER); 769 | 770 | for (int i = 1; i < _headerKeysCount; i++) 771 | { 772 | _currentHeaders[i].key = headerKeys[i - 1]; 773 | } 774 | } 775 | 776 | String WebServer::header(int i) 777 | { 778 | if (i < _headerKeysCount) 779 | return _currentHeaders[i].value; 780 | 781 | return ""; 782 | } 783 | 784 | String WebServer::headerName(int i) 785 | { 786 | if (i < _headerKeysCount) 787 | return _currentHeaders[i].key; 788 | 789 | return ""; 790 | } 791 | 792 | int WebServer::headers() 793 | { 794 | return _headerKeysCount; 795 | } 796 | 797 | bool WebServer::hasHeader(String name) 798 | { 799 | for (int i = 0; i < _headerKeysCount; ++i) 800 | { 801 | if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0)) 802 | return true; 803 | } 804 | 805 | return false; 806 | } 807 | 808 | String WebServer::hostHeader() 809 | { 810 | return _hostHeader; 811 | } 812 | 813 | void WebServer::onFileUpload(THandlerFunction fn) 814 | { 815 | _fileUploadHandler = fn; 816 | } 817 | 818 | void WebServer::onNotFound(THandlerFunction fn) 819 | { 820 | _notFoundHandler = fn; 821 | } 822 | 823 | void WebServer::_handleRequest() 824 | { 825 | bool handled = false; 826 | 827 | if (!_currentHandler) 828 | { 829 | log_e("request handler not found"); 830 | } 831 | else 832 | { 833 | handled = _currentHandler->handle(*this, _currentMethod, _currentUri); 834 | 835 | if (!handled) 836 | { 837 | log_e("request handler failed to handle request"); 838 | } 839 | } 840 | 841 | if (!handled && _notFoundHandler) 842 | { 843 | _notFoundHandler(); 844 | handled = true; 845 | } 846 | 847 | if (!handled) 848 | { 849 | using namespace mime; 850 | send(404, String(FPSTR(mimeTable[html].mimeType)), String(F("Not found: ")) + _currentUri); 851 | handled = true; 852 | } 853 | 854 | if (handled) 855 | { 856 | _finalizeResponse(); 857 | } 858 | 859 | _currentUri = ""; 860 | } 861 | 862 | 863 | void WebServer::_finalizeResponse() 864 | { 865 | if (_chunked) 866 | { 867 | sendContent(""); 868 | } 869 | } 870 | 871 | String WebServer::_responseCodeToString(int code) 872 | { 873 | switch (code) 874 | { 875 | case 100: 876 | return F("Continue"); 877 | 878 | case 101: 879 | return F("Switching Protocols"); 880 | 881 | case 200: 882 | return F("OK"); 883 | 884 | case 201: 885 | return F("Created"); 886 | 887 | case 202: 888 | return F("Accepted"); 889 | 890 | case 203: 891 | return F("Non-Authoritative Information"); 892 | 893 | case 204: 894 | return F("No Content"); 895 | 896 | case 205: 897 | return F("Reset Content"); 898 | 899 | case 206: 900 | return F("Partial Content"); 901 | 902 | case 300: 903 | return F("Multiple Choices"); 904 | 905 | case 301: 906 | return F("Moved Permanently"); 907 | 908 | case 302: 909 | return F("Found"); 910 | 911 | case 303: 912 | return F("See Other"); 913 | 914 | case 304: 915 | return F("Not Modified"); 916 | 917 | case 305: 918 | return F("Use Proxy"); 919 | 920 | case 307: 921 | return F("Temporary Redirect"); 922 | 923 | case 400: 924 | return F("Bad Request"); 925 | 926 | case 401: 927 | return F("Unauthorized"); 928 | 929 | case 402: 930 | return F("Payment Required"); 931 | 932 | case 403: 933 | return F("Forbidden"); 934 | 935 | case 404: 936 | return F("Not Found"); 937 | 938 | case 405: 939 | return F("Method Not Allowed"); 940 | 941 | case 406: 942 | return F("Not Acceptable"); 943 | 944 | case 407: 945 | return F("Proxy Authentication Required"); 946 | 947 | case 408: 948 | return F("Request Time-out"); 949 | 950 | case 409: 951 | return F("Conflict"); 952 | 953 | case 410: 954 | return F("Gone"); 955 | 956 | case 411: 957 | return F("Length Required"); 958 | 959 | case 412: 960 | return F("Precondition Failed"); 961 | 962 | case 413: 963 | return F("Request Entity Too Large"); 964 | 965 | case 414: 966 | return F("Request-URI Too Large"); 967 | 968 | case 415: 969 | return F("Unsupported Media Type"); 970 | 971 | case 416: 972 | return F("Requested range not satisfiable"); 973 | 974 | case 417: 975 | return F("Expectation Failed"); 976 | 977 | case 500: 978 | return F("Internal Server Error"); 979 | 980 | case 501: 981 | return F("Not Implemented"); 982 | 983 | case 502: 984 | return F("Bad Gateway"); 985 | 986 | case 503: 987 | return F("Service Unavailable"); 988 | 989 | case 504: 990 | return F("Gateway Time-out"); 991 | 992 | case 505: 993 | return F("HTTP Version not supported"); 994 | 995 | default: 996 | return F(""); 997 | } 998 | } 999 | -------------------------------------------------------------------------------- /esp32c3_ESPAsyncWebServer_Patch/AsyncWebSocket.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Asynchronous WebServer library for Espressif MCUs 3 | 4 | Copyright (c) 2016 Hristo Gochkov. All rights reserved. 5 | This file is part of the esp8266 core for Arduino environment. 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library 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 GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | #include "Arduino.h" 22 | #include "AsyncWebSocket.h" 23 | 24 | #include 25 | 26 | #ifndef ESP8266 27 | extern "C" { 28 | typedef struct 29 | { 30 | uint32_t state[5]; 31 | uint32_t count[2]; 32 | unsigned char buffer[64]; 33 | } SHA1_CTX; 34 | 35 | void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]); 36 | void SHA1Init(SHA1_CTX* context); 37 | void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len); 38 | void SHA1Final(unsigned char digest[20], SHA1_CTX* context); 39 | } 40 | #else 41 | #include 42 | #endif 43 | 44 | #define MAX_PRINTF_LEN 64 45 | 46 | size_t webSocketSendFrameWindow(AsyncClient *client) 47 | { 48 | if (!client->canSend()) 49 | return 0; 50 | 51 | size_t space = client->space(); 52 | 53 | if (space < 9) 54 | return 0; 55 | 56 | return space - 8; 57 | } 58 | 59 | size_t webSocketSendFrame(AsyncClient *client, bool final, uint8_t opcode, bool mask, uint8_t *data, size_t len) 60 | { 61 | if (!client->canSend()) 62 | return 0; 63 | 64 | size_t space = client->space(); 65 | 66 | if (space < 2) 67 | return 0; 68 | 69 | uint8_t mbuf[4] = {0, 0, 0, 0}; 70 | uint8_t headLen = 2; 71 | 72 | if (len && mask) 73 | { 74 | headLen += 4; 75 | mbuf[0] = rand() % 0xFF; 76 | mbuf[1] = rand() % 0xFF; 77 | mbuf[2] = rand() % 0xFF; 78 | mbuf[3] = rand() % 0xFF; 79 | } 80 | 81 | if (len > 125) 82 | headLen += 2; 83 | 84 | if (space < headLen) 85 | return 0; 86 | 87 | space -= headLen; 88 | 89 | if (len > space) 90 | len = space; 91 | 92 | uint8_t *buf = (uint8_t*)malloc(headLen); 93 | 94 | if (buf == NULL) 95 | { 96 | //os_printf("could not malloc %u bytes for frame header\n", headLen); 97 | return 0; 98 | } 99 | 100 | buf[0] = opcode & 0x0F; 101 | 102 | if (final) 103 | buf[0] |= 0x80; 104 | 105 | if (len < 126) 106 | buf[1] = len & 0x7F; 107 | else 108 | { 109 | buf[1] = 126; 110 | buf[2] = (uint8_t)((len >> 8) & 0xFF); 111 | buf[3] = (uint8_t)(len & 0xFF); 112 | } 113 | 114 | if (len && mask) 115 | { 116 | buf[1] |= 0x80; 117 | memcpy(buf + (headLen - 4), mbuf, 4); 118 | } 119 | 120 | if (client->add((const char *)buf, headLen) != headLen) 121 | { 122 | //os_printf("error adding %lu header bytes\n", headLen); 123 | free(buf); 124 | return 0; 125 | } 126 | 127 | free(buf); 128 | 129 | if (len) 130 | { 131 | if (len && mask) 132 | { 133 | size_t i; 134 | 135 | for (i = 0; i < len; i++) 136 | data[i] = data[i] ^ mbuf[i % 4]; 137 | } 138 | 139 | if (client->add((const char *)data, len) != len) 140 | { 141 | //os_printf("error adding %lu data bytes\n", len); 142 | return 0; 143 | } 144 | } 145 | 146 | if (!client->send()) 147 | { 148 | //os_printf("error sending frame: %lu\n", headLen+len); 149 | return 0; 150 | } 151 | 152 | return len; 153 | } 154 | 155 | 156 | /* 157 | AsyncWebSocketMessageBuffer 158 | */ 159 | 160 | 161 | 162 | AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer() 163 | : _data(nullptr) 164 | , _len(0) 165 | , _lock(false) 166 | , _count(0) 167 | { 168 | 169 | } 170 | 171 | AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(uint8_t * data, size_t size) 172 | : _data(nullptr) 173 | , _len(size) 174 | , _lock(false) 175 | , _count(0) 176 | { 177 | 178 | if (!data) 179 | { 180 | return; 181 | } 182 | 183 | _data = new uint8_t[_len + 1]; 184 | 185 | if (_data) 186 | { 187 | memcpy(_data, data, _len); 188 | _data[_len] = 0; 189 | } 190 | } 191 | 192 | 193 | AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(size_t size) 194 | : _data(nullptr) 195 | , _len(size) 196 | , _lock(false) 197 | , _count(0) 198 | { 199 | _data = new uint8_t[_len + 1]; 200 | 201 | if (_data) 202 | { 203 | _data[_len] = 0; 204 | } 205 | 206 | } 207 | 208 | AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(const AsyncWebSocketMessageBuffer & copy) 209 | : _data(nullptr) 210 | , _len(0) 211 | , _lock(false) 212 | , _count(0) 213 | { 214 | _len = copy._len; 215 | _lock = copy._lock; 216 | _count = 0; 217 | 218 | if (_len) 219 | { 220 | _data = new uint8_t[_len + 1]; 221 | _data[_len] = 0; 222 | } 223 | 224 | if (_data) 225 | { 226 | memcpy(_data, copy._data, _len); 227 | _data[_len] = 0; 228 | } 229 | 230 | } 231 | 232 | AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(AsyncWebSocketMessageBuffer && copy) 233 | : _data(nullptr) 234 | , _len(0) 235 | , _lock(false) 236 | , _count(0) 237 | { 238 | _len = copy._len; 239 | _lock = copy._lock; 240 | _count = 0; 241 | 242 | if (copy._data) 243 | { 244 | _data = copy._data; 245 | copy._data = nullptr; 246 | } 247 | 248 | } 249 | 250 | AsyncWebSocketMessageBuffer::~AsyncWebSocketMessageBuffer() 251 | { 252 | if (_data) 253 | { 254 | delete[] _data; 255 | } 256 | } 257 | 258 | bool AsyncWebSocketMessageBuffer::reserve(size_t size) 259 | { 260 | _len = size; 261 | 262 | if (_data) 263 | { 264 | delete[] _data; 265 | _data = nullptr; 266 | } 267 | 268 | _data = new uint8_t[_len + 1]; 269 | 270 | if (_data) 271 | { 272 | _data[_len] = 0; 273 | return true; 274 | } 275 | else 276 | { 277 | return false; 278 | } 279 | 280 | } 281 | 282 | 283 | 284 | /* 285 | Control Frame 286 | */ 287 | 288 | class AsyncWebSocketControl 289 | { 290 | private: 291 | uint8_t _opcode; 292 | uint8_t *_data; 293 | size_t _len; 294 | bool _mask; 295 | bool _finished; 296 | public: 297 | AsyncWebSocketControl(uint8_t opcode, uint8_t *data = NULL, size_t len = 0, bool mask = false) 298 | : _opcode(opcode) 299 | , _len(len) 300 | , _mask(len && mask) 301 | , _finished(false) 302 | { 303 | if (data == NULL) 304 | _len = 0; 305 | 306 | if (_len) 307 | { 308 | if (_len > 125) 309 | _len = 125; 310 | 311 | _data = (uint8_t*)malloc(_len); 312 | 313 | if (_data == NULL) 314 | _len = 0; 315 | else 316 | memcpy(_data, data, len); 317 | } 318 | else 319 | _data = NULL; 320 | } 321 | virtual ~AsyncWebSocketControl() 322 | { 323 | if (_data != NULL) 324 | free(_data); 325 | } 326 | virtual bool finished() const 327 | { 328 | return _finished; 329 | } 330 | uint8_t opcode() 331 | { 332 | return _opcode; 333 | } 334 | uint8_t len() 335 | { 336 | return _len + 2; 337 | } 338 | size_t send(AsyncClient *client) 339 | { 340 | _finished = true; 341 | return webSocketSendFrame(client, true, _opcode & 0x0F, _mask, _data, _len); 342 | } 343 | }; 344 | 345 | /* 346 | Basic Buffered Message 347 | */ 348 | 349 | 350 | AsyncWebSocketBasicMessage::AsyncWebSocketBasicMessage(const char * data, size_t len, uint8_t opcode, bool mask) 351 | : _len(len) 352 | , _sent(0) 353 | , _ack(0) 354 | , _acked(0) 355 | { 356 | _opcode = opcode & 0x07; 357 | _mask = mask; 358 | _data = (uint8_t*)malloc(_len + 1); 359 | 360 | if (_data == NULL) 361 | { 362 | _len = 0; 363 | _status = WS_MSG_ERROR; 364 | } 365 | else 366 | { 367 | _status = WS_MSG_SENDING; 368 | memcpy(_data, data, _len); 369 | _data[_len] = 0; 370 | } 371 | } 372 | AsyncWebSocketBasicMessage::AsyncWebSocketBasicMessage(uint8_t opcode, bool mask) 373 | : _len(0) 374 | , _sent(0) 375 | , _ack(0) 376 | , _acked(0) 377 | , _data(NULL) 378 | { 379 | _opcode = opcode & 0x07; 380 | _mask = mask; 381 | 382 | } 383 | 384 | 385 | AsyncWebSocketBasicMessage::~AsyncWebSocketBasicMessage() 386 | { 387 | if (_data != NULL) 388 | free(_data); 389 | } 390 | 391 | void AsyncWebSocketBasicMessage::ack(size_t len, uint32_t time) 392 | { 393 | (void)time; 394 | _acked += len; 395 | 396 | if (_sent == _len && _acked == _ack) 397 | { 398 | _status = WS_MSG_SENT; 399 | } 400 | } 401 | size_t AsyncWebSocketBasicMessage::send(AsyncClient *client) 402 | { 403 | if (_status != WS_MSG_SENDING) 404 | return 0; 405 | 406 | if (_acked < _ack) 407 | { 408 | return 0; 409 | } 410 | 411 | if (_sent == _len) 412 | { 413 | if (_acked == _ack) 414 | _status = WS_MSG_SENT; 415 | 416 | return 0; 417 | } 418 | 419 | if (_sent > _len) 420 | { 421 | _status = WS_MSG_ERROR; 422 | return 0; 423 | } 424 | 425 | size_t toSend = _len - _sent; 426 | size_t window = webSocketSendFrameWindow(client); 427 | 428 | if (window < toSend) 429 | { 430 | toSend = window; 431 | } 432 | 433 | _sent += toSend; 434 | _ack += toSend + ((toSend < 126) ? 2 : 4) + (_mask * 4); 435 | 436 | bool final = (_sent == _len); 437 | uint8_t* dPtr = (uint8_t*)(_data + (_sent - toSend)); 438 | uint8_t opCode = (toSend && _sent == toSend) ? _opcode : (uint8_t)WS_CONTINUATION; 439 | 440 | size_t sent = webSocketSendFrame(client, final, opCode, _mask, dPtr, toSend); 441 | _status = WS_MSG_SENDING; 442 | 443 | if (toSend && sent != toSend) 444 | { 445 | _sent -= (toSend - sent); 446 | _ack -= (toSend - sent); 447 | } 448 | 449 | return sent; 450 | } 451 | 452 | // bool AsyncWebSocketBasicMessage::reserve(size_t size) { 453 | // if (size) { 454 | // _data = (uint8_t*)malloc(size +1); 455 | // if (_data) { 456 | // memset(_data, 0, size); 457 | // _len = size; 458 | // _status = WS_MSG_SENDING; 459 | // return true; 460 | // } 461 | // } 462 | // return false; 463 | // } 464 | 465 | 466 | /* 467 | AsyncWebSocketMultiMessage Message 468 | */ 469 | 470 | 471 | AsyncWebSocketMultiMessage::AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuffer * buffer, uint8_t opcode, bool mask) 472 | : _len(0) 473 | , _sent(0) 474 | , _ack(0) 475 | , _acked(0) 476 | , _WSbuffer(nullptr) 477 | { 478 | 479 | _opcode = opcode & 0x07; 480 | _mask = mask; 481 | 482 | if (buffer) 483 | { 484 | _WSbuffer = buffer; 485 | (*_WSbuffer)++; 486 | _data = buffer->get(); 487 | _len = buffer->length(); 488 | _status = WS_MSG_SENDING; 489 | //ets_printf("M: %u\n", _len); 490 | } 491 | else 492 | { 493 | _status = WS_MSG_ERROR; 494 | } 495 | 496 | } 497 | 498 | 499 | AsyncWebSocketMultiMessage::~AsyncWebSocketMultiMessage() 500 | { 501 | if (_WSbuffer) 502 | { 503 | (*_WSbuffer)--; // decreases the counter. 504 | } 505 | } 506 | 507 | void AsyncWebSocketMultiMessage::ack(size_t len, uint32_t time) 508 | { 509 | (void)time; 510 | _acked += len; 511 | 512 | if (_sent >= _len && _acked >= _ack) 513 | { 514 | _status = WS_MSG_SENT; 515 | } 516 | 517 | //ets_printf("A: %u\n", len); 518 | } 519 | size_t AsyncWebSocketMultiMessage::send(AsyncClient *client) 520 | { 521 | if (_status != WS_MSG_SENDING) 522 | return 0; 523 | 524 | if (_acked < _ack) 525 | { 526 | return 0; 527 | } 528 | 529 | if (_sent == _len) 530 | { 531 | _status = WS_MSG_SENT; 532 | return 0; 533 | } 534 | 535 | if (_sent > _len) 536 | { 537 | _status = WS_MSG_ERROR; 538 | //ets_printf("E: %u > %u\n", _sent, _len); 539 | return 0; 540 | } 541 | 542 | size_t toSend = _len - _sent; 543 | size_t window = webSocketSendFrameWindow(client); 544 | 545 | if (window < toSend) 546 | { 547 | toSend = window; 548 | } 549 | 550 | _sent += toSend; 551 | _ack += toSend + ((toSend < 126) ? 2 : 4) + (_mask * 4); 552 | 553 | //ets_printf("W: %u %u\n", _sent - toSend, toSend); 554 | 555 | bool final = (_sent == _len); 556 | uint8_t* dPtr = (uint8_t*)(_data + (_sent - toSend)); 557 | uint8_t opCode = (toSend && _sent == toSend) ? _opcode : (uint8_t)WS_CONTINUATION; 558 | 559 | size_t sent = webSocketSendFrame(client, final, opCode, _mask, dPtr, toSend); 560 | _status = WS_MSG_SENDING; 561 | 562 | if (toSend && sent != toSend) 563 | { 564 | //ets_printf("E: %u != %u\n", toSend, sent); 565 | _sent -= (toSend - sent); 566 | _ack -= (toSend - sent); 567 | } 568 | 569 | //ets_printf("S: %u %u\n", _sent, sent); 570 | return sent; 571 | } 572 | 573 | 574 | /* 575 | Async WebSocket Client 576 | */ 577 | const char * AWSC_PING_PAYLOAD = "ESPAsyncWebServer-PING"; 578 | const size_t AWSC_PING_PAYLOAD_LEN = 22; 579 | 580 | AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server) 581 | : _controlQueue(LinkedList([](AsyncWebSocketControl * c) 582 | { 583 | delete c; 584 | })) 585 | , _messageQueue(LinkedList([](AsyncWebSocketMessage *m) 586 | { 587 | delete m; 588 | })) 589 | , _tempObject(NULL) 590 | { 591 | _client = request->client(); 592 | _server = server; 593 | _clientId = _server->_getNextId(); 594 | _status = WS_CONNECTED; 595 | _pstate = 0; 596 | _lastMessageTime = millis(); 597 | _keepAlivePeriod = 0; 598 | _client->setRxTimeout(0); 599 | _client->onError([](void *r, AsyncClient * c, int8_t error) 600 | { 601 | (void)c; 602 | ((AsyncWebSocketClient*)(r))->_onError(error); 603 | }, this); 604 | _client->onAck([](void *r, AsyncClient * c, size_t len, uint32_t time) 605 | { 606 | (void)c; 607 | ((AsyncWebSocketClient*)(r))->_onAck(len, time); 608 | }, this); 609 | _client->onDisconnect([](void *r, AsyncClient * c) 610 | { 611 | ((AsyncWebSocketClient*)(r))->_onDisconnect(); 612 | delete c; 613 | }, this); 614 | _client->onTimeout([](void *r, AsyncClient * c, uint32_t time) 615 | { 616 | (void)c; 617 | ((AsyncWebSocketClient*)(r))->_onTimeout(time); 618 | }, this); 619 | _client->onData([](void *r, AsyncClient * c, void *buf, size_t len) 620 | { 621 | (void)c; 622 | ((AsyncWebSocketClient*)(r))->_onData(buf, len); 623 | }, this); 624 | _client->onPoll([](void *r, AsyncClient * c) 625 | { 626 | (void)c; 627 | ((AsyncWebSocketClient*)(r))->_onPoll(); 628 | }, this); 629 | _server->_addClient(this); 630 | _server->_handleEvent(this, WS_EVT_CONNECT, request, NULL, 0); 631 | delete request; 632 | } 633 | 634 | AsyncWebSocketClient::~AsyncWebSocketClient() 635 | { 636 | _messageQueue.free(); 637 | _controlQueue.free(); 638 | _server->_handleEvent(this, WS_EVT_DISCONNECT, NULL, NULL, 0); 639 | } 640 | 641 | void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) 642 | { 643 | _lastMessageTime = millis(); 644 | 645 | if (!_controlQueue.isEmpty()) 646 | { 647 | auto head = _controlQueue.front(); 648 | 649 | if (head->finished()) 650 | { 651 | len -= head->len(); 652 | 653 | if (_status == WS_DISCONNECTING && head->opcode() == WS_DISCONNECT) 654 | { 655 | _controlQueue.remove(head); 656 | _status = WS_DISCONNECTED; 657 | _client->close(true); 658 | return; 659 | } 660 | 661 | _controlQueue.remove(head); 662 | } 663 | } 664 | 665 | if (len && !_messageQueue.isEmpty()) 666 | { 667 | _messageQueue.front()->ack(len, time); 668 | } 669 | 670 | _server->_cleanBuffers(); 671 | _runQueue(); 672 | } 673 | 674 | void AsyncWebSocketClient::_onPoll() 675 | { 676 | if (_client->canSend() && (!_controlQueue.isEmpty() || !_messageQueue.isEmpty())) 677 | { 678 | _runQueue(); 679 | } 680 | else if (_keepAlivePeriod > 0 && _controlQueue.isEmpty() && _messageQueue.isEmpty() 681 | && (millis() - _lastMessageTime) >= _keepAlivePeriod) 682 | { 683 | ping((uint8_t *)AWSC_PING_PAYLOAD, AWSC_PING_PAYLOAD_LEN); 684 | } 685 | } 686 | 687 | void AsyncWebSocketClient::_runQueue() 688 | { 689 | while (!_messageQueue.isEmpty() && _messageQueue.front()->finished()) 690 | { 691 | _messageQueue.remove(_messageQueue.front()); 692 | } 693 | 694 | if (!_controlQueue.isEmpty() && (_messageQueue.isEmpty() || _messageQueue.front()->betweenFrames()) 695 | && webSocketSendFrameWindow(_client) > (size_t)(_controlQueue.front()->len() - 1)) 696 | { 697 | _controlQueue.front()->send(_client); 698 | } 699 | else if (!_messageQueue.isEmpty() && _messageQueue.front()->betweenFrames() && webSocketSendFrameWindow(_client)) 700 | { 701 | _messageQueue.front()->send(_client); 702 | } 703 | } 704 | 705 | bool AsyncWebSocketClient::queueIsFull() 706 | { 707 | if ((_messageQueue.length() >= WS_MAX_QUEUED_MESSAGES) || (_status != WS_CONNECTED) ) 708 | return true; 709 | 710 | return false; 711 | } 712 | 713 | void AsyncWebSocketClient::_queueMessage(AsyncWebSocketMessage *dataMessage) 714 | { 715 | if (dataMessage == NULL) 716 | return; 717 | 718 | if (_status != WS_CONNECTED) 719 | { 720 | delete dataMessage; 721 | return; 722 | } 723 | 724 | if (_messageQueue.length() >= WS_MAX_QUEUED_MESSAGES) 725 | { 726 | ets_printf("ERROR: Too many messages queued\n"); 727 | delete dataMessage; 728 | } 729 | else 730 | { 731 | _messageQueue.add(dataMessage); 732 | } 733 | 734 | if (_client->canSend()) 735 | _runQueue(); 736 | } 737 | 738 | void AsyncWebSocketClient::_queueControl(AsyncWebSocketControl *controlMessage) 739 | { 740 | if (controlMessage == NULL) 741 | return; 742 | 743 | _controlQueue.add(controlMessage); 744 | 745 | if (_client->canSend()) 746 | _runQueue(); 747 | } 748 | 749 | void AsyncWebSocketClient::close(uint16_t code, const char * message) 750 | { 751 | if (_status != WS_CONNECTED) 752 | return; 753 | 754 | if (code) 755 | { 756 | uint8_t packetLen = 2; 757 | 758 | if (message != NULL) 759 | { 760 | size_t mlen = strlen(message); 761 | 762 | if (mlen > 123) 763 | mlen = 123; 764 | 765 | packetLen += mlen; 766 | } 767 | 768 | char * buf = (char*)malloc(packetLen); 769 | 770 | if (buf != NULL) 771 | { 772 | buf[0] = (uint8_t)(code >> 8); 773 | buf[1] = (uint8_t)(code & 0xFF); 774 | 775 | if (message != NULL) 776 | { 777 | memcpy(buf + 2, message, packetLen - 2); 778 | } 779 | 780 | _queueControl(new AsyncWebSocketControl(WS_DISCONNECT, (uint8_t*)buf, packetLen)); 781 | free(buf); 782 | return; 783 | } 784 | } 785 | 786 | _queueControl(new AsyncWebSocketControl(WS_DISCONNECT)); 787 | } 788 | 789 | void AsyncWebSocketClient::ping(uint8_t *data, size_t len) 790 | { 791 | if (_status == WS_CONNECTED) 792 | _queueControl(new AsyncWebSocketControl(WS_PING, data, len)); 793 | } 794 | 795 | void AsyncWebSocketClient::_onError(int8_t) {} 796 | 797 | void AsyncWebSocketClient::_onTimeout(uint32_t time) 798 | { 799 | (void)time; 800 | _client->close(true); 801 | } 802 | 803 | void AsyncWebSocketClient::_onDisconnect() 804 | { 805 | _client = NULL; 806 | _server->_handleDisconnect(this); 807 | } 808 | 809 | void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) 810 | { 811 | _lastMessageTime = millis(); 812 | uint8_t *data = (uint8_t*)pbuf; 813 | 814 | while (plen > 0) 815 | { 816 | if (!_pstate) 817 | { 818 | const uint8_t *fdata = data; 819 | _pinfo.index = 0; 820 | _pinfo.final = (fdata[0] & 0x80) != 0; 821 | _pinfo.opcode = fdata[0] & 0x0F; 822 | _pinfo.masked = (fdata[1] & 0x80) != 0; 823 | _pinfo.len = fdata[1] & 0x7F; 824 | data += 2; 825 | plen -= 2; 826 | 827 | if (_pinfo.len == 126) 828 | { 829 | _pinfo.len = fdata[3] | (uint16_t)(fdata[2]) << 8; 830 | data += 2; 831 | plen -= 2; 832 | } 833 | else if (_pinfo.len == 127) 834 | { 835 | _pinfo.len = fdata[9] | (uint16_t)(fdata[8]) << 8 | (uint32_t)(fdata[7]) << 16 | (uint32_t)(fdata[6]) << 24 | 836 | (uint64_t)(fdata[5]) << 32 | (uint64_t)(fdata[4]) << 40 | (uint64_t)(fdata[3]) << 48 | (uint64_t)(fdata[2]) << 56; 837 | data += 8; 838 | plen -= 8; 839 | } 840 | 841 | if (_pinfo.masked) 842 | { 843 | memcpy(_pinfo.mask, data, 4); 844 | data += 4; 845 | plen -= 4; 846 | } 847 | } 848 | 849 | const size_t datalen = std::min((size_t)(_pinfo.len - _pinfo.index), plen); 850 | const auto datalast = data[datalen]; 851 | 852 | if (_pinfo.masked) 853 | { 854 | for (size_t i = 0; i < datalen; i++) 855 | data[i] ^= _pinfo.mask[(_pinfo.index + i) % 4]; 856 | } 857 | 858 | if ((datalen + _pinfo.index) < _pinfo.len) 859 | { 860 | _pstate = 1; 861 | 862 | if (_pinfo.index == 0) 863 | { 864 | if (_pinfo.opcode) 865 | { 866 | _pinfo.message_opcode = _pinfo.opcode; 867 | _pinfo.num = 0; 868 | } 869 | else 870 | _pinfo.num += 1; 871 | } 872 | 873 | _server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, (uint8_t*)data, datalen); 874 | 875 | _pinfo.index += datalen; 876 | } 877 | else if ((datalen + _pinfo.index) == _pinfo.len) 878 | { 879 | _pstate = 0; 880 | 881 | if (_pinfo.opcode == WS_DISCONNECT) 882 | { 883 | if (datalen) 884 | { 885 | uint16_t reasonCode = (uint16_t)(data[0] << 8) + data[1]; 886 | char * reasonString = (char*)(data + 2); 887 | 888 | if (reasonCode > 1001) 889 | { 890 | _server->_handleEvent(this, WS_EVT_ERROR, (void *)&reasonCode, (uint8_t*)reasonString, strlen(reasonString)); 891 | } 892 | } 893 | 894 | if (_status == WS_DISCONNECTING) 895 | { 896 | _status = WS_DISCONNECTED; 897 | _client->close(true); 898 | } 899 | else 900 | { 901 | _status = WS_DISCONNECTING; 902 | _client->ackLater(); 903 | _queueControl(new AsyncWebSocketControl(WS_DISCONNECT, data, datalen)); 904 | } 905 | } 906 | else if (_pinfo.opcode == WS_PING) 907 | { 908 | _queueControl(new AsyncWebSocketControl(WS_PONG, data, datalen)); 909 | } 910 | else if (_pinfo.opcode == WS_PONG) 911 | { 912 | if (datalen != AWSC_PING_PAYLOAD_LEN || memcmp(AWSC_PING_PAYLOAD, data, AWSC_PING_PAYLOAD_LEN) != 0) 913 | _server->_handleEvent(this, WS_EVT_PONG, NULL, data, datalen); 914 | } 915 | else if (_pinfo.opcode < 8) //continuation or text/binary frame 916 | { 917 | _server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, data, datalen); 918 | } 919 | } 920 | else 921 | { 922 | //os_printf("frame error: len: %u, index: %llu, total: %llu\n", datalen, _pinfo.index, _pinfo.len); 923 | //what should we do? 924 | break; 925 | } 926 | 927 | // restore byte as _handleEvent may have added a null terminator i.e., data[len] = 0; 928 | if (datalen > 0) 929 | data[datalen] = datalast; 930 | 931 | data += datalen; 932 | plen -= datalen; 933 | } 934 | } 935 | 936 | size_t AsyncWebSocketClient::printf(const char *format, ...) 937 | { 938 | va_list arg; 939 | va_start(arg, format); 940 | char* temp = new char[MAX_PRINTF_LEN]; 941 | 942 | if (!temp) 943 | { 944 | va_end(arg); 945 | return 0; 946 | } 947 | 948 | char* buffer = temp; 949 | size_t len = vsnprintf(temp, MAX_PRINTF_LEN, format, arg); 950 | va_end(arg); 951 | 952 | if (len > (MAX_PRINTF_LEN - 1)) 953 | { 954 | buffer = new char[len + 1]; 955 | 956 | if (!buffer) 957 | { 958 | delete[] temp; 959 | return 0; 960 | } 961 | 962 | va_start(arg, format); 963 | vsnprintf(buffer, len + 1, format, arg); 964 | va_end(arg); 965 | } 966 | 967 | text(buffer, len); 968 | 969 | if (buffer != temp) 970 | { 971 | delete[] buffer; 972 | } 973 | 974 | delete[] temp; 975 | return len; 976 | } 977 | 978 | #ifndef ESP32 979 | size_t AsyncWebSocketClient::printf_P(PGM_P formatP, ...) 980 | { 981 | va_list arg; 982 | va_start(arg, formatP); 983 | char* temp = new char[MAX_PRINTF_LEN]; 984 | 985 | if (!temp) 986 | { 987 | va_end(arg); 988 | return 0; 989 | } 990 | 991 | char* buffer = temp; 992 | size_t len = vsnprintf_P(temp, MAX_PRINTF_LEN, formatP, arg); 993 | va_end(arg); 994 | 995 | if (len > (MAX_PRINTF_LEN - 1)) 996 | { 997 | buffer = new char[len + 1]; 998 | 999 | if (!buffer) 1000 | { 1001 | delete[] temp; 1002 | return 0; 1003 | } 1004 | 1005 | va_start(arg, formatP); 1006 | vsnprintf_P(buffer, len + 1, formatP, arg); 1007 | va_end(arg); 1008 | } 1009 | 1010 | text(buffer, len); 1011 | 1012 | if (buffer != temp) 1013 | { 1014 | delete[] buffer; 1015 | } 1016 | 1017 | delete[] temp; 1018 | return len; 1019 | } 1020 | #endif 1021 | 1022 | void AsyncWebSocketClient::text(const char * message, size_t len) 1023 | { 1024 | _queueMessage(new AsyncWebSocketBasicMessage(message, len)); 1025 | } 1026 | void AsyncWebSocketClient::text(const char * message) 1027 | { 1028 | text(message, strlen(message)); 1029 | } 1030 | void AsyncWebSocketClient::text(uint8_t * message, size_t len) 1031 | { 1032 | text((const char *)message, len); 1033 | } 1034 | void AsyncWebSocketClient::text(char * message) 1035 | { 1036 | text(message, strlen(message)); 1037 | } 1038 | void AsyncWebSocketClient::text(const String &message) 1039 | { 1040 | text(message.c_str(), message.length()); 1041 | } 1042 | void AsyncWebSocketClient::text(const __FlashStringHelper *data) 1043 | { 1044 | PGM_P p = reinterpret_cast(data); 1045 | size_t n = 0; 1046 | 1047 | while (1) 1048 | { 1049 | if (pgm_read_byte(p + n) == 0) 1050 | break; 1051 | 1052 | n += 1; 1053 | } 1054 | 1055 | char * message = (char*) malloc(n + 1); 1056 | 1057 | if (message) 1058 | { 1059 | for (size_t b = 0; b < n; b++) 1060 | message[b] = pgm_read_byte(p++); 1061 | 1062 | message[n] = 0; 1063 | text(message, n); 1064 | free(message); 1065 | } 1066 | } 1067 | void AsyncWebSocketClient::text(AsyncWebSocketMessageBuffer * buffer) 1068 | { 1069 | _queueMessage(new AsyncWebSocketMultiMessage(buffer)); 1070 | } 1071 | 1072 | void AsyncWebSocketClient::binary(const char * message, size_t len) 1073 | { 1074 | _queueMessage(new AsyncWebSocketBasicMessage(message, len, WS_BINARY)); 1075 | } 1076 | void AsyncWebSocketClient::binary(const char * message) 1077 | { 1078 | binary(message, strlen(message)); 1079 | } 1080 | void AsyncWebSocketClient::binary(uint8_t * message, size_t len) 1081 | { 1082 | binary((const char *)message, len); 1083 | } 1084 | void AsyncWebSocketClient::binary(char * message) 1085 | { 1086 | binary(message, strlen(message)); 1087 | } 1088 | void AsyncWebSocketClient::binary(const String &message) 1089 | { 1090 | binary(message.c_str(), message.length()); 1091 | } 1092 | void AsyncWebSocketClient::binary(const __FlashStringHelper *data, size_t len) 1093 | { 1094 | PGM_P p = reinterpret_cast(data); 1095 | char * message = (char*) malloc(len); 1096 | 1097 | if (message) 1098 | { 1099 | for (size_t b = 0; b < len; b++) 1100 | message[b] = pgm_read_byte(p++); 1101 | 1102 | binary(message, len); 1103 | free(message); 1104 | } 1105 | 1106 | } 1107 | void AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer * buffer) 1108 | { 1109 | _queueMessage(new AsyncWebSocketMultiMessage(buffer, WS_BINARY)); 1110 | } 1111 | 1112 | IPAddress AsyncWebSocketClient::remoteIP() 1113 | { 1114 | if (!_client) 1115 | { 1116 | return IPAddress((uint32_t) 0); 1117 | } 1118 | 1119 | return _client->remoteIP(); 1120 | } 1121 | 1122 | uint16_t AsyncWebSocketClient::remotePort() 1123 | { 1124 | if (!_client) 1125 | { 1126 | return 0; 1127 | } 1128 | 1129 | return _client->remotePort(); 1130 | } 1131 | 1132 | 1133 | 1134 | /* 1135 | Async Web Socket - Each separate socket location 1136 | */ 1137 | 1138 | AsyncWebSocket::AsyncWebSocket(const String& url) 1139 | : _url(url) 1140 | , _clients(LinkedList([](AsyncWebSocketClient * c) 1141 | { 1142 | delete c; 1143 | })) 1144 | , _cNextId(1) 1145 | , _enabled(true) 1146 | , _buffers(LinkedList([](AsyncWebSocketMessageBuffer *b) 1147 | { 1148 | delete b; 1149 | })) 1150 | { 1151 | _eventHandler = NULL; 1152 | } 1153 | 1154 | AsyncWebSocket::~AsyncWebSocket() {} 1155 | 1156 | void AsyncWebSocket::_handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, 1157 | size_t len) 1158 | { 1159 | if (_eventHandler != NULL) 1160 | { 1161 | _eventHandler(this, client, type, arg, data, len); 1162 | } 1163 | } 1164 | 1165 | void AsyncWebSocket::_addClient(AsyncWebSocketClient * client) 1166 | { 1167 | _clients.add(client); 1168 | } 1169 | 1170 | void AsyncWebSocket::_handleDisconnect(AsyncWebSocketClient * client) 1171 | { 1172 | 1173 | _clients.remove_first([ = ](AsyncWebSocketClient * c) 1174 | { 1175 | return c->id() == client->id(); 1176 | }); 1177 | } 1178 | 1179 | bool AsyncWebSocket::availableForWriteAll() 1180 | { 1181 | for (const auto& c : _clients) 1182 | { 1183 | if (c->queueIsFull()) 1184 | return false; 1185 | } 1186 | 1187 | return true; 1188 | } 1189 | 1190 | bool AsyncWebSocket::availableForWrite(uint32_t id) 1191 | { 1192 | for (const auto& c : _clients) 1193 | { 1194 | if (c->queueIsFull() && (c->id() == id )) 1195 | return false; 1196 | } 1197 | 1198 | return true; 1199 | } 1200 | 1201 | size_t AsyncWebSocket::count() const 1202 | { 1203 | return _clients.count_if([](AsyncWebSocketClient * c) 1204 | { 1205 | return c->status() == WS_CONNECTED; 1206 | }); 1207 | } 1208 | 1209 | AsyncWebSocketClient * AsyncWebSocket::client(uint32_t id) 1210 | { 1211 | for (const auto &c : _clients) 1212 | { 1213 | if (c->id() == id && c->status() == WS_CONNECTED) 1214 | { 1215 | return c; 1216 | } 1217 | } 1218 | 1219 | return nullptr; 1220 | } 1221 | 1222 | 1223 | void AsyncWebSocket::close(uint32_t id, uint16_t code, const char * message) 1224 | { 1225 | AsyncWebSocketClient * c = client(id); 1226 | 1227 | if (c) 1228 | c->close(code, message); 1229 | } 1230 | 1231 | void AsyncWebSocket::closeAll(uint16_t code, const char * message) 1232 | { 1233 | for (const auto& c : _clients) 1234 | { 1235 | if (c->status() == WS_CONNECTED) 1236 | c->close(code, message); 1237 | } 1238 | } 1239 | 1240 | void AsyncWebSocket::cleanupClients(uint16_t maxClients) 1241 | { 1242 | if (count() > maxClients) 1243 | { 1244 | _clients.front()->close(); 1245 | } 1246 | } 1247 | 1248 | void AsyncWebSocket::ping(uint32_t id, uint8_t *data, size_t len) 1249 | { 1250 | AsyncWebSocketClient * c = client(id); 1251 | 1252 | if (c) 1253 | c->ping(data, len); 1254 | } 1255 | 1256 | void AsyncWebSocket::pingAll(uint8_t *data, size_t len) 1257 | { 1258 | for (const auto& c : _clients) 1259 | { 1260 | if (c->status() == WS_CONNECTED) 1261 | c->ping(data, len); 1262 | } 1263 | } 1264 | 1265 | void AsyncWebSocket::text(uint32_t id, const char * message, size_t len) 1266 | { 1267 | AsyncWebSocketClient * c = client(id); 1268 | 1269 | if (c) 1270 | c->text(message, len); 1271 | } 1272 | 1273 | void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer * buffer) 1274 | { 1275 | if (!buffer) 1276 | return; 1277 | 1278 | buffer->lock(); 1279 | 1280 | for (const auto& c : _clients) 1281 | { 1282 | if (c->status() == WS_CONNECTED) 1283 | { 1284 | c->text(buffer); 1285 | } 1286 | } 1287 | 1288 | buffer->unlock(); 1289 | _cleanBuffers(); 1290 | } 1291 | 1292 | 1293 | void AsyncWebSocket::textAll(const char * message, size_t len) 1294 | { 1295 | AsyncWebSocketMessageBuffer * WSBuffer = makeBuffer((uint8_t *)message, len); 1296 | textAll(WSBuffer); 1297 | } 1298 | 1299 | void AsyncWebSocket::binary(uint32_t id, const char * message, size_t len) 1300 | { 1301 | AsyncWebSocketClient * c = client(id); 1302 | 1303 | if (c) 1304 | c->binary(message, len); 1305 | } 1306 | 1307 | void AsyncWebSocket::binaryAll(const char * message, size_t len) 1308 | { 1309 | AsyncWebSocketMessageBuffer * buffer = makeBuffer((uint8_t *)message, len); 1310 | binaryAll(buffer); 1311 | } 1312 | 1313 | void AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer * buffer) 1314 | { 1315 | if (!buffer) 1316 | return; 1317 | 1318 | buffer->lock(); 1319 | 1320 | for (const auto& c : _clients) 1321 | { 1322 | if (c->status() == WS_CONNECTED) 1323 | c->binary(buffer); 1324 | } 1325 | 1326 | buffer->unlock(); 1327 | _cleanBuffers(); 1328 | } 1329 | 1330 | void AsyncWebSocket::message(uint32_t id, AsyncWebSocketMessage *message) 1331 | { 1332 | AsyncWebSocketClient * c = client(id); 1333 | 1334 | if (c) 1335 | c->message(message); 1336 | } 1337 | 1338 | void AsyncWebSocket::messageAll(AsyncWebSocketMultiMessage *message) 1339 | { 1340 | for (const auto& c : _clients) 1341 | { 1342 | if (c->status() == WS_CONNECTED) 1343 | c->message(message); 1344 | } 1345 | 1346 | _cleanBuffers(); 1347 | } 1348 | 1349 | size_t AsyncWebSocket::printf(uint32_t id, const char *format, ...) 1350 | { 1351 | AsyncWebSocketClient * c = client(id); 1352 | 1353 | if (c) 1354 | { 1355 | va_list arg; 1356 | va_start(arg, format); 1357 | size_t len = c->printf(format, arg); 1358 | va_end(arg); 1359 | return len; 1360 | } 1361 | 1362 | return 0; 1363 | } 1364 | 1365 | size_t AsyncWebSocket::printfAll(const char *format, ...) 1366 | { 1367 | va_list arg; 1368 | char* temp = new char[MAX_PRINTF_LEN]; 1369 | 1370 | if (!temp) 1371 | { 1372 | return 0; 1373 | } 1374 | 1375 | va_start(arg, format); 1376 | size_t len = vsnprintf(temp, MAX_PRINTF_LEN, format, arg); 1377 | va_end(arg); 1378 | delete[] temp; 1379 | 1380 | AsyncWebSocketMessageBuffer * buffer = makeBuffer(len); 1381 | 1382 | if (!buffer) 1383 | { 1384 | return 0; 1385 | } 1386 | 1387 | va_start(arg, format); 1388 | vsnprintf( (char *)buffer->get(), len + 1, format, arg); 1389 | va_end(arg); 1390 | 1391 | textAll(buffer); 1392 | return len; 1393 | } 1394 | 1395 | #ifndef ESP32 1396 | size_t AsyncWebSocket::printf_P(uint32_t id, PGM_P formatP, ...) 1397 | { 1398 | AsyncWebSocketClient * c = client(id); 1399 | 1400 | if (c != NULL) 1401 | { 1402 | va_list arg; 1403 | va_start(arg, formatP); 1404 | size_t len = c->printf_P(formatP, arg); 1405 | va_end(arg); 1406 | return len; 1407 | } 1408 | 1409 | return 0; 1410 | } 1411 | #endif 1412 | 1413 | size_t AsyncWebSocket::printfAll_P(PGM_P formatP, ...) 1414 | { 1415 | va_list arg; 1416 | char* temp = new char[MAX_PRINTF_LEN]; 1417 | 1418 | if (!temp) 1419 | { 1420 | return 0; 1421 | } 1422 | 1423 | va_start(arg, formatP); 1424 | size_t len = vsnprintf_P(temp, MAX_PRINTF_LEN, formatP, arg); 1425 | va_end(arg); 1426 | delete[] temp; 1427 | 1428 | AsyncWebSocketMessageBuffer * buffer = makeBuffer(len + 1); 1429 | 1430 | if (!buffer) 1431 | { 1432 | return 0; 1433 | } 1434 | 1435 | va_start(arg, formatP); 1436 | vsnprintf_P((char *)buffer->get(), len + 1, formatP, arg); 1437 | va_end(arg); 1438 | 1439 | textAll(buffer); 1440 | return len; 1441 | } 1442 | 1443 | void AsyncWebSocket::text(uint32_t id, const char * message) 1444 | { 1445 | text(id, message, strlen(message)); 1446 | } 1447 | void AsyncWebSocket::text(uint32_t id, uint8_t * message, size_t len) 1448 | { 1449 | text(id, (const char *)message, len); 1450 | } 1451 | void AsyncWebSocket::text(uint32_t id, char * message) 1452 | { 1453 | text(id, message, strlen(message)); 1454 | } 1455 | void AsyncWebSocket::text(uint32_t id, const String &message) 1456 | { 1457 | text(id, message.c_str(), message.length()); 1458 | } 1459 | void AsyncWebSocket::text(uint32_t id, const __FlashStringHelper *message) 1460 | { 1461 | AsyncWebSocketClient * c = client(id); 1462 | 1463 | if (c != NULL) 1464 | c->text(message); 1465 | } 1466 | void AsyncWebSocket::textAll(const char * message) 1467 | { 1468 | textAll(message, strlen(message)); 1469 | } 1470 | void AsyncWebSocket::textAll(uint8_t * message, size_t len) 1471 | { 1472 | textAll((const char *)message, len); 1473 | } 1474 | void AsyncWebSocket::textAll(char * message) 1475 | { 1476 | textAll(message, strlen(message)); 1477 | } 1478 | void AsyncWebSocket::textAll(const String &message) 1479 | { 1480 | textAll(message.c_str(), message.length()); 1481 | } 1482 | void AsyncWebSocket::textAll(const __FlashStringHelper *message) 1483 | { 1484 | for (const auto& c : _clients) 1485 | { 1486 | if (c->status() == WS_CONNECTED) 1487 | c->text(message); 1488 | } 1489 | } 1490 | void AsyncWebSocket::binary(uint32_t id, const char * message) 1491 | { 1492 | binary(id, message, strlen(message)); 1493 | } 1494 | void AsyncWebSocket::binary(uint32_t id, uint8_t * message, size_t len) 1495 | { 1496 | binary(id, (const char *)message, len); 1497 | } 1498 | void AsyncWebSocket::binary(uint32_t id, char * message) 1499 | { 1500 | binary(id, message, strlen(message)); 1501 | } 1502 | void AsyncWebSocket::binary(uint32_t id, const String &message) 1503 | { 1504 | binary(id, message.c_str(), message.length()); 1505 | } 1506 | void AsyncWebSocket::binary(uint32_t id, const __FlashStringHelper *message, size_t len) 1507 | { 1508 | AsyncWebSocketClient * c = client(id); 1509 | 1510 | if (c != NULL) 1511 | c-> binary(message, len); 1512 | } 1513 | void AsyncWebSocket::binaryAll(const char * message) 1514 | { 1515 | binaryAll(message, strlen(message)); 1516 | } 1517 | void AsyncWebSocket::binaryAll(uint8_t * message, size_t len) 1518 | { 1519 | binaryAll((const char *)message, len); 1520 | } 1521 | void AsyncWebSocket::binaryAll(char * message) 1522 | { 1523 | binaryAll(message, strlen(message)); 1524 | } 1525 | void AsyncWebSocket::binaryAll(const String &message) 1526 | { 1527 | binaryAll(message.c_str(), message.length()); 1528 | } 1529 | void AsyncWebSocket::binaryAll(const __FlashStringHelper *message, size_t len) 1530 | { 1531 | for (const auto& c : _clients) 1532 | { 1533 | if (c->status() == WS_CONNECTED) 1534 | c-> binary(message, len); 1535 | } 1536 | } 1537 | 1538 | const char * WS_STR_CONNECTION = "Connection"; 1539 | const char * WS_STR_UPGRADE = "Upgrade"; 1540 | const char * WS_STR_ORIGIN = "Origin"; 1541 | const char * WS_STR_VERSION = "Sec-WebSocket-Version"; 1542 | const char * WS_STR_KEY = "Sec-WebSocket-Key"; 1543 | const char * WS_STR_PROTOCOL = "Sec-WebSocket-Protocol"; 1544 | const char * WS_STR_ACCEPT = "Sec-WebSocket-Accept"; 1545 | const char * WS_STR_UUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 1546 | 1547 | bool AsyncWebSocket::canHandle(AsyncWebServerRequest *request) 1548 | { 1549 | if (!_enabled) 1550 | return false; 1551 | 1552 | if (request->method() != HTTP_GET || !request->url().equals(_url) || !request->isExpectedRequestedConnType(RCT_WS)) 1553 | return false; 1554 | 1555 | request->addInterestingHeader(WS_STR_CONNECTION); 1556 | request->addInterestingHeader(WS_STR_UPGRADE); 1557 | request->addInterestingHeader(WS_STR_ORIGIN); 1558 | request->addInterestingHeader(WS_STR_VERSION); 1559 | request->addInterestingHeader(WS_STR_KEY); 1560 | request->addInterestingHeader(WS_STR_PROTOCOL); 1561 | return true; 1562 | } 1563 | 1564 | void AsyncWebSocket::handleRequest(AsyncWebServerRequest *request) 1565 | { 1566 | if (!request->hasHeader(WS_STR_VERSION) || !request->hasHeader(WS_STR_KEY)) 1567 | { 1568 | request->send(400); 1569 | return; 1570 | } 1571 | 1572 | if ((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) 1573 | { 1574 | return request->requestAuthentication(); 1575 | } 1576 | 1577 | AsyncWebHeader* version = request->getHeader(WS_STR_VERSION); 1578 | 1579 | if (version->value().toInt() != 13) 1580 | { 1581 | AsyncWebServerResponse *response = request->beginResponse(400); 1582 | response->addHeader(WS_STR_VERSION, "13"); 1583 | request->send(response); 1584 | return; 1585 | } 1586 | 1587 | AsyncWebHeader* key = request->getHeader(WS_STR_KEY); 1588 | AsyncWebServerResponse *response = new AsyncWebSocketResponse(key->value(), this); 1589 | 1590 | if (request->hasHeader(WS_STR_PROTOCOL)) 1591 | { 1592 | AsyncWebHeader* protocol = request->getHeader(WS_STR_PROTOCOL); 1593 | //ToDo: check protocol 1594 | response->addHeader(WS_STR_PROTOCOL, protocol->value()); 1595 | } 1596 | 1597 | request->send(response); 1598 | } 1599 | 1600 | AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(size_t size) 1601 | { 1602 | AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(size); 1603 | 1604 | if (buffer) 1605 | { 1606 | AsyncWebLockGuard l(_lock); 1607 | _buffers.add(buffer); 1608 | } 1609 | 1610 | return buffer; 1611 | } 1612 | 1613 | AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(uint8_t * data, size_t size) 1614 | { 1615 | AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(data, size); 1616 | 1617 | if (buffer) 1618 | { 1619 | AsyncWebLockGuard l(_lock); 1620 | _buffers.add(buffer); 1621 | } 1622 | 1623 | return buffer; 1624 | } 1625 | 1626 | void AsyncWebSocket::_cleanBuffers() 1627 | { 1628 | AsyncWebLockGuard l(_lock); 1629 | 1630 | for (AsyncWebSocketMessageBuffer * c : _buffers) 1631 | { 1632 | if (c && c->canDelete()) 1633 | { 1634 | _buffers.remove(c); 1635 | } 1636 | } 1637 | } 1638 | 1639 | AsyncWebSocket::AsyncWebSocketClientLinkedList AsyncWebSocket::getClients() const 1640 | { 1641 | return _clients; 1642 | } 1643 | 1644 | /* 1645 | Response to Web Socket request - sends the authorization and detaches the TCP Client from the web server 1646 | Authentication code from https://github.com/Links2004/arduinoWebSockets/blob/master/src/WebSockets.cpp#L480 1647 | */ 1648 | 1649 | AsyncWebSocketResponse::AsyncWebSocketResponse(const String& key, AsyncWebSocket *server) 1650 | { 1651 | _server = server; 1652 | _code = 101; 1653 | _sendContentLength = false; 1654 | 1655 | uint8_t * hash = (uint8_t*)malloc(20); 1656 | 1657 | if (hash == NULL) 1658 | { 1659 | _state = RESPONSE_FAILED; 1660 | return; 1661 | } 1662 | 1663 | char * buffer = (char *) malloc(33); 1664 | 1665 | if (buffer == NULL) 1666 | { 1667 | free(hash); 1668 | _state = RESPONSE_FAILED; 1669 | return; 1670 | } 1671 | 1672 | #ifdef ESP8266 1673 | sha1(key + WS_STR_UUID, hash); 1674 | #else 1675 | (String&)key += WS_STR_UUID; 1676 | SHA1_CTX ctx; 1677 | SHA1Init(&ctx); 1678 | SHA1Update(&ctx, (const unsigned char*)key.c_str(), key.length()); 1679 | SHA1Final(hash, &ctx); 1680 | #endif 1681 | base64_encodestate _state; 1682 | base64_init_encodestate(&_state); 1683 | int len = base64_encode_block((const char *) hash, 20, buffer, &_state); 1684 | len = base64_encode_blockend((buffer + len), &_state); 1685 | addHeader(WS_STR_CONNECTION, WS_STR_UPGRADE); 1686 | addHeader(WS_STR_UPGRADE, "websocket"); 1687 | addHeader(WS_STR_ACCEPT, buffer); 1688 | free(buffer); 1689 | free(hash); 1690 | } 1691 | 1692 | void AsyncWebSocketResponse::_respond(AsyncWebServerRequest *request) 1693 | { 1694 | if (_state == RESPONSE_FAILED) 1695 | { 1696 | request->client()->close(true); 1697 | return; 1698 | } 1699 | 1700 | String out = _assembleHead(request->version()); 1701 | request->client()->write(out.c_str(), _headLength); 1702 | _state = RESPONSE_WAIT_ACK; 1703 | } 1704 | 1705 | size_t AsyncWebSocketResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time) 1706 | { 1707 | (void)time; 1708 | 1709 | if (len) 1710 | { 1711 | new AsyncWebSocketClient(request, _server); 1712 | } 1713 | 1714 | return 0; 1715 | } 1716 | --------------------------------------------------------------------------------