From 3aa115c40924931560e3b67da93e3da1254489d7 Mon Sep 17 00:00:00 2001 From: Khoi Hoang <57012152+khoih-prog@users.noreply.github.com> Date: Mon, 28 Dec 2020 18:01:36 -0500 Subject: [PATCH] v1.2.5 ### Releases v1.2.5 1. Clean-up all compiler warnings possible. 2. Update Table of Contents 3. Add examples 4. Add Version String --- CONTRIBUTING.md | 22 +- README.md | 461 ++++++++++++++---- .../AsyncFSBrowser_STM32.ino | 16 +- .../AsyncMultiWebServer_STM32.ino | 272 +++++++++++ .../Async_AdvancedWebServer.ino | 11 +- .../Async_HelloServer/Async_HelloServer.ino | 9 +- .../Async_HelloServer2/Async_HelloServer2.ino | 9 +- .../Async_HttpBasicAuth.ino | 10 +- .../Async_PostServer/Async_PostServer.ino | 9 +- .../Async_RegexPatterns_STM32.ino | 8 +- .../Async_SimpleWebServer_STM32.ino | 8 +- examples/MQTTClient_Auth/MQTTClient_Auth.ino | 168 +++++++ examples/MQTTClient_Auth/defines.h | 125 +++++ .../MQTTClient_Basic/MQTTClient_Basic.ino | 170 +++++++ examples/MQTTClient_Basic/defines.h | 125 +++++ .../MQTT_ThingStream/MQTT_ThingStream.ino | 221 +++++++++ examples/MQTT_ThingStream/defines.h | 125 +++++ examples/WebClient/WebClient.ino | 6 +- examples/WebClient/defines.h | 11 +- .../WebClientRepeating/WebClientRepeating.ino | 6 +- examples/WebClientRepeating/defines.h | 10 +- library.json | 6 +- library.properties | 3 +- pics/AsyncMultiWebServer_STM32_SVR1.png | Bin 0 -> 33016 bytes pics/AsyncMultiWebServer_STM32_SVR2.png | Bin 0 -> 33093 bytes pics/AsyncMultiWebServer_STM32_SVR3.png | Bin 0 -> 33945 bytes platformio/platformio.ini | 16 +- src/AsyncEventSource_STM32.cpp | 5 +- src/AsyncEventSource_STM32.h | 5 +- src/AsyncJson_STM32.h | 5 +- src/AsyncWebAuthentication_STM32.cpp | 3 +- src/AsyncWebAuthentication_STM32.h | 7 +- src/AsyncWebHandlerImpl_STM32.h | 7 +- src/AsyncWebHandlers_STM32.cpp | 3 +- src/AsyncWebRequest_STM32.cpp | 3 +- src/AsyncWebResponseImpl_STM32.h | 7 +- src/AsyncWebResponses_STM32.cpp | 3 +- src/AsyncWebServer_Debug_STM32.h | 7 +- src/AsyncWebServer_STM32.cpp | 3 +- src/AsyncWebServer_STM32.h | 9 +- src/AsyncWebSocket_STM32.cpp | 3 +- src/AsyncWebSocket_STM32.h | 5 +- src/AsyncWebSynchronization_STM32.h | 5 +- src/Crypto/Hash.h | 2 + src/Crypto/bearssl_hash.h | 2 + src/Crypto/md5.h | 2 + src/Crypto/sha1.h | 2 + src/StringArray_STM32.h | 5 +- src/libb64/cdecode.c | 108 ++-- src/libb64/cdecode.h | 15 +- src/libb64/cencode.c | 76 ++- src/libb64/cencode.h | 15 +- 52 files changed, 1890 insertions(+), 244 deletions(-) create mode 100644 examples/AsyncMultiWebServer_STM32/AsyncMultiWebServer_STM32.ino create mode 100644 examples/MQTTClient_Auth/MQTTClient_Auth.ino create mode 100644 examples/MQTTClient_Auth/defines.h create mode 100644 examples/MQTTClient_Basic/MQTTClient_Basic.ino create mode 100644 examples/MQTTClient_Basic/defines.h create mode 100644 examples/MQTT_ThingStream/MQTT_ThingStream.ino create mode 100644 examples/MQTT_ThingStream/defines.h create mode 100644 pics/AsyncMultiWebServer_STM32_SVR1.png create mode 100644 pics/AsyncMultiWebServer_STM32_SVR2.png create mode 100644 pics/AsyncMultiWebServer_STM32_SVR3.png diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0166a0d..c78be0e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,21 +1,21 @@ -## Contributing to ESP_WiFiManager +## Contributing to AsyncWebServer_STM32 ### Reporting Bugs -Please report bugs in ESP_WiFiManager if you find them. +Please report bugs in AsyncWebServer_STM32 if you find them. However, before reporting a bug please check through the following: -* [Existing Open Issues](https://github.com/khoih-prog/ESP_WiFiManager/issues) - someone might have already encountered this. +* [Existing Open Issues](https://github.com/khoih-prog/AsyncWebServer_STM32/issues) - someone might have already encountered this. -If you don't find anything, please [open a new issue](https://github.com/khoih-prog/ESP_WiFiManager/issues/new). +If you don't find anything, please [open a new issue](https://github.com/khoih-prog/AsyncWebServer_STM32/issues/new). ### How to submit a bug report Please ensure to specify the following: -* Arduino IDE version (e.g. 1.8.11) or Platform.io version -* `ESP8266` or `ESP32` Core Version (e.g. ESP8266 core v2.6.3 or ESP32 v1.0.4) +* Arduino IDE version (e.g. 1.8.13) or Platform.io version +* Board Core Version (e.g. STM32F7 Nucleo-144 NUCLEO_F767ZI core v1.9.0, etc.) * Contextual information (e.g. what you were trying to achieve) * Simplest possible steps to reproduce * Anything that might be relevant in your opinion, such as: @@ -26,10 +26,10 @@ Please ensure to specify the following: ### Example ``` -Arduino IDE version: 1.8.11 -ESP8266 Core Version 2.6.3 -OS: Ubuntu 16.04 LTS -Linux Inspiron 4.4.0-170-generic #199-Ubuntu SMP Thu Nov 14 01:45:04 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux +Arduino IDE version: 1.8.13 +STM32F7 Nucleo-144 NUCLEO_F767ZI core v1.9.0 +OS: Ubuntu 20.04 LTS +Linux xy-Inspiron-3593 5.4.0-51-generic #56-Ubuntu SMP Mon Oct 5 14:28:49 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux Context: I encountered an endless loop while trying to connect to Local WiFi. @@ -44,7 +44,7 @@ Steps to reproduce: Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful. -There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/ESP_WiFiManager/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. +There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/AsyncWebServer_STM32/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. ### Sending Pull Requests diff --git a/README.md b/README.md index a5b666f..7978501 100644 --- a/README.md +++ b/README.md @@ -9,17 +9,130 @@ --- --- -### Releases v1.2.4 +## Table of contents -1. Add back MD5/SHA1 authentication feature. -2. Add example, update README.md, clean up. +* [Table of contents](#table-of-contents) +* [Why do we need this AsyncWebServer_STM32 library](#why-do-we-need-this-asyncwebserver_stm32-library) + * [Features](#features) + * [Currently supported Boards](#currently-supported-boards) +* [Changelog](#changelog) + * [Releases v1.2.5](#releases-v125) + * [Releases v1.2.4](#releases-v124) + * [Releases v1.2.3](#releases-v123) +* [Prerequisites](#prerequisites) +* [Installation](#installation) + * [Use Arduino Library Manager](#use-arduino-library-manager) + * [Manual Install](#manual-install) + * [VS Code & PlatformIO](#vs-code--platformio) +* [Important things to remember](#important-things-to-remember) +* [Principles of operation](#principles-of-operation) + * [The Async Web server](#the-async-web-server) + * [Request Life Cycle](#request-life-cycle) + * [Rewrites and how do they work](#rewrites-and-how-do-they-work) + * [Handlers and how do they work](#handlers-and-how-do-they-work) + * [Responses and how do they work](#responses-and-how-do-they-work) + * [Template processing](#template-processing) +* [Request Variables](#request-variables) + * [Common Variables](#common-variables) + * [Headers](#headers) + * [GET, POST and FILE parameters](#get-post-and-file-parameters) + * [JSON body handling with ArduinoJson](#json-body-handling-with-arduinojson) +* [Responses](#responses) + * [Redirect to another URL](#redirect-to-another-url) + * [Basic response with HTTP Code](#basic-response-with-http-code) + * [Basic response with HTTP Code and extra headers](#basic-response-with-http-code-and-extra-headers) + * [Basic response with string content](#basic-response-with-string-content) + * [Basic response with string content and extra headers](#basic-response-with-string-content-and-extra-headers) + * [Respond with content coming from a Stream](#respond-with-content-coming-from-a-stream) + * [Respond with content coming from a Stream and extra headers](#respond-with-content-coming-from-a-stream-and-extra-headers) + * [Respond with content coming from a Stream containing templates](#respond-with-content-coming-from-a-stream-containing-templates) + * [Respond with content coming from a Stream containing templates and extra headers](#respond-with-content-coming-from-a-stream-containing-templates-and-extra-headers) + * [Respond with content using a callback](#respond-with-content-using-a-callback) + * [Respond with content using a callback and extra headers](#respond-with-content-using-a-callback-and-extra-headers) + * [Respond with content using a callback containing templates](#respond-with-content-using-a-callback-containing-templates) + * [Respond with content using a callback containing templates and extra headers](#respond-with-content-using-a-callback-containing-templates-and-extra-headers) + * [Chunked Response](#chunked-response) + * [Chunked Response containing templates](#chunked-response-containing-templates) + * [Print to response](#print-to-response) + * [ArduinoJson Basic Response](#arduinojson-basic-response) + * [ArduinoJson Advanced Response](#arduinojson-advanced-response) +* [Param Rewrite With Matching](#param-rewrite-with-matching) +* [Using filters](#using-filters) +* [Bad Responses](#bad-responses) + * [Respond with content using a callback without content length to HTTP/1.0 clients](#respond-with-content-using-a-callback-without-content-length-to-http10-clients) +* [Async WebSocket Plugin](#async-websocket-plugin) + * [Async WebSocket Event](#async-websocket-event) + * [Methods for sending data to a socket client](#methods-for-sending-data-to-a-socket-client) + * [Direct access to web socket message buffer](#direct-access-to-web-socket-message-buffer) + * [Limiting the number of web socket clients](#limiting-the-number-of-web-socket-clients) +* [Async Event Source Plugin](#async-event-source-plugin) + * [Setup Event Source on the server](#setup-event-source-on-the-server) + * [Setup Event Source in the browser](#setup-event-source-in-the-browser) +* [Remove handlers and rewrites](#remove-handlers-and-rewrites) +* [Setting up the server](#setting-up-the-server) + * [Setup global and class functions as request handlers](#setup-global-and-class-functions-as-request-handlers) + * [Methods for controlling websocket connections](#methods-for-controlling-websocket-connections) + * [Adding Default Headers](#adding-default-headers) + * [Path variable](#path-variable) +* [Examples](#examples) + * [ 1. Async_AdvancedWebServer](examples/Async_AdvancedWebServer) + * [ 2. AsyncFSBrowser_STM32](examples/AsyncFSBrowser_STM32) + * [ 3. Async_HelloServer](examples/Async_HelloServer) + * [ 4. Async_HelloServer2](examples/Async_HelloServer2) + * [ 5. Async_HttpBasicAuth](examples/Async_HttpBasicAuth) + * [ 6. AsyncMultiWebServer_STM32](examples/AsyncMultiWebServer_STM32) + * [ 7. Async_PostServer](examples/Async_PostServer) + * [ 8. Async_RegexPatterns_STM32](examples/Async_RegexPatterns_STM32) + * [ 9. Async_SimpleWebServer_STM32](examples/Async_SimpleWebServer_STM32) + * [10. **MQTTClient_Auth**](examples/MQTTClient_Auth) + * [11. **MQTTClient_Basic**](examples/MQTTClient_Basic) + * [12. **MQTT_ThingStream**](examples/MQTT_ThingStream) + * [13. WebClient](examples/WebClient) + * [14. WebClientRepeating](examples/WebClientRepeating) +* [Debug Terminal Output Samples](#debug-termimal-output-samples) + * [1. AsyncMultiWebServer_STM32 on NUCLEO_F767ZI using Built-in LAN8742A Ethernet and STM32Ethernet Library](#1-asyncmultiwebserver_stm32-on-nucleo_f767zi-using-built-in-lan8742a-ethernet-and-stm32ethernet-library) + * [2. WebClient on NUCLEO_F767ZI using Built-in LAN8742A Ethernet and STM32Ethernet Library](#1-webclient-on-nucleo_f767zi-using-built-in-lan8742a-ethernet-and-stm32ethernet-library) + * [3. MQTTClient_Auth on NUCLEO_F767ZI using Built-in LAN8742A Ethernet and STM32Ethernet Library](#1-mqttclient_auth-on-nucleo_f767zi-using-built-in-lan8742a-ethernet-and-stm32ethernet-library) + * [4. MQTTClient_Basic on NUCLEO_F767ZI using Built-in LAN8742A Ethernet and STM32Ethernet Library](#1-mqttclient_basic-on-nucleo_f767zi-using-built-in-lan8742a-ethernet-and-stm32ethernet-library) + * [5. MQTT_ThingStream on NUCLEO_F767ZI using Built-in LAN8742A Ethernet and STM32Ethernet Library](#1-mqtt_thingstream-on-nucleo_f767zi-using-built-in-lan8742a-ethernet-and-stm32ethernet-library) +* [Debug](#debug) +* [Troubleshooting](#troubleshooting) +* [Releases](#releases) +* [Issues](#issues) +* [TO DO](#to-do) +* [DONE](#done) +* [Contributions and Thanks](#contributions-and-thanks) +* [Contributing](#contributing) +* [License](#license) +* [Copyright](#copyright) -#### Initial Releases v1.2.3 +--- +--- -1. Initial coding to port [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer) to STM32 boards using builtin LAN8742A Ethernet. More supports will be added gradually later, such as AsyncUDP, other Ethernet / WiFi shields. -2. Add more examples. -3. Add debugging features. -4. Bump up to v1.2.3 to sync with [ESPAsyncWebServer v1.2.3](https://github.com/me-no-dev/ESPAsyncWebServer). +### Why do we need this Async [AsyncWebServer_STM32 library](https://github.com/khoih-prog/AsyncWebServer_STM32) + +#### Features + +- Using asynchronous network means that you can handle **more than one connection at the same time** +- **You are called once the request is ready and parsed** +- When you send the response, you are **immediately ready** to handle other connections while the server is taking care of sending the response in the background +- **Speed is OMG** +- **Easy to use API, HTTP Basic and Digest MD5 Authentication (default), ChunkedResponse** +- Easily extensible to handle **any type of content** +- Supports Continue 100 +- **Async WebSocket plugin offering different locations without extra servers or ports** +- Async EventSource (Server-Sent Events) plugin to send events to the browser +- URL Rewrite plugin for conditional and permanent url rewrites +- ServeStatic plugin that supports cache, Last-Modified, default index and more +- Simple template processing engine to handle templates + +This library is based on, modified from: + +1. [Hristo Gochkov's ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer) + +to apply the better and faster **asynchronous** feature of the **powerful** [ESPAsyncWebServer Library](https://github.com/me-no-dev/ESPAsyncWebServer) into STM32 boards using builtin LAN8742A Ethernet. + +--- #### Currently Supported Boards @@ -27,20 +140,35 @@ 2. Discovery STM32F746G-DISCOVERY 3. Any STM32 boards with enough flash/memory and already configured to run LAN8742A Ethernet. + +--- --- -### Async HTTP and WebSocket Server for STM32 boards using builtin LAN8742A Ethernet +## Changelog -This library is based on, modified from: +### Releases v1.2.5 -1. [Hristo Gochkov's ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer) +1. Clean-up all compiler warnings possible. +2. Update Table of Contents +3. Add examples +4. Add Version String -to apply the better and faster **asynchronous** feature of the **powerful** [ESPAsyncWebServer Library](https://github.com/me-no-dev/ESPAsyncWebServer) into STM32 boards using builtin LAN8742A Ethernet. +### Releases v1.2.4 + +1. Add back MD5/SHA1 authentication feature. +2. Add example, update README.md, clean up. + +#### Releases v1.2.3 + +1. Initial coding to port [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer) to STM32 boards using builtin LAN8742A Ethernet. More supports will be added gradually later, such as AsyncUDP, other Ethernet / WiFi shields. +2. Add more examples. +3. Add debugging features. +4. Bump up to v1.2.3 to sync with [ESPAsyncWebServer v1.2.3](https://github.com/me-no-dev/ESPAsyncWebServer). --- --- -## Prerequisite +## Prerequisites 1. [`Arduino IDE 1.8.13+` for Arduino](https://www.arduino.cc/en/Main/Software) 2. [`Arduino Core for STM32 1.9.0+`](https://github.com/stm32duino/Arduino_Core_STM32) for STM32 (Use Arduino Board Manager) @@ -53,6 +181,7 @@ to apply the better and faster **asynchronous** feature of the **powerful** [ESP ## Installation ### Use Arduino Library Manager + The best and easiest way is to use `Arduino Library Manager`. Search for `AsyncWebServer_STM32`, then select / install the latest version. You can also use this link [![arduino-library-badge](https://www.ardu-badge.com/badge/AsyncWebServer_STM32.svg?)](https://www.ardu-badge.com/AsyncWebServer_STM32) for more detailed instructions. ### Manual Install @@ -63,88 +192,15 @@ The best and easiest way is to use `Arduino Library Manager`. Search for `AsyncW 4. Copy the whole `AsyncWebServer_STM32-master` folder to Arduino libraries' directory such as `~/Arduino/libraries/`. ### VS Code & PlatformIO: + 1. Install [VS Code](https://code.visualstudio.com/) 2. Install [PlatformIO](https://platformio.org/platformio-ide) -3. Install **AsyncWebServer_STM32** library by using [Library Manager](https://docs.platformio.org/en/latest/librarymanager/). Search for ***AsyncWebServer_STM32*** in [Platform.io Author's Libraries](https://platformio.org/lib/search?query=author:%22Khoi%20Hoang%22) +3. Install [**AsyncWebServer_STM32** library](https://platformio.org/lib/show/11237/AsyncWebServer_STM32) by using [Library Manager](https://platformio.org/lib/show/11237/AsyncWebServer_STM32/installation). Search for **AsyncWebServer_STM32** in [Platform.io Author's Libraries](https://platformio.org/lib/search?query=author:%22Khoi%20Hoang%22) 4. Use included [platformio.ini](platformio/platformio.ini) file from examples to ensure that all dependent libraries will installed automatically. Please visit documentation for the other options and examples at [Project Configuration File](https://docs.platformio.org/page/projectconf.html) ---- - -## Table of contents - -- [AsyncWebServer_STM32](#espasyncwebserver) - - [Table of contents](#table-of-contents) - - [Why should you care](#why-should-you-care) - - [Important things to remember](#important-things-to-remember) - - [Principles of operation](#principles-of-operation) - - [The Async Web server](#the-async-web-server) - - [Request Life Cycle](#request-life-cycle) - - [Rewrites and how do they work](#rewrites-and-how-do-they-work) - - [Handlers and how do they work](#handlers-and-how-do-they-work) - - [Responses and how do they work](#responses-and-how-do-they-work) - - [Template processing](#template-processing) - - [Request Variables](#request-variables) - - [Common Variables](#common-variables) - - [Headers](#headers) - - [GET, POST and FILE parameters](#get-post-and-file-parameters) - - [JSON body handling with ArduinoJson](#json-body-handling-with-arduinojson) - - [Responses](#responses) - - [Redirect to another URL](#redirect-to-another-url) - - [Basic response with HTTP Code](#basic-response-with-http-code) - - [Basic response with HTTP Code and extra headers](#basic-response-with-http-code-and-extra-headers) - - [Basic response with string content](#basic-response-with-string-content) - - [Basic response with string content and extra headers](#basic-response-with-string-content-and-extra-headers) - - [Respond with content coming from a Stream](#respond-with-content-coming-from-a-stream) - - [Respond with content coming from a Stream and extra headers](#respond-with-content-coming-from-a-stream-and-extra-headers) - - [Respond with content coming from a Stream containing templates](#respond-with-content-coming-from-a-stream-containing-templates) - - [Respond with content coming from a Stream containing templates and extra headers](#respond-with-content-coming-from-a-stream-containing-templates-and-extra-headers) - - [Respond with content using a callback](#respond-with-content-using-a-callback) - - [Respond with content using a callback and extra headers](#respond-with-content-using-a-callback-and-extra-headers) - - [Respond with content using a callback containing templates](#respond-with-content-using-a-callback-containing-templates) - - [Respond with content using a callback containing templates and extra headers](#respond-with-content-using-a-callback-containing-templates-and-extra-headers) - - [Chunked Response](#chunked-response) - - [Chunked Response containing templates](#chunked-response-containing-templates) - - [Print to response](#print-to-response) - - [ArduinoJson Basic Response](#arduinojson-basic-response) - - [ArduinoJson Advanced Response](#arduinojson-advanced-response) - - [Param Rewrite With Matching](#param-rewrite-with-matching) - - [Using filters](#using-filters) - - [Bad Responses](#bad-responses) - - [Respond with content using a callback without content length to HTTP/1.0 clients](#respond-with-content-using-a-callback-without-content-length-to-http10-clients) - - [Async WebSocket Plugin](#async-websocket-plugin) - - [Async WebSocket Event](#async-websocket-event) - - [Methods for sending data to a socket client](#methods-for-sending-data-to-a-socket-client) - - [Direct access to web socket message buffer](#direct-access-to-web-socket-message-buffer) - - [Limiting the number of web socket clients](#limiting-the-number-of-web-socket-clients) - - [Async Event Source Plugin](#async-event-source-plugin) - - [Setup Event Source on the server](#setup-event-source-on-the-server) - - [Setup Event Source in the browser](#setup-event-source-in-the-browser) - - [Remove handlers and rewrites](#remove-handlers-and-rewrites) - - [Setting up the server](#setting-up-the-server) - - [Setup global and class functions as request handlers](#setup-global-and-class-functions-as-request-handlers) - - [Methods for controlling websocket connections](#methods-for-controlling-websocket-connections) - - [Adding Default Headers](#adding-default-headers) - - [Path variable](#path-variable) --- --- -## Why should you care - -- Using asynchronous network means that you can handle **more than one connection at the same time** -- You are called once the request is ready and parsed -- When you send the response, you are **immediately ready** to handle other connections while the server is taking care of sending the response in the background -- **Speed is OMG** -- **Easy to use API, HTTP Basic and Digest MD5 Authentication (default), ChunkedResponse** -- Easily extensible to handle **any type of content** -- Supports Continue 100 -- Async WebSocket plugin offering different locations without extra servers or ports -- Async EventSource (Server-Sent Events) plugin to send events to the browser -- URL Rewrite plugin for conditional and permanent url rewrites -- ServeStatic plugin that supports cache, Last-Modified, default index and more -- Simple template processing engine to handle templates - ---- - ## Important things to remember - This is fully asynchronous server and as such does not run on the loop thread. @@ -1309,12 +1365,16 @@ build_flags = 2. [AsyncFSBrowser_STM32](examples/AsyncFSBrowser_STM32) 3. [Async_HelloServer](examples/Async_HelloServer) 4. [Async_HelloServer2](examples/Async_HelloServer2) - 5. [Async_PostServer](examples/Async_PostServer) - 6. [Async_RegexPatterns_STM32](examples/Async_RegexPatterns_STM32) - 7. [Async_SimpleWebServer_STM32](examples/Async_SimpleWebServer_STM32) - 8. [WebClient](examples/WebClient) - 9. [WebClientRepeating](examples/WebClientRepeating) -10. [Async_HttpBasicAuth](examples/Async_HttpBasicAuth) + 5. [Async_HttpBasicAuth](examples/Async_HttpBasicAuth) + 6. [Async_PostServer](examples/Async_PostServer) + 7. [**AsyncMultiWebServer_STM32**](examples/AsyncMultiWebServer_STM32) + 8. [Async_RegexPatterns_STM32](examples/Async_RegexPatterns_STM32) + 9. [Async_SimpleWebServer_STM32](examples/Async_SimpleWebServer_STM32) +10. [**MQTTClient_Auth**](examples/MQTTClient_Auth) +11. [**MQTTClient_Basic**](examples/MQTTClient_Basic) +12. [**MQTT_ThingStream**](examples/MQTT_ThingStream) +13. [WebClient](examples/WebClient) +14. [WebClientRepeating](examples/WebClientRepeating) --- @@ -1389,6 +1449,8 @@ build_flags = #define BOARD_NAME BOARD_TYPE #endif +#define SHIELD_TYPE "LAN8742A built-in Ethernet" + #include #include #include @@ -1515,7 +1577,9 @@ void setup(void) digitalWrite(led, 0); Serial.begin(115200); - Serial.println("\nStart Async_AdvancedWebServer_STM32 on " + String(BOARD_NAME)); + + Serial.printf("\nStarting Async_AdvancedWebServer_STM32 on %s with %s\n", BOARD_NAME, SHIELD_TYPE); + Serial.println(ASYNC_WEBSERVER_STM32_VERSION); // start the ethernet connection and the server // Use random mac @@ -1560,15 +1624,206 @@ You can access the Async Advanced WebServer @ the server IP

+--- +--- + +### Debug Termimal Output Samples + +#### 1. AsyncMultiWebServer_STM32 on NUCLEO_F767ZI using Built-in LAN8742A Ethernet and STM32Ethernet Library + +Following are debug terminal output and screen shots when running example [AsyncMultiWebServer_STM32](examples/AsyncMultiWebServer_STM32) on STM32F7 Nucleo-144 NUCLEO_F767ZI using Built-in LAN8742A Ethernet and STM32Ethernet Library to demonstrate the operation of 3 independent AsyncWebServers on 3 different ports and how to handle the complicated AsyncMultiWebServers. + + +``` +Starting AsyncMultiWebServer_STM32 on NUCLEO_F767ZI with LAN8742A built-in Ethernet +AsyncWebServer_STM32 v1.2.5 + +Connected to network. IP = 192.168.2.141 +Initialize multiServer OK, serverIndex = 0, port = 8080 +HTTP server started at ports 8080 +Initialize multiServer OK, serverIndex = 1, port = 8081 +HTTP server started at ports 8081 +Initialize multiServer OK, serverIndex = 2, port = 8082 +HTTP server started at ports 8082 +``` + +You can access the Async Advanced WebServers @ the server IP and corresponding ports (8080, 8081 and 8082) + +

+ +

+ +

+ +

+ +

+ +

+ --- +#### 2. WebClient on NUCLEO_F767ZI using Built-in LAN8742A Ethernet and STM32Ethernet Library + +Following is debug terminal output when running example [WebClient](examples/WebClient) on STM32F7 Nucleo-144 NUCLEO_F767ZI using Built-in LAN8742A Ethernet and STM32Ethernet Library. + +``` +Starting WebClient on NUCLEO_F767ZI with LAN8742A built-in Ethernet +AsyncWebServer_STM32 v1.2.5 +You're connected to the network, IP = 192.168.2.71 + +Starting connection to server... +Connected to server +HTTP/1.1 200 OK +Server: nginx/1.4.2 +Date: Mon, 28 Dec 2020 22:30:39 GMT +Content-Type: text/plain +Content-Length: 2263 +Last-Modified: Wed, 02 Oct 2013 13:46:47 GMT +Connection: close +Vary: Accept-Encoding +ETag: "524c23c7-8d7" +Accept-Ranges: bytes + + + `:;;;,` .:;;:. + .;;;;;;;;;;;` :;;;;;;;;;;: TM + `;;;;;;;;;;;;;;;` :;;;;;;;;;;;;;;; + :;;;;;;;;;;;;;;;;;; `;;;;;;;;;;;;;;;;;; + ;;;;;;;;;;;;;;;;;;;;; .;;;;;;;;;;;;;;;;;;;; + ;;;;;;;;:` `;;;;;;;;; ,;;;;;;;;.` .;;;;;;;; + .;;;;;;, :;;;;;;; .;;;;;;; ;;;;;;; + ;;;;;; ;;;;;;; ;;;;;;, ;;;;;;. + ,;;;;; ;;;;;;.;;;;;;` ;;;;;; + ;;;;;. ;;;;;;;;;;;` ``` ;;;;;` + ;;;;; ;;;;;;;;;, ;;; .;;;;; +`;;;;: `;;;;;;;; ;;; ;;;;; +,;;;;` `,,,,,,,, ;;;;;;; .,,;;;,,, ;;;;; +:;;;;` .;;;;;;;; ;;;;;, :;;;;;;;; ;;;;; +:;;;;` .;;;;;;;; `;;;;;; :;;;;;;;; ;;;;; +.;;;;. ;;;;;;;. ;;; ;;;;; + ;;;;; ;;;;;;;;; ;;; ;;;;; + ;;;;; .;;;;;;;;;; ;;; ;;;;;, + ;;;;;; `;;;;;;;;;;;; ;;;;; + `;;;;;, .;;;;;; ;;;;;;; ;;;;;; + ;;;;;;: :;;;;;;. ;;;;;;; ;;;;;; + ;;;;;;;` .;;;;;;;, ;;;;;;;; ;;;;;;;: + ;;;;;;;;;:,:;;;;;;;;;: ;;;;;;;;;;:,;;;;;;;;;; + `;;;;;;;;;;;;;;;;;;;. ;;;;;;;;;;;;;;;;;;;; + ;;;;;;;;;;;;;;;;; :;;;;;;;;;;;;;;;;: + ,;;;;;;;;;;;;;, ;;;;;;;;;;;;;; + .;;;;;;;;;` ,;;;;;;;;: + + + + + ;;; ;;;;;` ;;;;: .;; ;; ,;;;;;, ;;. `;, ;;;; + ;;; ;;:;;; ;;;;;; .;; ;; ,;;;;;: ;;; `;, ;;;:;; + ,;:; ;; ;; ;; ;; .;; ;; ,;, ;;;,`;, ;; ;; + ;; ;: ;; ;; ;; ;; .;; ;; ,;, ;;;;`;, ;; ;;. + ;: ;; ;;;;;: ;; ;; .;; ;; ,;, ;;`;;;, ;; ;;` + ,;;;;; ;;`;; ;; ;; .;; ;; ,;, ;; ;;;, ;; ;; + ;; ,;, ;; .;; ;;;;;: ;;;;;: ,;;;;;: ;; ;;, ;;;;;; + ;; ;; ;; ;;` ;;;;. `;;;: ,;;;;;, ;; ;;, ;;;; + +Disconnecting from server... +``` + --- + +#### 3. MQTTClient_Auth on NUCLEO_F767ZI using Built-in LAN8742A Ethernet and STM32Ethernet Library + +Following is debug terminal output when running example [MQTTClient_Auth](examples/MQTTClient_Auth) on STM32F7 Nucleo-144 NUCLEO_F767ZI using Built-in LAN8742A Ethernet and STM32Ethernet Library. + +``` +Starting MQTTClient_Auth on NUCLEO_F767ZI with LAN8742A built-in Ethernet +AsyncWebServer_STM32 v1.2.5 + +Connected to network. IP = 192.168.2.71 +Attempting MQTT connection to broker.emqx.io...connected +Message Send : MQTT_Pub => Hello from MQTTClient_Auth on NUCLEO_F767ZI with LAN8742A built-in Ethernet +Message arrived [MQTT_Pub] Hello from MQTTClient_Auth on NUCLEO_F767ZI with LAN8742A built-in Ethernet +Message Send : MQTT_Pub => Hello from MQTTClient_Auth on NUCLEO_F767ZI with LAN8742A built-in Ethernet +Message arrived [MQTT_Pub] Hello from MQTTClient_Auth on NUCLEO_F767ZI with LAN8742A built-in Ethernet +``` + +--- + +#### 4. MQTTClient_Basic on NUCLEO_F767ZI using Built-in LAN8742A Ethernet and STM32Ethernet Library + +Following is debug terminal output when running example [MQTTClient_Basic](examples/MQTTClient_Basic) on STM32F7 Nucleo-144 NUCLEO_F767ZI using Built-in LAN8742A Ethernet and STM32Ethernet Library. + +``` +Starting MQTTClient_Basic on NUCLEO_F767ZI with LAN8742A built-in Ethernet +AsyncWebServer_STM32 v1.2.5 + +Connected to network. IP = 192.168.2.71 +Attempting MQTT connection to broker.shiftr.io...connected +Message Send : MQTT_Pub => Hello from MQTTClient_Basic on NUCLEO_F767ZI with LAN8742A built-in Ethernet +Message arrived [MQTT_Pub] Hello from MQTTClient_Basic on NUCLEO_F767ZI with LAN8742A built-in Ethernet +Message Send : MQTT_Pub => Hello from MQTTClient_Basic on NUCLEO_F767ZI with LAN8742A built-in Ethernet +Message arrived [MQTT_Pub] Hello from MQTTClient_Basic on NUCLEO_F767ZI with LAN8742A built-in Ethernet +Message Send : MQTT_Pub => Hello from MQTTClient_Basic on NUCLEO_F767ZI with LAN8742A built-in Ethernet +Message arrived [MQTT_Pub] Hello from MQTTClient_Basic on NUCLEO_F767ZI with LAN8742A built-in Ethernet +Message Send : MQTT_Pub => Hello from MQTTClient_Basic on NUCLEO_F767ZI with LAN8742A built-in Ethernet +Message arrived [MQTT_Pub] Hello from MQTTClient_Basic on NUCLEO_F767ZI with LAN8742A built-in Ethernet +Message Send : MQTT_Pub => Hello from MQTTClient_Basic on NUCLEO_F767ZI with LAN8742A built-in Ethernet +Message arrived [MQTT_Pub] Hello from MQTTClient_Basic on NUCLEO_F767ZI with LAN8742A built-in Ethernet +Message Send : MQTT_Pub => Hello from MQTTClient_Basic on NUCLEO_F767ZI with LAN8742A built-in Ethernet +Message arrived [MQTT_Pub] Hello from MQTTClient_Basic on NUCLEO_F767ZI with LAN8742A built-in Ethernet +``` + +--- + +#### 5. MQTT_ThingStream on NUCLEO_F767ZI using Built-in LAN8742A Ethernet and STM32Ethernet Library + +Following is debug terminal output when running example [MQTT_ThingStream](examples/MQTT_ThingStream) on STM32F7 Nucleo-144 NUCLEO_F767ZI using Built-in LAN8742A Ethernet and STM32Ethernet Library. + +``` +Starting MQTT_ThingStream on NUCLEO_F767ZI with LAN8742A built-in Ethernet +AsyncWebServer_STM32 v1.2.5 + +Connected to network. IP = 192.168.2.71 +*************************************** +STM32_Pub +*************************************** +Attempting MQTT connection to broker.emqx.io +...connected +Published connection message successfully! +Subcribed to: STM32_Sub +MQTT Message Send : STM32_Pub => Hello from MQTT_ThingStream on NUCLEO_F767ZI with LAN8742A built-in Ethernet +MQTT Message receive [STM32_Pub] Hello from MQTT_ThingStream on NUCLEO_F767ZI with LAN8742A built-in Ethernet +MQTT Message Send : STM32_Pub => Hello from MQTT_ThingStream on NUCLEO_F767ZI with LAN8742A built-in Ethernet +MQTT Message receive [STM32_Pub] Hello from MQTT_ThingStream on NUCLEO_F767ZI with LAN8742A built-in Ethernet +MQTT Message Send : STM32_Pub => Hello from MQTT_ThingStream on NUCLEO_F767ZI with LAN8742A built-in Ethernet +MQTT Message receive [STM32_Pub] Hello from MQTT_ThingStream on NUCLEO_F767ZI with LAN8742A built-in Ethernet +MQTT Message Send : STM32_Pub => Hello from MQTT_ThingStream on NUCLEO_F767ZI with LAN8742A built-in Ethernet +MQTT Message receive [STM32_Pub] Hello from MQTT_ThingStream on NUCLEO_F767ZI with LAN8742A built-in Ethernet +MQTT Message Send : STM32_Pub => Hello from MQTT_ThingStream on NUCLEO_F767ZI with LAN8742A built-in Ethernet +MQTT Message receive [STM32_Pub] Hello from MQTT_ThingStream on NUCLEO_F767ZI with LAN8742A built-in Ethernet +``` + +--- +--- + +### Debug + +Debug is enabled by default on Serial. Debug Level from 0 to 4. To disable, change the _ETHERNET_WEBSERVER_LOGLEVEL_ to 0 + +```cpp +// Use this to output debug msgs to Serial +#define ASYNCWEBSERVER_STM32_DEBUG_PORT Serial +// Use this to disable all output debug msgs +// Debug Level from 0 to 4 +#define _ASYNCWEBSERVER_STM32_LOGLEVEL_ 0 +``` + ### Troubleshooting If you get compilation errors, more often than not, you may need to install a newer version of Arduino IDE, the Arduino `STM32` core or depending libraries. -Sometimes, the library will only work if you update the `STM32` core to the latest version because I am always using the latest cores /libraries. +Sometimes, the library will only work if you update the `STM32` core to the latest version because I'm always using the latest cores /libraries. If you connect to the created configuration Access Point but the ConfigPortal does not show up, just open a browser and type in the IP of the web portal, by default `192.168.4.1`. @@ -1600,6 +1855,15 @@ Submit issues to: [AsyncWebServer_STM32 issues](https://github.com/khoih-prog/As --- --- +## Releases + +### Releases v1.2.5 + +1. Clean-up all compiler warnings possible. +2. Update Table of Contents +3. Add examples +4. Add Version String + ### Releases v1.2.4 1. Add back MD5/SHA1 authentication feature. @@ -1618,7 +1882,7 @@ Submit issues to: [AsyncWebServer_STM32 issues](https://github.com/khoih-prog/As 2. Discovery STM32F746G-DISCOVERY 3. Any STM32 boards with enough flash/memory and already configured to run LAN8742A Ethernet. ---- + --- This library is based on, modified from: @@ -1627,6 +1891,7 @@ This library is based on, modified from: to apply the better and faster **asynchronous** feature of the **great** [ESPAsyncWebServer Library](https://github.com/me-no-dev/ESPAsyncWebServer) into STM32 boards using builtin LAN8742A Ethernet. +--- --- ### Contributions and Thanks @@ -1658,7 +1923,7 @@ If you want to contribute to this project: --- -### License and credits ### +### License - The library is licensed under [MIT](https://github.com/khoih-prog/AsyncWebServer_STM32/blob/master/LICENSE) diff --git a/examples/AsyncFSBrowser_STM32/AsyncFSBrowser_STM32.ino b/examples/AsyncFSBrowser_STM32/AsyncFSBrowser_STM32.ino index 6651f54..7a269bc 100644 --- a/examples/AsyncFSBrowser_STM32/AsyncFSBrowser_STM32.ino +++ b/examples/AsyncFSBrowser_STM32/AsyncFSBrowser_STM32.ino @@ -9,13 +9,14 @@ Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 Licensed under MIT license - Version: 1.2.4 + Version: 1.2.5 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. *****************************************************************************************************************************/ /* Currently support @@ -83,6 +84,8 @@ #define BOARD_NAME BOARD_TYPE #endif +#define SHIELD_TYPE "LAN8742A built-in Ethernet" + #include #include #include @@ -126,7 +129,7 @@ void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventT if (type == WS_EVT_CONNECT) { Serial.printf("ws[%s][%u] connect\n", server->url(), client->id()); - client->printf("Hello Client %u :)", client->id()); + client->printf("Hello Client %lu :)", client->id()); client->ping(); } else if (type == WS_EVT_DISCONNECT) @@ -160,7 +163,7 @@ void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventT } else { - char buff[3]; + char buff[6]; for (size_t i = 0; i < info->len; i++) { @@ -199,7 +202,7 @@ void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventT } else { - char buff[3]; + char buff[6]; for (size_t i = 0; i < len; i++) { @@ -237,7 +240,8 @@ void setup() Serial.begin(115200); while (!Serial); - Serial.println("\nStarting AsyncFSBrowser_STM32 on " + String(BOARD_NAME)); + Serial.printf("\nStarting AsyncFSBrowser_STM32 on %s with %s\n", BOARD_NAME, SHIELD_TYPE); + Serial.println(ASYNC_WEBSERVER_STM32_VERSION); // start the ethernet connection and the server // Use random mac @@ -320,6 +324,8 @@ void setup() server.onRequestBody([](AsyncWebServerRequest * request, uint8_t *data, size_t len, size_t index, size_t total) { + AWS_STM32_UNUSED(request); + if (!index) Serial.printf("BodyStart: %u\n", total); diff --git a/examples/AsyncMultiWebServer_STM32/AsyncMultiWebServer_STM32.ino b/examples/AsyncMultiWebServer_STM32/AsyncMultiWebServer_STM32.ino new file mode 100644 index 0000000..1805a3c --- /dev/null +++ b/examples/AsyncMultiWebServer_STM32/AsyncMultiWebServer_STM32.ino @@ -0,0 +1,272 @@ +/**************************************************************************************************************************** + AsyncMultiWebServer_STM32.h - Dead simple AsyncWebServer for STM32 built-in LAN8742A Ethernet + + For STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) + + AsyncWebServer_STM32 is a library for the STM32 run built-in Ethernet WebServer + + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 + Licensed under MIT license + + Version: 1.2.5 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). + Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 + 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. + *****************************************************************************************************************************/ +/* + Currently support + 1) STM32 boards with built-in Ethernet (to use USE_BUILTIN_ETHERNET = true) such as : + - Nucleo-144 (F429ZI, F767ZI) + - Discovery (STM32F746G-DISCOVERY) + - STM32 boards (STM32F/L/H/G/WB/MP1) with 32K+ Flash, with Built-in Ethernet, + - See How To Use Built-in Ethernet at (https://github.com/khoih-prog/EthernetWebServer_STM32/issues/1) +*/ + +#if !( defined(STM32F0) || defined(STM32F1) || defined(STM32F2) || defined(STM32F3) ||defined(STM32F4) || defined(STM32F7) || \ + defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32H7) ||defined(STM32G0) || defined(STM32G4) || \ + defined(STM32WB) || defined(STM32MP1) ) + #error This code is designed to run on STM32F/L/H/G/WB/MP1 platform! Please check your Tools->Board setting. +#endif + +#if defined(STM32F0) + #warning STM32F0 board selected + #define BOARD_TYPE "STM32F0" +#elif defined(STM32F1) + #warning STM32F1 board selected + #define BOARD_TYPE "STM32F1" +#elif defined(STM32F2) + #warning STM32F2 board selected + #define BOARD_TYPE "STM32F2" +#elif defined(STM32F3) + #warning STM32F3 board selected + #define BOARD_TYPE "STM32F3" +#elif defined(STM32F4) + #warning STM32F4 board selected + #define BOARD_TYPE "STM32F4" +#elif defined(STM32F7) + #warning STM32F7 board selected + #define BOARD_TYPE "STM32F7" +#elif defined(STM32L0) + #warning STM32L0 board selected + #define BOARD_TYPE "STM32L0" +#elif defined(STM32L1) + #warning STM32L1 board selected + #define BOARD_TYPE "STM32L1" +#elif defined(STM32L4) + #warning STM32L4 board selected + #define BOARD_TYPE "STM32L4" +#elif defined(STM32H7) + #warning STM32H7 board selected + #define BOARD_TYPE "STM32H7" +#elif defined(STM32G0) + #warning STM32G0 board selected + #define BOARD_TYPE "STM32G0" +#elif defined(STM32G4) + #warning STM32G4 board selected + #define BOARD_TYPE "STM32G4" +#elif defined(STM32WB) + #warning STM32WB board selected + #define BOARD_TYPE "STM32WB" +#elif defined(STM32MP1) + #warning STM32MP1 board selected + #define BOARD_TYPE "STM32MP1" +#else + #warning STM32 unknown board selected + #define BOARD_TYPE "STM32 Unknown" +#endif + +#ifndef BOARD_NAME + #define BOARD_NAME BOARD_TYPE +#endif + +#define SHIELD_TYPE "LAN8742A built-in Ethernet" + +#include +#include +#include + +// Enter a MAC address and IP address for your controller below. +#define NUMBER_OF_MAC 20 + +byte mac[][NUMBER_OF_MAC] = +{ + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x01 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x02 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x03 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x04 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x05 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x06 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x07 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x08 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x09 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0A }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0B }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0C }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0D }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0E }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0F }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x10 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x11 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x12 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x13 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x14 }, +}; +// Select the IP address according to your local network +IPAddress ip(192, 168, 2, 232); + +unsigned int analogReadPin [] = { 12, 13, 14 }; + +#define BUFFER_SIZE 500 + +#define HTTP_PORT1 8080 +#define HTTP_PORT2 8081 +#define HTTP_PORT3 8082 + +AsyncWebServer* server1; +AsyncWebServer* server2; +AsyncWebServer* server3; + +AsyncWebServer* multiServer [] = { server1, server2, server3 }; +uint16_t http_port [] = { HTTP_PORT1, HTTP_PORT2, HTTP_PORT3 }; + +#define NUM_SERVERS ( sizeof(multiServer) / sizeof(AsyncWebServer*) ) + +unsigned int serverIndex; + +String createBuffer() +{ + char temp[BUFFER_SIZE]; + + memset(temp, 0, sizeof(temp)); + + int sec = millis() / 1000; + int min = sec / 60; + int hr = min / 60; + int day = hr / 24; + + snprintf(temp, BUFFER_SIZE - 1, + "\ +\ +\ +%s\ +\ +\ +\ +

Hello from %s

\ +

running AsyncWebServer_STM32

\ +

on %s

\ +

Uptime: %d d %02d:%02d:%02d

\ +\ +", BOARD_NAME, BOARD_NAME, "Built-in LAN8742A", day, hr, min % 60, sec % 60); + + return temp; +} + + +void handleRoot(AsyncWebServerRequest * request) +{ + String message = createBuffer(); + request->send(200, F("text/html"), message); +} + +String createNotFoundBuffer(AsyncWebServerRequest * request) +{ + String message; + + message.reserve(500); + + message = F("File Not Found\n\n"); + + message += F("URI: "); + message += request->url(); + message += F("\nMethod: "); + message += (request->method() == HTTP_GET) ? F("GET") : F("POST"); + message += F("\nArguments: "); + message += request->args(); + message += F("\n"); + + for (uint8_t i = 0; i < request->args(); i++) + { + message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; + } + + return message; +} + +void handleNotFound(AsyncWebServerRequest * request) +{ + String message = createNotFoundBuffer(request); + request->send(404, F("text/plain"), message); +} + +void setup() +{ + Serial.begin(115200); + while (!Serial); + + delay(200); + + Serial.printf("\nStarting AsyncMultiWebServer_STM32 on %s with %s\n", BOARD_NAME, SHIELD_TYPE); + Serial.println(ASYNC_WEBSERVER_STM32_VERSION); + + // start the ethernet connection and the server + // Use random mac + uint16_t index = millis() % NUMBER_OF_MAC; + + // Use Static IP + //Ethernet.begin(mac[index], ip); + // Use DHCP dynamic IP and random mac + Ethernet.begin(mac[index]); + + Serial.print("\nConnected to network. IP = "); + Serial.println(Ethernet.localIP()); + + for (serverIndex = 0; serverIndex < NUM_SERVERS; serverIndex++) + { + multiServer[serverIndex] = new AsyncWebServer(http_port[serverIndex]); + + if (multiServer[serverIndex]) + { + Serial.printf("Initialize multiServer OK, serverIndex = %d, port = %d\n", serverIndex, http_port[serverIndex]); + } + else + { + Serial.printf("Error initialize multiServer, serverIndex = %d\n", serverIndex); + + while(1); + } + + multiServer[serverIndex]->on("/", HTTP_GET, [](AsyncWebServerRequest * request) + { + handleRoot(request); + }); + + multiServer[serverIndex]->on("/hello", HTTP_GET, [](AsyncWebServerRequest * request) + { + String message = F("Hello from AsyncWebServer using built-in LAN8742A Ethernet, running on "); + message += BOARD_NAME; + + request->send(200, "text/plain", message); + }); + + multiServer[serverIndex]->onNotFound([](AsyncWebServerRequest * request) + { + handleNotFound(request); + }); + + multiServer[serverIndex]->begin(); + + Serial.printf("HTTP server started at ports %d\n", http_port[serverIndex]); + } +} + +void loop() +{ +} diff --git a/examples/Async_AdvancedWebServer/Async_AdvancedWebServer.ino b/examples/Async_AdvancedWebServer/Async_AdvancedWebServer.ino index 6fdc82a..dfda7db 100644 --- a/examples/Async_AdvancedWebServer/Async_AdvancedWebServer.ino +++ b/examples/Async_AdvancedWebServer/Async_AdvancedWebServer.ino @@ -37,13 +37,14 @@ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - Version: 1.2.4 + Version: 1.2.5 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. *****************************************************************************************************************************/ /* Currently support @@ -60,7 +61,7 @@ #error This code is designed to run on STM32F/L/H/G/WB/MP1 platform! Please check your Tools->Board setting. #endif -#define _ASYNCWEBSERVER_STM32_LOGLEVEL_ 3 +#define _ASYNCWEBSERVER_STM32_LOGLEVEL_ 4 #if defined(STM32F0) #warning STM32F0 board selected @@ -113,6 +114,8 @@ #define BOARD_NAME BOARD_TYPE #endif +#define SHIELD_TYPE "LAN8742A built-in Ethernet" + #include #include #include @@ -239,7 +242,9 @@ void setup(void) digitalWrite(led, 0); Serial.begin(115200); - Serial.println("\nStart Async_AdvancedWebServer_STM32 on " + String(BOARD_NAME)); + + Serial.printf("\nStarting Async_AdvancedWebServer_STM32 on %s with %s\n", BOARD_NAME, SHIELD_TYPE); + Serial.println(ASYNC_WEBSERVER_STM32_VERSION); // start the ethernet connection and the server // Use random mac diff --git a/examples/Async_HelloServer/Async_HelloServer.ino b/examples/Async_HelloServer/Async_HelloServer.ino index 593791b..2f73035 100644 --- a/examples/Async_HelloServer/Async_HelloServer.ino +++ b/examples/Async_HelloServer/Async_HelloServer.ino @@ -9,13 +9,14 @@ Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 Licensed under MIT license - Version: 1.2.4 + Version: 1.2.5 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. *****************************************************************************************************************************/ /* Currently support @@ -83,6 +84,8 @@ #define BOARD_NAME BOARD_TYPE #endif +#define SHIELD_TYPE "LAN8742A built-in Ethernet" + #include #include #include @@ -156,7 +159,9 @@ void setup(void) digitalWrite(led, 0); Serial.begin(115200); - Serial.println("\nStart Async_HelloServer on " + String(BOARD_NAME)); + + Serial.printf("\nStarting Async_HelloServer on %s with %s\n", BOARD_NAME, SHIELD_TYPE); + Serial.println(ASYNC_WEBSERVER_STM32_VERSION); // start the ethernet connection and the server // Use random mac diff --git a/examples/Async_HelloServer2/Async_HelloServer2.ino b/examples/Async_HelloServer2/Async_HelloServer2.ino index a7a74c6..cd4c6cf 100644 --- a/examples/Async_HelloServer2/Async_HelloServer2.ino +++ b/examples/Async_HelloServer2/Async_HelloServer2.ino @@ -9,13 +9,14 @@ Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 Licensed under MIT license - Version: 1.2.4 + Version: 1.2.5 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. *****************************************************************************************************************************/ /* Currently support @@ -83,6 +84,8 @@ #define BOARD_NAME BOARD_TYPE #endif +#define SHIELD_TYPE "LAN8742A built-in Ethernet" + #include #include #include @@ -156,7 +159,9 @@ void setup(void) digitalWrite(led, 0); Serial.begin(115200); - Serial.println("\nStart Async_HelloServer2 on " + String(BOARD_NAME)); + + Serial.printf("\nStarting Async_HelloServer2 on %s with %s\n", BOARD_NAME, SHIELD_TYPE); + Serial.println(ASYNC_WEBSERVER_STM32_VERSION); // start the ethernet connection and the server // Use random mac diff --git a/examples/Async_HttpBasicAuth/Async_HttpBasicAuth.ino b/examples/Async_HttpBasicAuth/Async_HttpBasicAuth.ino index 1416daa..d78942e 100644 --- a/examples/Async_HttpBasicAuth/Async_HttpBasicAuth.ino +++ b/examples/Async_HttpBasicAuth/Async_HttpBasicAuth.ino @@ -9,13 +9,14 @@ Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 Licensed under MIT license - Version: 1.2.4 + Version: 1.2.5 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. *****************************************************************************************************************************/ /* Currently support @@ -86,6 +87,8 @@ #define BOARD_NAME BOARD_TYPE #endif +#define SHIELD_TYPE "LAN8742A built-in Ethernet" + #include #include #include @@ -127,8 +130,9 @@ const char* www_password = "ethernet"; void setup() { Serial.begin(115200); - delay(1000); - Serial.println("\nStart Async_HTTPBasicAuth on " + String(BOARD_NAME)); + + Serial.printf("\nStarting Async_HTTPBasicAuth on %s with %s\n", BOARD_NAME, SHIELD_TYPE); + Serial.println(ASYNC_WEBSERVER_STM32_VERSION); // start the ethernet connection and the server // Use random mac diff --git a/examples/Async_PostServer/Async_PostServer.ino b/examples/Async_PostServer/Async_PostServer.ino index 2c4f013..10433dc 100644 --- a/examples/Async_PostServer/Async_PostServer.ino +++ b/examples/Async_PostServer/Async_PostServer.ino @@ -9,13 +9,14 @@ Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 Licensed under MIT license - Version: 1.2.4 + Version: 1.2.5 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. *****************************************************************************************************************************/ /* Currently support @@ -83,6 +84,8 @@ #define BOARD_NAME BOARD_TYPE #endif +#define SHIELD_TYPE "LAN8742A built-in Ethernet" + #include #include #include @@ -211,7 +214,9 @@ void setup(void) digitalWrite(led, 0); Serial.begin(115200); - Serial.println("\nStart Async_PostServer on " + String(BOARD_NAME)); + + Serial.printf("\nStarting Async_PostServer on %s with %s\n", BOARD_NAME, SHIELD_TYPE); + Serial.println(ASYNC_WEBSERVER_STM32_VERSION); // start the ethernet connection and the server // Use random mac diff --git a/examples/Async_RegexPatterns_STM32/Async_RegexPatterns_STM32.ino b/examples/Async_RegexPatterns_STM32/Async_RegexPatterns_STM32.ino index afa5480..0dafbd9 100644 --- a/examples/Async_RegexPatterns_STM32/Async_RegexPatterns_STM32.ino +++ b/examples/Async_RegexPatterns_STM32/Async_RegexPatterns_STM32.ino @@ -9,13 +9,14 @@ Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 Licensed under MIT license - Version: 1.2.4 + Version: 1.2.5 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. *****************************************************************************************************************************/ /* Currently support @@ -102,6 +103,8 @@ #define BOARD_NAME BOARD_TYPE #endif +#define SHIELD_TYPE "LAN8742A built-in Ethernet" + #include #include #include @@ -149,7 +152,8 @@ void setup() Serial.begin(115200); while (!Serial); - Serial.println("\nStarting Async_RegexPatterns_STM32 on " + String(BOARD_NAME)); + Serial.printf("\nStarting Async_RegexPatterns_STM32 on %s with %s\n", BOARD_NAME, SHIELD_TYPE); + Serial.println(ASYNC_WEBSERVER_STM32_VERSION); // start the ethernet connection and the server // Use random mac diff --git a/examples/Async_SimpleWebServer_STM32/Async_SimpleWebServer_STM32.ino b/examples/Async_SimpleWebServer_STM32/Async_SimpleWebServer_STM32.ino index 97fedf1..b17fd68 100644 --- a/examples/Async_SimpleWebServer_STM32/Async_SimpleWebServer_STM32.ino +++ b/examples/Async_SimpleWebServer_STM32/Async_SimpleWebServer_STM32.ino @@ -9,13 +9,14 @@ Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 Licensed under MIT license - Version: 1.2.4 + Version: 1.2.5 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. *****************************************************************************************************************************/ /* Currently support @@ -89,6 +90,8 @@ #define BOARD_NAME BOARD_TYPE #endif +#define SHIELD_TYPE "LAN8742A built-in Ethernet" + #include #include #include @@ -136,7 +139,8 @@ void setup() Serial.begin(115200); while (!Serial); - Serial.println("\nStarting Async_SimpleWebServer_STM32 on " + String(BOARD_NAME)); + Serial.printf("\nStarting Async_SimpleWebServer_STM32 on %s with %s\n", BOARD_NAME, SHIELD_TYPE); + Serial.println(ASYNC_WEBSERVER_STM32_VERSION); // start the ethernet connection and the server // Use random mac diff --git a/examples/MQTTClient_Auth/MQTTClient_Auth.ino b/examples/MQTTClient_Auth/MQTTClient_Auth.ino new file mode 100644 index 0000000..6c3cf7b --- /dev/null +++ b/examples/MQTTClient_Auth/MQTTClient_Auth.ino @@ -0,0 +1,168 @@ +/**************************************************************************************************************************** + MQTTClient_Auth.ino - Dead simple MQTT Client for Ethernet shields + + For STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) + + AsyncWebServer_STM32 is a library for the STM32 run built-in Ethernet WebServer + + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 + Licensed under MIT license + + Version: 1.2.5 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). + Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 + 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. + *****************************************************************************************************************************/ + +/* + Basic MQTT example (without SSL!) with Authentication + This sketch demonstrates the basic capabilities of the library. + It connects to an MQTT server then: + - providing username and password + - publishes "hello world" to the topic "outTopic" + - subscribes to the topic "inTopic", printing out any messages + it receives. NB - it assumes the received payloads are strings not binary + It will reconnect to the server if the connection is lost using a blocking + reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to + achieve the same result without blocking the main loop. +*/ + +// To remove boolean warnings caused by PubSubClient library +#define boolean bool + +#include "defines.h" + +#include + +// Update these with values suitable for your network. +//const char* mqttServer = "broker.example"; // Broker address +const char* mqttServer = "broker.emqx.io"; // Broker address +//const char* mqttServer = "broker.shiftr.io"; // Broker address + +const char *ID = "MQTTClient_SSL-Client"; // Name of our device, must be unique +const char *TOPIC = "MQTT_Pub"; // Topic to subcribe to +const char *subTopic = "MQTT_Sub"; // Topic to subcribe to + +//IPAddress mqttServer(172, 16, 0, 2); + +void callback(char* topic, byte* payload, unsigned int length) +{ + Serial.print("Message arrived ["); + Serial.print(topic); + Serial.print("] "); + + for (unsigned int i = 0; i < length; i++) + { + Serial.print((char)payload[i]); + } + + Serial.println(); +} + +EthernetClient ethClient; +PubSubClient client(mqttServer, 1883, callback, ethClient); + +void reconnect() +{ + // Loop until we're reconnected + while (!client.connected()) + { + Serial.print("Attempting MQTT connection to "); + Serial.print(mqttServer); + + // Attempt to connect + if (client.connect("arduino", "try", "try")) + { + Serial.println("...connected"); + + // Once connected, publish an announcement... + String data = "Hello from MQTTClient_SSL on " + String(BOARD_NAME); + + client.publish(TOPIC, data.c_str()); + + //Serial.println("Published connection message successfully!"); + //Serial.print("Subcribed to: "); + //Serial.println(subTopic); + + client.subscribe(subTopic); + // for loopback testing + client.subscribe(TOPIC); + } + else + { + Serial.print("...failed, rc="); + Serial.print(client.state()); + Serial.println(" try again in 5 seconds"); + + // Wait 5 seconds before retrying + delay(5000); + } + } +} + +void setup() +{ + // Open serial communications and wait for port to open: + Serial.begin(115200); + while (!Serial); + + Serial.printf("\nStarting MQTTClient_Auth on %s with %s\n", BOARD_NAME, SHIELD_TYPE); + Serial.println(ASYNC_WEBSERVER_STM32_VERSION); + + // start the ethernet connection and the server + // Use random mac + uint16_t index = millis() % NUMBER_OF_MAC; + + // Use Static IP + //Ethernet.begin(mac[index], ip); + // Use DHCP dynamic IP and random mac + Ethernet.begin(mac[index]); + + Serial.print("\nConnected to network. IP = "); + Serial.println(Ethernet.localIP()); + + // Note - the default maximum packet size is 128 bytes. If the + // combined length of clientId, username and password exceed this use the + // following to increase the buffer size: + // client.setBufferSize(255); +} + +#define MQTT_PUBLISH_INTERVAL_MS 5000L + +String data = "Hello from MQTTClient_Auth on " + String(BOARD_NAME) + " with " + String(SHIELD_TYPE); +const char *pubData = data.c_str(); + +unsigned long lastMsg = 0; + +void loop() +{ + static unsigned long now; + + if (!client.connected()) + { + reconnect(); + } + + // Sending Data + now = millis(); + + if (now - lastMsg > MQTT_PUBLISH_INTERVAL_MS) + { + lastMsg = now; + + if (!client.publish(TOPIC, pubData)) + { + Serial.println("Message failed to send."); + } + + Serial.print("Message Send : " + String(TOPIC) + " => "); + Serial.println(data); + } + + client.loop(); +} diff --git a/examples/MQTTClient_Auth/defines.h b/examples/MQTTClient_Auth/defines.h new file mode 100644 index 0000000..15f9c6f --- /dev/null +++ b/examples/MQTTClient_Auth/defines.h @@ -0,0 +1,125 @@ +/**************************************************************************************************************************** + defines.h + + For STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) + + AsyncWebServer_STM32 is a library for the STM32 run built-in Ethernet WebServer + + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 + Licensed under MIT license + + Version: 1.2.5 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). + Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 + 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. + *****************************************************************************************************************************/ +/* + Currently support + 1) STM32 boards with built-in Ethernet (to use USE_BUILTIN_ETHERNET = true) such as : + - Nucleo-144 (F429ZI, F767ZI) + - Discovery (STM32F746G-DISCOVERY) + - STM32 boards (STM32F/L/H/G/WB/MP1) with 32K+ Flash, with Built-in Ethernet, + - See How To Use Built-in Ethernet at (https://github.com/khoih-prog/EthernetWebServer_STM32/issues/1) +*/ + +#ifndef defines_h +#define defines_h + +#if !( defined(STM32F0) || defined(STM32F1) || defined(STM32F2) || defined(STM32F3) ||defined(STM32F4) || defined(STM32F7) || \ + defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32H7) ||defined(STM32G0) || defined(STM32G4) || \ + defined(STM32WB) || defined(STM32MP1) ) + #error This code is designed to run on STM32F/L/H/G/WB/MP1 platform! Please check your Tools->Board setting. +#endif + +#if defined(STM32F0) + #warning STM32F0 board selected + #define BOARD_TYPE "STM32F0" +#elif defined(STM32F1) + #warning STM32F1 board selected + #define BOARD_TYPE "STM32F1" +#elif defined(STM32F2) + #warning STM32F2 board selected + #define BOARD_TYPE "STM32F2" +#elif defined(STM32F3) + #warning STM32F3 board selected + #define BOARD_TYPE "STM32F3" +#elif defined(STM32F4) + #warning STM32F4 board selected + #define BOARD_TYPE "STM32F4" +#elif defined(STM32F7) + #warning STM32F7 board selected + #define BOARD_TYPE "STM32F7" +#elif defined(STM32L0) + #warning STM32L0 board selected + #define BOARD_TYPE "STM32L0" +#elif defined(STM32L1) + #warning STM32L1 board selected + #define BOARD_TYPE "STM32L1" +#elif defined(STM32L4) + #warning STM32L4 board selected + #define BOARD_TYPE "STM32L4" +#elif defined(STM32H7) + #warning STM32H7 board selected + #define BOARD_TYPE "STM32H7" +#elif defined(STM32G0) + #warning STM32G0 board selected + #define BOARD_TYPE "STM32G0" +#elif defined(STM32G4) + #warning STM32G4 board selected + #define BOARD_TYPE "STM32G4" +#elif defined(STM32WB) + #warning STM32WB board selected + #define BOARD_TYPE "STM32WB" +#elif defined(STM32MP1) + #warning STM32MP1 board selected + #define BOARD_TYPE "STM32MP1" +#else + #warning STM32 unknown board selected + #define BOARD_TYPE "STM32 Unknown" +#endif + +#ifndef BOARD_NAME + #define BOARD_NAME BOARD_TYPE +#endif + +#define SHIELD_TYPE "LAN8742A built-in Ethernet" + +#include +#include +#include + +// Enter a MAC address and IP address for your controller below. +#define NUMBER_OF_MAC 20 + +byte mac[][NUMBER_OF_MAC] = +{ + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x01 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x02 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x03 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x04 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x05 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x06 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x07 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x08 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x09 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0A }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0B }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0C }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0D }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0E }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0F }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x10 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x11 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x12 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x13 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x14 }, +}; +// Select the IP address according to your local network +IPAddress ip(192, 168, 2, 232); + +#endif //defines_h diff --git a/examples/MQTTClient_Basic/MQTTClient_Basic.ino b/examples/MQTTClient_Basic/MQTTClient_Basic.ino new file mode 100644 index 0000000..1a7fa74 --- /dev/null +++ b/examples/MQTTClient_Basic/MQTTClient_Basic.ino @@ -0,0 +1,170 @@ +/**************************************************************************************************************************** + MQTTClient_Basic.ino - Dead simple MQTT Client for Ethernet shields + + For STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) + + AsyncWebServer_STM32 is a library for the STM32 run built-in Ethernet WebServer + + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 + Licensed under MIT license + + Version: 1.2.5 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). + Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 + 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. + *****************************************************************************************************************************/ + +/* + Basic MQTT example (without SSL!) with Authentication + This sketch demonstrates the basic capabilities of the library. + It connects to an MQTT server then: + - providing username and password + - publishes "hello world" to the topic "outTopic" + - subscribes to the topic "inTopic", printing out any messages + it receives. NB - it assumes the received payloads are strings not binary + + It will reconnect to the server if the connection is lost using a blocking + reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to + achieve the same result without blocking the main loop. +*/ + +// To remove boolean warnings caused by PubSubClient library +#define boolean bool + +#include "defines.h" + +#include + +// Update these with values suitable for your network. +//const char* mqttServer = "broker.example"; // Broker address +//const char* mqttServer = "broker.emqx.io"; // Broker address +const char* mqttServer = "broker.shiftr.io"; // Broker address + +const char *ID = "MQTTClient_SSL-Client"; // Name of our device, must be unique +const char *TOPIC = "MQTT_Pub"; // Topic to subcribe to +const char *subTopic = "MQTT_Sub"; // Topic to subcribe to + +//IPAddress mqttServer(172, 16, 0, 2); + +void callback(char* topic, byte* payload, unsigned int length) +{ + Serial.print("Message arrived ["); + Serial.print(topic); + Serial.print("] "); + + for (unsigned int i = 0; i < length; i++) + { + Serial.print((char)payload[i]); + } + + Serial.println(); +} + +EthernetClient ethClient; +PubSubClient client(mqttServer, 1883, callback, ethClient); + +void reconnect() +{ + // Loop until we're reconnected + while (!client.connected()) + { + Serial.print("Attempting MQTT connection to "); + Serial.print(mqttServer); + + // Attempt to connect + if (client.connect(ID, "try", "try")) + { + Serial.println("...connected"); + + // Once connected, publish an announcement... + String data = "Hello from MQTTClient_SSL on " + String(BOARD_NAME); + + client.publish(TOPIC, data.c_str()); + + //Serial.println("Published connection message successfully!"); + //Serial.print("Subcribed to: "); + //Serial.println(subTopic); + + client.subscribe(subTopic); + // for loopback testing + client.subscribe(TOPIC); + } + else + { + Serial.print("...failed, rc="); + Serial.print(client.state()); + Serial.println(" try again in 5 seconds"); + + // Wait 5 seconds before retrying + delay(5000); + } + } +} + +void setup() +{ + // Open serial communications and wait for port to open: + Serial.begin(115200); + while (!Serial); + + Serial.printf("\nStarting MQTTClient_Basic on %s with %s\n", BOARD_NAME, SHIELD_TYPE); + Serial.println(ASYNC_WEBSERVER_STM32_VERSION); + + // start the ethernet connection and the server + // Use random mac + uint16_t index = millis() % NUMBER_OF_MAC; + + // Use Static IP + //Ethernet.begin(mac[index], ip); + // Use DHCP dynamic IP and random mac + Ethernet.begin(mac[index]); + + Serial.print("\nConnected to network. IP = "); + Serial.println(Ethernet.localIP()); + + client.setServer(mqttServer, 1883); + client.setCallback(callback); + + // Allow the hardware to sort itself out + delay(1500); +} + +#define MQTT_PUBLISH_INTERVAL_MS 5000L + +String data = "Hello from MQTTClient_Basic on " + String(BOARD_NAME) + " with " + String(SHIELD_TYPE); +const char *pubData = data.c_str(); + +unsigned long lastMsg = 0; + +void loop() +{ + static unsigned long now; + + if (!client.connected()) + { + reconnect(); + } + + // Sending Data + now = millis(); + + if (now - lastMsg > MQTT_PUBLISH_INTERVAL_MS) + { + lastMsg = now; + + if (!client.publish(TOPIC, pubData)) + { + Serial.println("Message failed to send."); + } + + Serial.print("Message Send : " + String(TOPIC) + " => "); + Serial.println(data); + } + + client.loop(); +} diff --git a/examples/MQTTClient_Basic/defines.h b/examples/MQTTClient_Basic/defines.h new file mode 100644 index 0000000..15f9c6f --- /dev/null +++ b/examples/MQTTClient_Basic/defines.h @@ -0,0 +1,125 @@ +/**************************************************************************************************************************** + defines.h + + For STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) + + AsyncWebServer_STM32 is a library for the STM32 run built-in Ethernet WebServer + + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 + Licensed under MIT license + + Version: 1.2.5 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). + Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 + 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. + *****************************************************************************************************************************/ +/* + Currently support + 1) STM32 boards with built-in Ethernet (to use USE_BUILTIN_ETHERNET = true) such as : + - Nucleo-144 (F429ZI, F767ZI) + - Discovery (STM32F746G-DISCOVERY) + - STM32 boards (STM32F/L/H/G/WB/MP1) with 32K+ Flash, with Built-in Ethernet, + - See How To Use Built-in Ethernet at (https://github.com/khoih-prog/EthernetWebServer_STM32/issues/1) +*/ + +#ifndef defines_h +#define defines_h + +#if !( defined(STM32F0) || defined(STM32F1) || defined(STM32F2) || defined(STM32F3) ||defined(STM32F4) || defined(STM32F7) || \ + defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32H7) ||defined(STM32G0) || defined(STM32G4) || \ + defined(STM32WB) || defined(STM32MP1) ) + #error This code is designed to run on STM32F/L/H/G/WB/MP1 platform! Please check your Tools->Board setting. +#endif + +#if defined(STM32F0) + #warning STM32F0 board selected + #define BOARD_TYPE "STM32F0" +#elif defined(STM32F1) + #warning STM32F1 board selected + #define BOARD_TYPE "STM32F1" +#elif defined(STM32F2) + #warning STM32F2 board selected + #define BOARD_TYPE "STM32F2" +#elif defined(STM32F3) + #warning STM32F3 board selected + #define BOARD_TYPE "STM32F3" +#elif defined(STM32F4) + #warning STM32F4 board selected + #define BOARD_TYPE "STM32F4" +#elif defined(STM32F7) + #warning STM32F7 board selected + #define BOARD_TYPE "STM32F7" +#elif defined(STM32L0) + #warning STM32L0 board selected + #define BOARD_TYPE "STM32L0" +#elif defined(STM32L1) + #warning STM32L1 board selected + #define BOARD_TYPE "STM32L1" +#elif defined(STM32L4) + #warning STM32L4 board selected + #define BOARD_TYPE "STM32L4" +#elif defined(STM32H7) + #warning STM32H7 board selected + #define BOARD_TYPE "STM32H7" +#elif defined(STM32G0) + #warning STM32G0 board selected + #define BOARD_TYPE "STM32G0" +#elif defined(STM32G4) + #warning STM32G4 board selected + #define BOARD_TYPE "STM32G4" +#elif defined(STM32WB) + #warning STM32WB board selected + #define BOARD_TYPE "STM32WB" +#elif defined(STM32MP1) + #warning STM32MP1 board selected + #define BOARD_TYPE "STM32MP1" +#else + #warning STM32 unknown board selected + #define BOARD_TYPE "STM32 Unknown" +#endif + +#ifndef BOARD_NAME + #define BOARD_NAME BOARD_TYPE +#endif + +#define SHIELD_TYPE "LAN8742A built-in Ethernet" + +#include +#include +#include + +// Enter a MAC address and IP address for your controller below. +#define NUMBER_OF_MAC 20 + +byte mac[][NUMBER_OF_MAC] = +{ + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x01 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x02 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x03 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x04 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x05 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x06 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x07 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x08 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x09 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0A }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0B }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0C }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0D }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0E }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0F }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x10 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x11 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x12 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x13 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x14 }, +}; +// Select the IP address according to your local network +IPAddress ip(192, 168, 2, 232); + +#endif //defines_h diff --git a/examples/MQTT_ThingStream/MQTT_ThingStream.ino b/examples/MQTT_ThingStream/MQTT_ThingStream.ino new file mode 100644 index 0000000..5ae7873 --- /dev/null +++ b/examples/MQTT_ThingStream/MQTT_ThingStream.ino @@ -0,0 +1,221 @@ +/**************************************************************************************************************************** + MQTT_ThingStream.ino - Dead simple MQTT Client for Ethernet shields + + For STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) + + AsyncWebServer_STM32 is a library for the STM32 run built-in Ethernet WebServer + + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 + Licensed under MIT license + + Version: 1.2.5 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). + Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 + 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. + *****************************************************************************************************************************/ +/* + Basic MQTT example (without SSL!) + This sketch demonstrates the basic capabilities of the library. + It connects to an MQTT server then: + - publishes {Hello from MQTTClient_SSL on NUCLEO_F767ZI} to the topic [STM32_Pub] + - subscribes to the topic [STM32_Sub], printing out any messages + it receives. NB - it assumes the received payloads are strings not binary + It will reconnect to the server if the connection is lost using a blocking + reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to + achieve the same result without blocking the main loop. + + You will need to populate "certificates.h" with your trust anchors + (see https://github.com/OPEnSLab-OSU/SSLClient/blob/master/TrustAnchors.md) + and my_cert/my_key with your certificate/private key pair + (see https://github.com/OPEnSLab-OSU/SSLClient#mtls). +*/ + +// To remove boolean warnings caused by PubSubClient library +#define boolean bool + +#include "defines.h" + +#include + +const char my_cert[] = "FIXME"; +const char my_key[] = "FIXME"; + +#define USING_THINGSTREAM_IO false //true + +#if USING_THINGSTREAM_IO + +const char *MQTT_PREFIX_TOPIC = "esp32-sniffer/"; +const char *MQTT_ANNOUNCE_TOPIC = "/status"; +const char *MQTT_CONTROL_TOPIC = "/control"; +const char *MQTT_BLE_TOPIC = "/ble"; + + +// GOT FROM ThingsStream! +const char *MQTT_SERVER = "mqtt.thingstream.io"; +const char *MQTT_USER = "MQTT_USER"; +const char *MQTT_PASS = "MQTT_PASS"; +const char *MQTT_CLIENT_ID = "MQTT_CLIENT_ID"; + +String topic = MQTT_PREFIX_TOPIC + String("12345678") + MQTT_BLE_TOPIC; +String subTopic = MQTT_PREFIX_TOPIC + String("12345678") + MQTT_BLE_TOPIC; + +#else + +const char* MQTT_SERVER = "broker.emqx.io"; // Broker address + +const char* ID = "MQTTClient_SSL-Client"; // Name of our device, must be unique +String topic = "STM32_Pub"; // Topic to subcribe to +String subTopic = "STM32_Sub"; // Topic to subcribe to + +#endif + +void mqtt_receive_callback(char* topic, byte* payload, unsigned int length); + +const int MQTT_PORT = 1883; //if you use SSL //1883 no SSL + +unsigned long lastMsg = 0; + +// Initialize the SSL client library +// Arguments: EthernetClient, our trust anchors + + +EthernetClient ethClient; + +PubSubClient client(MQTT_SERVER, MQTT_PORT, mqtt_receive_callback, ethClient); + +/* + Called whenever a payload is received from a subscribed MQTT topic +*/ +void mqtt_receive_callback(char* topic, byte* payload, unsigned int length) +{ + Serial.print("MQTT Message receive ["); + Serial.print(topic); + Serial.print("] "); + + for (unsigned int i = 0; i < length; i++) + { + Serial.print((char)payload[i]); + } + + Serial.println(); +} + +void reconnect() +{ + // Loop until we're reconnected + while (!client.connected()) + { + Serial.print("Attempting MQTT connection to "); + Serial.println(MQTT_SERVER); + + // Attempt to connect + +#if USING_THINGSTREAM_IO + int connect_status = client.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS, topic.c_str(), 2, false, ""); +#else + int connect_status = client.connect(ID); +#endif + + if (connect_status) + { + Serial.println("...connected"); + + // Once connected, publish an announcement... + String data = "Hello from MQTTClient_SSL on " + String(BOARD_NAME); + + client.publish(topic.c_str(), data.c_str()); + + Serial.println("Published connection message successfully!"); + + Serial.print("Subcribed to: "); + Serial.println(subTopic); + + // This is a workaround to address https://github.com/OPEnSLab-OSU/SSLClient/issues/9 + //ethClientSSL.flush(); + // ... and resubscribe + client.subscribe(subTopic.c_str()); + // for loopback testing + client.subscribe(topic.c_str()); + // This is a workaround to address https://github.com/OPEnSLab-OSU/SSLClient/issues/9 + //ethClientSSL.flush(); + } + else + { + Serial.print("failed, rc="); + Serial.print(client.state()); + Serial.println(" try again in 5 seconds"); + + // Wait 5 seconds before retrying + delay(5000); + } + } +} + +void setup() +{ + // Open serial communications and wait for port to open: + Serial.begin(115200); + while (!Serial); + + Serial.printf("\nStarting MQTT_ThingStream on %s with %s\n", BOARD_NAME, SHIELD_TYPE); + Serial.println(ASYNC_WEBSERVER_STM32_VERSION); + + // start the ethernet connection and the server + // Use random mac + uint16_t index = millis() % NUMBER_OF_MAC; + + // Use Static IP + //Ethernet.begin(mac[index], ip); + // Use DHCP dynamic IP and random mac + Ethernet.begin(mac[index]); + + Serial.print("\nConnected to network. IP = "); + Serial.println(Ethernet.localIP()); + + // Note - the default maximum packet size is 256 bytes. If the + // combined length of clientId, username and password exceed this use the + // following to increase the buffer size: + //client.setBufferSize(256); + + Serial.println("***************************************"); + Serial.println(topic); + Serial.println("***************************************"); +} + +#define MQTT_PUBLISH_INTERVAL_MS 5000L + +String data = "Hello from MQTT_ThingStream on " + String(BOARD_NAME) + " with " + String(SHIELD_TYPE); +const char *pubData = data.c_str(); + +void loop() +{ + static unsigned long now; + + if (!client.connected()) + { + reconnect(); + } + + // Sending Data + now = millis(); + + if (now - lastMsg > MQTT_PUBLISH_INTERVAL_MS) + { + lastMsg = now; + + if (!client.publish(topic.c_str(), pubData)) + { + Serial.println("Message failed to send."); + } + + Serial.print("MQTT Message Send : " + topic + " => "); + Serial.println(data); + } + + client.loop(); +} diff --git a/examples/MQTT_ThingStream/defines.h b/examples/MQTT_ThingStream/defines.h new file mode 100644 index 0000000..15f9c6f --- /dev/null +++ b/examples/MQTT_ThingStream/defines.h @@ -0,0 +1,125 @@ +/**************************************************************************************************************************** + defines.h + + For STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) + + AsyncWebServer_STM32 is a library for the STM32 run built-in Ethernet WebServer + + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 + Licensed under MIT license + + Version: 1.2.5 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). + Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 + 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. + *****************************************************************************************************************************/ +/* + Currently support + 1) STM32 boards with built-in Ethernet (to use USE_BUILTIN_ETHERNET = true) such as : + - Nucleo-144 (F429ZI, F767ZI) + - Discovery (STM32F746G-DISCOVERY) + - STM32 boards (STM32F/L/H/G/WB/MP1) with 32K+ Flash, with Built-in Ethernet, + - See How To Use Built-in Ethernet at (https://github.com/khoih-prog/EthernetWebServer_STM32/issues/1) +*/ + +#ifndef defines_h +#define defines_h + +#if !( defined(STM32F0) || defined(STM32F1) || defined(STM32F2) || defined(STM32F3) ||defined(STM32F4) || defined(STM32F7) || \ + defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32H7) ||defined(STM32G0) || defined(STM32G4) || \ + defined(STM32WB) || defined(STM32MP1) ) + #error This code is designed to run on STM32F/L/H/G/WB/MP1 platform! Please check your Tools->Board setting. +#endif + +#if defined(STM32F0) + #warning STM32F0 board selected + #define BOARD_TYPE "STM32F0" +#elif defined(STM32F1) + #warning STM32F1 board selected + #define BOARD_TYPE "STM32F1" +#elif defined(STM32F2) + #warning STM32F2 board selected + #define BOARD_TYPE "STM32F2" +#elif defined(STM32F3) + #warning STM32F3 board selected + #define BOARD_TYPE "STM32F3" +#elif defined(STM32F4) + #warning STM32F4 board selected + #define BOARD_TYPE "STM32F4" +#elif defined(STM32F7) + #warning STM32F7 board selected + #define BOARD_TYPE "STM32F7" +#elif defined(STM32L0) + #warning STM32L0 board selected + #define BOARD_TYPE "STM32L0" +#elif defined(STM32L1) + #warning STM32L1 board selected + #define BOARD_TYPE "STM32L1" +#elif defined(STM32L4) + #warning STM32L4 board selected + #define BOARD_TYPE "STM32L4" +#elif defined(STM32H7) + #warning STM32H7 board selected + #define BOARD_TYPE "STM32H7" +#elif defined(STM32G0) + #warning STM32G0 board selected + #define BOARD_TYPE "STM32G0" +#elif defined(STM32G4) + #warning STM32G4 board selected + #define BOARD_TYPE "STM32G4" +#elif defined(STM32WB) + #warning STM32WB board selected + #define BOARD_TYPE "STM32WB" +#elif defined(STM32MP1) + #warning STM32MP1 board selected + #define BOARD_TYPE "STM32MP1" +#else + #warning STM32 unknown board selected + #define BOARD_TYPE "STM32 Unknown" +#endif + +#ifndef BOARD_NAME + #define BOARD_NAME BOARD_TYPE +#endif + +#define SHIELD_TYPE "LAN8742A built-in Ethernet" + +#include +#include +#include + +// Enter a MAC address and IP address for your controller below. +#define NUMBER_OF_MAC 20 + +byte mac[][NUMBER_OF_MAC] = +{ + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x01 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x02 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x03 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x04 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x05 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x06 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x07 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x08 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x09 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0A }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0B }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0C }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0D }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0E }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0F }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x10 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x11 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x12 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x13 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x14 }, +}; +// Select the IP address according to your local network +IPAddress ip(192, 168, 2, 232); + +#endif //defines_h diff --git a/examples/WebClient/WebClient.ino b/examples/WebClient/WebClient.ino index b3867e9..258a2f9 100644 --- a/examples/WebClient/WebClient.ino +++ b/examples/WebClient/WebClient.ino @@ -9,13 +9,14 @@ Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 Licensed under MIT license - Version: 1.2.4 + Version: 1.2.5 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. *****************************************************************************************************************************/ /* Currently support @@ -39,7 +40,8 @@ void setup() Serial.begin(115200); while (!Serial); - Serial.println("\nStart WebClient on " + String(BOARD_NAME)); + Serial.printf("\nStarting WebClient on %s with %s\n", BOARD_NAME, SHIELD_TYPE); + Serial.println(ASYNC_WEBSERVER_STM32_VERSION); // start the ethernet connection and the server // Use random mac diff --git a/examples/WebClient/defines.h b/examples/WebClient/defines.h index 77114f0..15f9c6f 100644 --- a/examples/WebClient/defines.h +++ b/examples/WebClient/defines.h @@ -9,11 +9,14 @@ Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 Licensed under MIT license - Version: 1.0.0 + Version: 1.2.5 Version Modified By Date Comments ------- ----------- ---------- ----------- - 1.0.0 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc) + 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). + Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 + 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. *****************************************************************************************************************************/ /* Currently support @@ -84,9 +87,11 @@ #define BOARD_NAME BOARD_TYPE #endif +#define SHIELD_TYPE "LAN8742A built-in Ethernet" + #include #include -//#include +#include // Enter a MAC address and IP address for your controller below. #define NUMBER_OF_MAC 20 diff --git a/examples/WebClientRepeating/WebClientRepeating.ino b/examples/WebClientRepeating/WebClientRepeating.ino index e401b6e..84545cc 100644 --- a/examples/WebClientRepeating/WebClientRepeating.ino +++ b/examples/WebClientRepeating/WebClientRepeating.ino @@ -9,13 +9,14 @@ Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 Licensed under MIT license - Version: 1.2.4 + Version: 1.2.5 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. *****************************************************************************************************************************/ /* Currently support @@ -72,7 +73,8 @@ void setup() Serial.begin(115200); while (!Serial); - Serial.println("\nStart WebClientRepeating on " + String(BOARD_NAME)); + Serial.printf("\nStarting WebClientRepeating on %s with %s\n", BOARD_NAME, SHIELD_TYPE); + Serial.println(ASYNC_WEBSERVER_STM32_VERSION); // start the ethernet connection and the server // Use random mac diff --git a/examples/WebClientRepeating/defines.h b/examples/WebClientRepeating/defines.h index 745c739..15f9c6f 100644 --- a/examples/WebClientRepeating/defines.h +++ b/examples/WebClientRepeating/defines.h @@ -9,11 +9,14 @@ Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 Licensed under MIT license - Version: 1.0.0 + Version: 1.2.5 Version Modified By Date Comments ------- ----------- ---------- ----------- - 1.0.0 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc) + 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). + Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 + 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. *****************************************************************************************************************************/ /* Currently support @@ -84,8 +87,11 @@ #define BOARD_NAME BOARD_TYPE #endif +#define SHIELD_TYPE "LAN8742A built-in Ethernet" + #include #include +#include // Enter a MAC address and IP address for your controller below. #define NUMBER_OF_MAC 20 diff --git a/library.json b/library.json index 7219b03..cda0c45 100644 --- a/library.json +++ b/library.json @@ -1,8 +1,8 @@ { "name":"AsyncWebServer_STM32", - "version": "1.2.4", - "description":"Asynchronous HTTP and WebSocket Server Library for STM32 using builtin LAN8742A Ethernet", - "keywords":"http,async,websocket,webserver,stm32,ethernet", + "version": "1.2.5", + "description":"Asynchronous HTTP and WebSocket Server Library for STM32F/L/H/G/WB/MP1 using LAN8742A Ethernet", + "keywords":"http,async,websocket,webserver,stm32f,stm32l,stm32h,stm32g,stm32wb,stm32mp1,ethernet,lan8742a", "authors": [ { diff --git a/library.properties b/library.properties index 07ef708..91b1200 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=AsyncWebServer_STM32 -version=1.2.4 +version=1.2.5 author=Hristo Gochkov,Khoi Hoang maintainer=Khoi Hoang license=MIT @@ -9,3 +9,4 @@ category=Communication,AsyncWebServer url=https://github.com/khoih-prog/AsyncWebServer_STM32 architectures=stm32 depends=STM32duino LwIP,STM32duino STM32Ethernet,STM32AsyncTCP +includes=LwIP.h,STM32Ethernet.h,AsyncWebServer_STM32.h diff --git a/pics/AsyncMultiWebServer_STM32_SVR1.png b/pics/AsyncMultiWebServer_STM32_SVR1.png new file mode 100644 index 0000000000000000000000000000000000000000..9b09e6fb1c908021352e42a16fb7b84d53a93922 GIT binary patch literal 33016 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 KRUv8e?Y{w%i3&yl literal 0 HcmV?d00001 diff --git a/pics/AsyncMultiWebServer_STM32_SVR2.png b/pics/AsyncMultiWebServer_STM32_SVR2.png new file mode 100644 index 0000000000000000000000000000000000000000..915bec36fbf0d855523d0b016e9461a0d2f47b44 GIT binary patch literal 33093 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 G1^pi+J_}X= literal 0 HcmV?d00001 diff --git a/pics/AsyncMultiWebServer_STM32_SVR3.png b/pics/AsyncMultiWebServer_STM32_SVR3.png new file mode 100644 index 0000000000000000000000000000000000000000..33ea378323acdc6020a080a004a33d11b8bde9db GIT binary patch literal 33945 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 diff --git a/src/AsyncWebSocket_STM32.cpp b/src/AsyncWebSocket_STM32.cpp index 69f1252..29fb5fb 100644 --- a/src/AsyncWebSocket_STM32.cpp +++ b/src/AsyncWebSocket_STM32.cpp @@ -9,13 +9,14 @@ Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 Licensed under MIT license - Version: 1.2.4 + Version: 1.2.5 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. *****************************************************************************************************************************/ #include "Arduino.h" diff --git a/src/AsyncWebSocket_STM32.h b/src/AsyncWebSocket_STM32.h index 5a6e4ed..d85efb4 100644 --- a/src/AsyncWebSocket_STM32.h +++ b/src/AsyncWebSocket_STM32.h @@ -9,14 +9,17 @@ Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 Licensed under MIT license - Version: 1.2.4 + Version: 1.2.5 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 05/09/2020 Suppress all possible compiler warnings. Add examples. *****************************************************************************************************************************/ + +#pragma once #ifndef ASYNCWEBSOCKET_STM32_H_ #define ASYNCWEBSOCKET_STM32_H_ diff --git a/src/AsyncWebSynchronization_STM32.h b/src/AsyncWebSynchronization_STM32.h index 75e223a..76ca362 100644 --- a/src/AsyncWebSynchronization_STM32.h +++ b/src/AsyncWebSynchronization_STM32.h @@ -9,15 +9,18 @@ Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 Licensed under MIT license - Version: 1.2.4 + Version: 1.2.5 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. *****************************************************************************************************************************/ +#pragma once + #ifndef ASYNCWEBSYNCHRONIZATION_STM32_H_ #define ASYNCWEBSYNCHRONIZATION_STM32_H_ diff --git a/src/Crypto/Hash.h b/src/Crypto/Hash.h index 2634d6d..e9151ff 100644 --- a/src/Crypto/Hash.h +++ b/src/Crypto/Hash.h @@ -22,6 +22,8 @@ * */ +#pragma once + #ifndef HASH_H_ #define HASH_H_ diff --git a/src/Crypto/bearssl_hash.h b/src/Crypto/bearssl_hash.h index b9bc040..2c38df3 100644 --- a/src/Crypto/bearssl_hash.h +++ b/src/Crypto/bearssl_hash.h @@ -22,6 +22,8 @@ * SOFTWARE. */ +#pragma once + #ifndef BR_BEARSSL_HASH_H__ #define BR_BEARSSL_HASH_H__ diff --git a/src/Crypto/md5.h b/src/Crypto/md5.h index e3a6005..67ba0a9 100644 --- a/src/Crypto/md5.h +++ b/src/Crypto/md5.h @@ -33,6 +33,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#pragma once + #ifndef LWIP_INCLUDED_POLARSSL_MD5_H #define LWIP_INCLUDED_POLARSSL_MD5_H diff --git a/src/Crypto/sha1.h b/src/Crypto/sha1.h index 15d13f7..2e2329d 100644 --- a/src/Crypto/sha1.h +++ b/src/Crypto/sha1.h @@ -33,6 +33,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#pragma once + #ifndef LWIP_INCLUDED_POLARSSL_SHA1_H #define LWIP_INCLUDED_POLARSSL_SHA1_H diff --git a/src/StringArray_STM32.h b/src/StringArray_STM32.h index b4522a0..b68b3a9 100644 --- a/src/StringArray_STM32.h +++ b/src/StringArray_STM32.h @@ -9,14 +9,17 @@ Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 Licensed under MIT license - Version: 1.2.4 + Version: 1.2.5 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. *****************************************************************************************************************************/ + +#pragma once #ifndef STRINGARRAY_STM32_H_ #define STRINGARRAY_STM32_H_ diff --git a/src/libb64/cdecode.c b/src/libb64/cdecode.c index d6f774a..a426437 100644 --- a/src/libb64/cdecode.c +++ b/src/libb64/cdecode.c @@ -1,10 +1,10 @@ /**************************************************************************************************************************** - cdecode.c - c source to a base64 decoding algorithm implementation + cdecode.c - c source to a base64 decoding algorithm implementation - This is part of the libb64 project, and has been placed in the public domain. - For details, see http://sourceforge.net/projects/libb64 + This is part of the libb64 project, and has been placed in the public domain. + For details, see http://sourceforge.net/projects/libb64 - EFor STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) + EFor STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) AsyncWebServer_STM32 is a library for the STM32 run built-in Ethernet WebServer @@ -12,96 +12,138 @@ Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 Licensed under MIT license - Version: 1.2.4 + Version: 1.2.5 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. *****************************************************************************************************************************/ #include "cdecode.h" -int base64_decode_value(char value_in) { - static const char decoding[] = { 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, -1, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, - 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 +int base64_decode_value(char value_in) +{ + static const char decoding[] = {62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, -1, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, + -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51 }; - static const char decoding_size = sizeof(decoding); - value_in -= 43; - if (value_in < 0 || value_in > decoding_size) return -1; - return decoding[(int)value_in]; + + int newValue = (int) value_in - 43; + //value_in -= 43; + if (newValue < 0 || newValue > decoding_size) return -1; + return decoding[newValue]; } -void base64_init_decodestate(base64_decodestate* state_in) { +void base64_init_decodestate(base64_decodestate* state_in) +{ state_in->step = step_a; state_in->plainchar = 0; } -int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in) { + +int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in) +{ const char* codechar = code_in; char* plainchar = plaintext_out; - char fragment; + //char fragment; + int fragment; *plainchar = state_in->plainchar; - switch (state_in->step) { - while (1) { + switch (state_in->step) + { + while (1) + { case step_a: - do { - if (codechar == code_in + length_in) { + do + { + if (codechar == code_in + length_in) + { state_in->step = step_a; state_in->plainchar = *plainchar; return plainchar - plaintext_out; } - fragment = (char)base64_decode_value(*codechar++); + + fragment = base64_decode_value(*codechar++); } while (fragment < 0); + *plainchar = (fragment & 0x03f) << 2; + + break; + case step_b: - do { - if (codechar == code_in + length_in) { + do + { + if (codechar == code_in + length_in) + { state_in->step = step_b; state_in->plainchar = *plainchar; return plainchar - plaintext_out; } - fragment = (char)base64_decode_value(*codechar++); + + fragment = base64_decode_value(*codechar++); } while (fragment < 0); + *plainchar++ |= (fragment & 0x030) >> 4; *plainchar = (fragment & 0x00f) << 4; + + break; + case step_c: - do { - if (codechar == code_in + length_in) { + do + { + if (codechar == code_in + length_in) + { state_in->step = step_c; state_in->plainchar = *plainchar; return plainchar - plaintext_out; } - fragment = (char)base64_decode_value(*codechar++); + + fragment = base64_decode_value(*codechar++); } while (fragment < 0); + *plainchar++ |= (fragment & 0x03c) >> 2; *plainchar = (fragment & 0x003) << 6; + + break; + case step_d: - do { - if (codechar == code_in + length_in) { + do + { + if (codechar == code_in + length_in) + { state_in->step = step_d; state_in->plainchar = *plainchar; return plainchar - plaintext_out; } - fragment = (char)base64_decode_value(*codechar++); + + fragment = base64_decode_value(*codechar++); } while (fragment < 0); + *plainchar++ |= (fragment & 0x03f); + + break; } + } + /* control should not reach here */ return plainchar - plaintext_out; } -int base64_decode_chars(const char* code_in, const int length_in, char* plaintext_out) { +int base64_decode_chars(const char* code_in, const int length_in, char* plaintext_out) +{ base64_decodestate _state; base64_init_decodestate(&_state); int len = base64_decode_block(code_in, length_in, plaintext_out, &_state); - if (len > 0) plaintext_out[len] = 0; + + if (len > 0) + plaintext_out[len] = 0; + return len; } diff --git a/src/libb64/cdecode.h b/src/libb64/cdecode.h index eb645cc..4d60022 100644 --- a/src/libb64/cdecode.h +++ b/src/libb64/cdecode.h @@ -1,10 +1,10 @@ /**************************************************************************************************************************** - cdecode.h - c header for a base64 decoding algorithm + cdecode.h - c header for a base64 decoding algorithm - This is part of the libb64 project, and has been placed in the public domain. - For details, see http://sourceforge.net/projects/libb64 + This is part of the libb64 project, and has been placed in the public domain. + For details, see http://sourceforge.net/projects/libb64 - For STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) + For STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) AsyncWebServer_STM32 is a library for the STM32 run built-in Ethernet WebServer @@ -12,15 +12,20 @@ Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 Licensed under MIT license - Version: 1.2.4 + Version: 1.2.5 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. *****************************************************************************************************************************/ +#pragma once + +// Reintroduce to prevent duplication compile error if other lib/core already has LIB64 +// pragma once can't prevent that #ifndef BASE64_CDECODE_H #define BASE64_CDECODE_H diff --git a/src/libb64/cencode.c b/src/libb64/cencode.c index 1890d9a..f08a113 100644 --- a/src/libb64/cencode.c +++ b/src/libb64/cencode.c @@ -1,10 +1,10 @@ /**************************************************************************************************************************** - cencode.c - c source to a base64 encoding algorithm implementation + cencode.c - c source to a base64 encoding algorithm implementation - This is part of the libb64 project, and has been placed in the public domain. - For details, see http://sourceforge.net/projects/libb64 + This is part of the libb64 project, and has been placed in the public domain. + For details, see http://sourceforge.net/projects/libb64 - For STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) + For STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) AsyncWebServer_STM32 is a library for the STM32 run built-in Ethernet WebServer @@ -12,68 +12,92 @@ Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 Licensed under MIT license - Version: 1.2.4 + Version: 1.2.5 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. *****************************************************************************************************************************/ #include "cencode.h" const int CHARS_PER_LINE = 72; -void base64_init_encodestate(base64_encodestate* state_in) { +void base64_init_encodestate(base64_encodestate* state_in) +{ state_in->step = step_A; state_in->result = 0; state_in->stepcount = 0; } -char base64_encode_value(char value_in) { +char base64_encode_value(char value_in) +{ static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - if (value_in > 63) return '='; + + if (value_in > 63) + return '='; + return encoding[(int)value_in]; } -int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in) { +int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in) +{ const char* plainchar = plaintext_in; const char* const plaintextend = plaintext_in + length_in; char* codechar = code_out; - char result; - char fragment; + char result; + char fragment; result = state_in->result; - switch (state_in->step) { - while (1) { + switch (state_in->step) + { + while (1) + { case step_A: - if (plainchar == plaintextend) { + + if (plainchar == plaintextend) + { state_in->result = result; state_in->step = step_A; return codechar - code_out; } + fragment = *plainchar++; result = (fragment & 0x0fc) >> 2; *codechar++ = base64_encode_value(result); result = (fragment & 0x003) << 4; + + break; + case step_B: - if (plainchar == plaintextend) { + + if (plainchar == plaintextend) + { state_in->result = result; state_in->step = step_B; return codechar - code_out; } + fragment = *plainchar++; result |= (fragment & 0x0f0) >> 4; *codechar++ = base64_encode_value(result); result = (fragment & 0x00f) << 2; + + break; + case step_C: - if (plainchar == plaintextend) { + + if (plainchar == plaintextend) + { state_in->result = result; state_in->step = step_C; return codechar - code_out; } + fragment = *plainchar++; result |= (fragment & 0x0c0) >> 6; *codechar++ = base64_encode_value(result); @@ -81,20 +105,27 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, *codechar++ = base64_encode_value(result); ++(state_in->stepcount); - if (state_in->stepcount == CHARS_PER_LINE / 4) { + + if (state_in->stepcount == CHARS_PER_LINE / 4) + { *codechar++ = '\n'; state_in->stepcount = 0; } + + break; } } + /* control should not reach here */ return codechar - code_out; } -int base64_encode_blockend(char* code_out, base64_encodestate* state_in) { +int base64_encode_blockend(char* code_out, base64_encodestate* state_in) +{ char* codechar = code_out; - switch (state_in->step) { + switch (state_in->step) + { case step_B: *codechar++ = base64_encode_value(state_in->result); *codechar++ = '='; @@ -107,14 +138,17 @@ int base64_encode_blockend(char* code_out, base64_encodestate* state_in) { case step_A: break; } + *codechar = 0x00; return codechar - code_out; } -int base64_encode_chars(const char* plaintext_in, int length_in, char* code_out) { +int base64_encode_chars(const char* plaintext_in, int length_in, char* code_out) +{ base64_encodestate _state; base64_init_encodestate(&_state); int len = base64_encode_block(plaintext_in, length_in, code_out, &_state); - return len + base64_encode_blockend((code_out + len), &_state); + + return ( len + base64_encode_blockend((code_out + len), &_state) ); } diff --git a/src/libb64/cencode.h b/src/libb64/cencode.h index 4267237..a6f0707 100644 --- a/src/libb64/cencode.h +++ b/src/libb64/cencode.h @@ -1,10 +1,10 @@ /**************************************************************************************************************************** - cencode.h - c header for a base64 encoding algorithm + cencode.h - c header for a base64 encoding algorithm - This is part of the libb64 project, and has been placed in the public domain. - For details, see http://sourceforge.net/projects/libb64 + This is part of the libb64 project, and has been placed in the public domain. + For details, see http://sourceforge.net/projects/libb64 - For STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) + For STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) AsyncWebServer_STM32 is a library for the STM32 run built-in Ethernet WebServer @@ -12,15 +12,20 @@ Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_STM32 Licensed under MIT license - Version: 1.2.4 + Version: 1.2.5 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.2.3 K Hoang 02/09/2020 Initial coding for STM32 for built-in Ethernet (Nucleo-144, DISCOVERY, etc). Bump up version to v1.2.3 to sync with ESPAsyncWebServer v1.2.3 1.2.4 K Hoang 05/09/2020 Add back MD5/SHA1 authentication feature. + 1.2.5 K Hoang 28/12/2020 Suppress all possible compiler warnings. Add examples. *****************************************************************************************************************************/ +#pragma once + +// Reintroduce to prevent duplication compile error if other lib/core already has LIB64 +// pragma once can't prevent that #ifndef BASE64_CENCODE_H #define BASE64_CENCODE_H