From c90fc983bb61ba48f6a0bbc5609b0e5a6f5f1651 Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Mon, 20 Jan 2025 22:57:32 +0100 Subject: [PATCH 01/32] update readme --- README.ESP32Async.md | 60 ------------------------------------------- README.md | 61 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 61 deletions(-) delete mode 100644 README.ESP32Async.md diff --git a/README.ESP32Async.md b/README.ESP32Async.md deleted file mode 100644 index b86bcfe..0000000 --- a/README.ESP32Async.md +++ /dev/null @@ -1,60 +0,0 @@ -# AsyncTCP - -[![License: LGPL 3.0](https://img.shields.io/badge/License-LGPL%203.0-yellow.svg)](https://opensource.org/license/lgpl-3-0/) -[![Continuous Integration](https://github.com/ESP32Async/AsyncTCP/actions/workflows/ci.yml/badge.svg)](https://github.com/ESP32Async/AsyncTCP/actions/workflows/ci.yml) -[![PlatformIO Registry](https://badges.registry.platformio.org/packages/ESP32Async/library/AsyncTCP.svg)](https://registry.platformio.org/libraries/ESP32Async/AsyncTCP) - -Discord Server: [https://discord.gg/X7zpGdyUcY](https://discord.gg/X7zpGdyUcY) - -### Async TCP Library for ESP32 Arduino - -This is a fully asynchronous TCP library, aimed at enabling trouble-free, multi-connection network environment for Espressif's ESP32 MCUs. - -This library is the base for [ESPAsyncWebServer](https://github.com/ESP32Async/ESPAsyncWebServer) - -## AsyncClient and AsyncServer - -The base classes on which everything else is built. They expose all possible scenarios, but are really raw and require more skills to use. - -## Changes - -- `library.properties` for Arduino IDE users -- Add `CONFIG_ASYNC_TCP_MAX_ACK_TIME` -- Add `CONFIG_ASYNC_TCP_PRIORITY` -- Add `CONFIG_ASYNC_TCP_QUEUE_SIZE` -- Add `setKeepAlive()` -- Arduino 3 / ESP-IDF 5 compatibility -- Better CI -- Better example -- Customizable macros -- Fix for "Required to lock TCPIP core functionality". Ref: https://github.com/ESP32Async/AsyncTCP/issues/27 and https://github.com/espressif/arduino-esp32/issues/10526 -- Fix for "ack timeout 4" client disconnects. -- Fix from https://github.com/me-no-dev/AsyncTCP/pull/173 (partially applied) -- Fix from https://github.com/me-no-dev/AsyncTCP/pull/184 -- IPv6 -- LIBRETINY support -- LibreTuya -- Reduce logging of non critical messages -- Use IPADDR6_INIT() macro to set connecting IPv6 address -- xTaskCreateUniversal function - -## Coordinates - -``` -ESP32Async/AsyncTCP @ ^3.3.2 -``` - -## Important recommendations - -Most of the crashes are caused by improper configuration of the library for the project. -Here are some recommendations to avoid them. - -I personally use the following configuration in my projects: - -```c++ - -D CONFIG_ASYNC_TCP_MAX_ACK_TIME=5000 // (keep default) - -D CONFIG_ASYNC_TCP_PRIORITY=10 // (keep default) - -D CONFIG_ASYNC_TCP_QUEUE_SIZE=64 // (keep default) - -D CONFIG_ASYNC_TCP_RUNNING_CORE=1 // force async_tcp task to be on same core as the app (default is core 0) - -D CONFIG_ASYNC_TCP_STACK_SIZE=4096 // reduce the stack size (default is 16K) -``` diff --git a/README.md b/README.md index f66f505..ffae536 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,12 @@ ![https://avatars.githubusercontent.com/u/195753706?s=96&v=4](https://avatars.githubusercontent.com/u/195753706?s=96&v=4) -# Project moved to [ESP32Async](https://github.com/organizations/ESP32Async) organization at [https://github.com/ESP32Async/AsyncTCP](https://github.com/ESP32Async/AsyncTCP) +# AsyncTCP + +[![License: LGPL 3.0](https://img.shields.io/badge/License-LGPL%203.0-yellow.svg)](https://opensource.org/license/lgpl-3-0/) +[![Continuous Integration](https://github.com/ESP32Async/AsyncTCP/actions/workflows/ci.yml/badge.svg)](https://github.com/ESP32Async/AsyncTCP/actions/workflows/ci.yml) +[![PlatformIO Registry](https://badges.registry.platformio.org/packages/ESP32Async/library/AsyncTCP.svg)](https://registry.platformio.org/libraries/ESP32Async/AsyncTCP) + +Project moved to [ESP32Async](https://github.com/organizations/ESP32Async) organization at [https://github.com/ESP32Async/AsyncTCP](https://github.com/ESP32Async/AsyncTCP) Discord Server: [https://discord.gg/X7zpGdyUcY](https://discord.gg/X7zpGdyUcY) @@ -11,3 +17,56 @@ Please see the new links: - `ESP32Async/ESPAsyncTCP @ 2.0.0` (ESP8266) - `https://github.com/ESP32Async/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip` (AsyncTCP alternative for ESP32) - `khoih-prog/AsyncTCP_RP2040W @ 1.2.0` (RP2040) + +### Async TCP Library for ESP32 Arduino + +This is a fully asynchronous TCP library, aimed at enabling trouble-free, multi-connection network environment for Espressif's ESP32 MCUs. + +This library is the base for [ESPAsyncWebServer](https://github.com/ESP32Async/ESPAsyncWebServer) + +## AsyncClient and AsyncServer + +The base classes on which everything else is built. They expose all possible scenarios, but are really raw and require more skills to use. + +## Changes + +- `library.properties` for Arduino IDE users +- Add `CONFIG_ASYNC_TCP_MAX_ACK_TIME` +- Add `CONFIG_ASYNC_TCP_PRIORITY` +- Add `CONFIG_ASYNC_TCP_QUEUE_SIZE` +- Add `setKeepAlive()` +- Arduino 3 / ESP-IDF 5 compatibility +- Better CI +- Better example +- Customizable macros +- Fix for "Required to lock TCPIP core functionality". Ref: https://github.com/ESP32Async/AsyncTCP/issues/27 and https://github.com/espressif/arduino-esp32/issues/10526 +- Fix for "ack timeout 4" client disconnects. +- Fix from https://github.com/me-no-dev/AsyncTCP/pull/173 (partially applied) +- Fix from https://github.com/me-no-dev/AsyncTCP/pull/184 +- IPv6 +- LIBRETINY support +- LibreTuya +- Reduce logging of non critical messages +- Use IPADDR6_INIT() macro to set connecting IPv6 address +- xTaskCreateUniversal function + +## Coordinates + +``` +ESP32Async/AsyncTCP @ ^3.3.2 +``` + +## Important recommendations + +Most of the crashes are caused by improper configuration of the library for the project. +Here are some recommendations to avoid them. + +I personally use the following configuration in my projects: + +```c++ + -D CONFIG_ASYNC_TCP_MAX_ACK_TIME=5000 // (keep default) + -D CONFIG_ASYNC_TCP_PRIORITY=10 // (keep default) + -D CONFIG_ASYNC_TCP_QUEUE_SIZE=64 // (keep default) + -D CONFIG_ASYNC_TCP_RUNNING_CORE=1 // force async_tcp task to be on same core as the app (default is core 0) + -D CONFIG_ASYNC_TCP_STACK_SIZE=4096 // reduce the stack size (default is 16K) +``` From 7d0cd60c37c13fc45ec9ce522f2515f3b3bf6bfc Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Tue, 21 Jan 2025 12:21:56 +0100 Subject: [PATCH 02/32] Fix link which was not publicly accessible --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ffae536..7fe2218 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Continuous Integration](https://github.com/ESP32Async/AsyncTCP/actions/workflows/ci.yml/badge.svg)](https://github.com/ESP32Async/AsyncTCP/actions/workflows/ci.yml) [![PlatformIO Registry](https://badges.registry.platformio.org/packages/ESP32Async/library/AsyncTCP.svg)](https://registry.platformio.org/libraries/ESP32Async/AsyncTCP) -Project moved to [ESP32Async](https://github.com/organizations/ESP32Async) organization at [https://github.com/ESP32Async/AsyncTCP](https://github.com/ESP32Async/AsyncTCP) +Project moved to [ESP32Async](https://github.com/ESP32Async) organization at [https://github.com/ESP32Async/AsyncTCP](https://github.com/ESP32Async/AsyncTCP) Discord Server: [https://discord.gg/X7zpGdyUcY](https://discord.gg/X7zpGdyUcY) From d872772cbf2a5f4c45117cc9b506a4ed1382cdb4 Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Tue, 21 Jan 2025 21:35:33 +0100 Subject: [PATCH 03/32] Add GitHub workflow to publish to PlatformIO registry --- .github/workflows/publish.yml | 62 +++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..2961c7b --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,62 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json + +name: Publish to PlatformIO + +on: + workflow_dispatch: + inputs: + tag_name: + description: "Tag name of the release to publish (use 'latest' for the most recent tag)" + required: true + default: "latest" + +jobs: + publish: + name: Publish + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-tags: true + fetch-depth: 0 # Ensure all commits and tags are fetched + + - name: Show latest tag + run: git tag --sort=-creatordate | head -n 1 + + - name: Download release zip + run: | + if [ "${{ inputs.tag_name }}" == "latest" ]; then + TAG_NAME=$(git tag --sort=-creatordate | head -n 1) + if [ -z "$TAG_NAME" ]; then + echo "Error: No tags found in the repository." + exit 1 + fi + else + TAG_NAME="${{ inputs.tag_name }}" + fi + echo "Downloading tag: $TAG_NAME" + curl -L -o project.zip "https://github.com/${{ github.repository }}/archive/refs/tags/$TAG_NAME.zip" + + - name: Cache PlatformIO + uses: actions/cache@v4 + with: + key: ${{ runner.os }}-pio + path: | + ~/.cache/pip + ~/.platformio + + - name: Python + uses: actions/setup-python@v5 + with: + python-version: "3.x" + + - name: Install PlatformIO CLI + run: | + python -m pip install --upgrade pip + pip install --upgrade platformio + + - name: Publish to PlatformIO + run: pio pkg publish --no-interactive --owner ${{ github.repository_owner }} project.zip + env: + PLATFORMIO_AUTH_TOKEN: ${{ secrets.PLATFORMIO_AUTH_TOKEN }} From 41822b6700b2b1fe435ed5ed4eff1cc1c8bb6c88 Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Wed, 22 Jan 2025 08:03:11 +0100 Subject: [PATCH 04/32] update issue template --- .github/ISSUE_TEMPLATE/bug_report.md | 35 ---------------------------- .github/ISSUE_TEMPLATE/issue.md | 33 ++++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/question.md | 10 -------- 3 files changed, 33 insertions(+), 45 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/issue.md delete mode 100644 .github/ISSUE_TEMPLATE/question.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 935eb4f..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: "[BUG]" -labels: bug -assignees: '' - ---- - -**Please make sure to go through the recommendations before opening a bug report:** - -[https://github.com/ESP32Async/AsyncTCP?tab=readme-ov-file#important-recommendations](https://github.com/ESP32Async/AsyncTCP?tab=readme-ov-file#important-recommendations) - -**Description** - -A clear and concise description of what the bug is. - -**Board** - -esp32dev, esp32s3, etc - -**Ethernet adapter used ?** - -If yes, please specify which one - -**Stack trace** - -Please provide the stack trace here taken with `monitor_filters = esp32_exception_decoder`. -**Any issue opened with a non readable stack trace will be ignored because not helpful at all.** - -As an alternative, you can use [https://maximeborges.github.io/esp-stacktrace-decoder/](https://maximeborges.github.io/esp-stacktrace-decoder/). - -**Additional notes** - -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/issue.md b/.github/ISSUE_TEMPLATE/issue.md new file mode 100644 index 0000000..b314bb5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/issue.md @@ -0,0 +1,33 @@ +--- +name: Bug Report +about: Only for bugs founds in the library! Otherwise, please go to the discussions section! +title: "[BUG] " +labels: triage +assignees: "" +--- + +PLEASE MAKE SURE TO READ THE RECOMMENDATIONS BEFORE OPENING A BUG REPORT: + +https://github.com/ESP32Async/AsyncTCP?tab=readme-ov-file#important-recommendations-for-build-options + +PLEASE READ THE FOLLOWING BEFORE OPENING A BUG REPORT: + +If you want to discuss a feature, improvement, or as a question, please create a discussion here: https://github.com/ESP32Async/AsyncTCP/discussions/categories/general + +If you have a crash, regression or found an issue, please give the following information: + +- Which board: +- AsyncTCP version (if applicable): +- AsyncTCP repository location you use (if applicable and if you use one coming from elsewhere): + +Please post a complete decoded stack trace below in case of a crash (if applicable): + +```plaintext + +``` + +Please post some code, ino file, minimal reproductible use case (if applicable): + +```cpp + +``` diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md deleted file mode 100644 index 634179c..0000000 --- a/.github/ISSUE_TEMPLATE/question.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Question -about: Describe your question -title: "[Q]" -labels: question -assignees: '' - ---- - - From 937cd5500e66312d35ef43c8b8331e1b20ccb44b Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Wed, 22 Jan 2025 11:43:34 +0100 Subject: [PATCH 05/32] Updated CI --- .github/workflows/ci.yml | 11 ----------- platformio.ini | 7 ------- 2 files changed, 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e4ae11..94d5ae4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,17 +59,6 @@ jobs: - env: ci-arduino-3 board: esp32-c6-devkitc-1 - - env: ci-arduino-311 - board: esp32dev - - env: ci-arduino-311 - board: esp32-s2-saola-1 - - env: ci-arduino-311 - board: esp32-s3-devkitc-1 - - env: ci-arduino-311 - board: esp32-c3-devkitc-02 - - env: ci-arduino-311 - board: esp32-c6-devkitc-1 - steps: - name: Checkout uses: actions/checkout@v4 diff --git a/platformio.ini b/platformio.ini index a19908a..46cb646 100644 --- a/platformio.ini +++ b/platformio.ini @@ -23,9 +23,6 @@ board = esp32dev platform = espressif32@6.9.0 [env:arduino-3] -platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip - -[env:arduino-311] platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.11/platform-espressif32.zip ; CI @@ -35,9 +32,5 @@ platform = espressif32@6.9.0 board = ${sysenv.PIO_BOARD} [env:ci-arduino-3] -platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip -board = ${sysenv.PIO_BOARD} - -[env:ci-arduino-311] platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.11/platform-espressif32.zip board = ${sysenv.PIO_BOARD} From 7ec39af8aee7136c3fce69dd2e0a0063759d846b Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Wed, 22 Jan 2025 11:47:06 +0100 Subject: [PATCH 06/32] Fix CI --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 46cb646..b103b27 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,5 +1,5 @@ [platformio] -default_envs = arduino-2, arduino-3, arduino-311 +default_envs = arduino-2, arduino-3 lib_dir = . src_dir = examples/Client From 9e199c847ca75d604bdbb90b16f6cdbd9c6a00ba Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Wed, 22 Jan 2025 14:56:02 +0100 Subject: [PATCH 07/32] Add release note generation to CI --- .github/workflows/ci.yml | 30 ++++++++++++- .github/workflows/release-notes-config.json | 50 +++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/release-notes-config.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 94d5ae4..7f790f5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ concurrency: cancel-in-progress: true jobs: - build-arduino: + arduino: name: ${{ matrix.config }} runs-on: ubuntu-latest strategy: @@ -82,3 +82,31 @@ jobs: pip install --upgrade platformio - run: PLATFORMIO_SRC_DIR=examples/Client PIO_BOARD=${{ matrix.board }} pio run -e ${{ matrix.env }} + + release: + name: Release + if: ${{ github.repository_owner == 'ESP32Async' && github.event_name != 'pull_request' && startsWith(github.ref, 'refs/tags/v') }} + runs-on: ubuntu-latest + needs: [arduino, platformio] + permissions: + contents: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Changelog + id: github_release + uses: mikepenz/release-changelog-builder-action@v5 + with: + failOnError: true + commitMode: true + configuration: ".github/workflows/release-notes-config.json" + + - name: Release + uses: softprops/action-gh-release@v2 + with: + body: ${{steps.github_release.outputs.changelog}} + append_body: true + make_latest: true + draft: false + prerelease: false diff --git a/.github/workflows/release-notes-config.json b/.github/workflows/release-notes-config.json new file mode 100644 index 0000000..720dcc3 --- /dev/null +++ b/.github/workflows/release-notes-config.json @@ -0,0 +1,50 @@ +{ + "categories": [ + { + "title": "## ⚡ Breaking Changes", + "labels": [ + "break" + ] + }, + { + "title": "## 🚀 Features", + "labels": [ + "feat" + ] + }, + { + "title": "## 🐛 Fixes", + "labels": [ + "fix" + ] + }, + { + "title": "## 📚 Documentation", + "labels": [ + "doc" + ] + }, + { + "title": "## 🛠 Under the hood", + "labels": [] + } + ], + "template": "${{CHANGELOG}}", + "pr_template": "- [${{TITLE}}](https://github.com/mathieucarbou/${{REPO}}/commit/${{MERGE_SHA}})", + "empty_template": "- no changes", + "label_extractor": [ + { + "pattern": "(.): (.+)", + "target": "$1", + "on_property": "title" + }, + { + "pattern": "(.) (.+)", + "target": "$1", + "on_property": "title" + } + ], + "tag_resolver": { + "method": "semver" + } +} From 350f3f0e3a978e796beb684407dfd140f08cba03 Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Wed, 22 Jan 2025 16:18:05 +0100 Subject: [PATCH 08/32] Revert "Add release note generation to CI" This reverts commit 9e199c847ca75d604bdbb90b16f6cdbd9c6a00ba. --- .github/workflows/ci.yml | 28 ------------ .github/workflows/release-notes-config.json | 50 --------------------- 2 files changed, 78 deletions(-) delete mode 100644 .github/workflows/release-notes-config.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7f790f5..3d73198 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,31 +82,3 @@ jobs: pip install --upgrade platformio - run: PLATFORMIO_SRC_DIR=examples/Client PIO_BOARD=${{ matrix.board }} pio run -e ${{ matrix.env }} - - release: - name: Release - if: ${{ github.repository_owner == 'ESP32Async' && github.event_name != 'pull_request' && startsWith(github.ref, 'refs/tags/v') }} - runs-on: ubuntu-latest - needs: [arduino, platformio] - permissions: - contents: write - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Changelog - id: github_release - uses: mikepenz/release-changelog-builder-action@v5 - with: - failOnError: true - commitMode: true - configuration: ".github/workflows/release-notes-config.json" - - - name: Release - uses: softprops/action-gh-release@v2 - with: - body: ${{steps.github_release.outputs.changelog}} - append_body: true - make_latest: true - draft: false - prerelease: false diff --git a/.github/workflows/release-notes-config.json b/.github/workflows/release-notes-config.json deleted file mode 100644 index 720dcc3..0000000 --- a/.github/workflows/release-notes-config.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "categories": [ - { - "title": "## ⚡ Breaking Changes", - "labels": [ - "break" - ] - }, - { - "title": "## 🚀 Features", - "labels": [ - "feat" - ] - }, - { - "title": "## 🐛 Fixes", - "labels": [ - "fix" - ] - }, - { - "title": "## 📚 Documentation", - "labels": [ - "doc" - ] - }, - { - "title": "## 🛠 Under the hood", - "labels": [] - } - ], - "template": "${{CHANGELOG}}", - "pr_template": "- [${{TITLE}}](https://github.com/mathieucarbou/${{REPO}}/commit/${{MERGE_SHA}})", - "empty_template": "- no changes", - "label_extractor": [ - { - "pattern": "(.): (.+)", - "target": "$1", - "on_property": "title" - }, - { - "pattern": "(.) (.+)", - "target": "$1", - "on_property": "title" - } - ], - "tag_resolver": { - "method": "semver" - } -} From 3838c227a7de99f68838831ee05c2019b2c34217 Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Fri, 24 Jan 2025 09:33:41 +0100 Subject: [PATCH 09/32] Update to ESP32Async/ESPAsyncWebServer @ 3.6.2 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7fe2218..f098ef5 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Discord Server: [https://discord.gg/X7zpGdyUcY](https://discord.gg/X7zpGdyUcY) Please see the new links: -- `ESP32Async/ESPAsyncWebServer @ 3.6.0` (ESP32, ESP8266, RP2040) +- `ESP32Async/ESPAsyncWebServer @ 3.6.2` (ESP32, ESP8266, RP2040) - `ESP32Async/AsyncTCP @ 3.3.2` (ESP32) - `ESP32Async/ESPAsyncTCP @ 2.0.0` (ESP8266) - `https://github.com/ESP32Async/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip` (AsyncTCP alternative for ESP32) From e9f25bca9fd10961eb40c6023b4c851caa9d9424 Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Sat, 25 Jan 2025 20:23:35 +0100 Subject: [PATCH 10/32] espressif32@6.10.0 --- platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index b103b27..d72dc3f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -20,7 +20,7 @@ monitor_filters = esp32_exception_decoder, log2file board = esp32dev [env:arduino-2] -platform = espressif32@6.9.0 +platform = espressif32@6.10.0 [env:arduino-3] platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.11/platform-espressif32.zip @@ -28,7 +28,7 @@ platform = https://github.com/pioarduino/platform-espressif32/releases/download/ ; CI [env:ci-arduino-2] -platform = espressif32@6.9.0 +platform = espressif32@6.10.0 board = ${sysenv.PIO_BOARD} [env:ci-arduino-3] From e285ff66080ac46a530096ac47e00c6b9e52e571 Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Sat, 25 Jan 2025 22:03:07 +0100 Subject: [PATCH 11/32] Fix compilation warnings when using LTO Rename structures to avoid compilation warning when using LTO compilation flags: ``` build_flags = -flto=auto build_unflags = -fno-lto ``` Warnings are caused by a conflict with structure names in AsyncUDP: ``` /Users/mat/.platformio/packages/framework-arduinoespressif32-src-77c8e93767360b28deee4aedf5d0a1ab/libraries/AsyncUDP/src/AsyncUDP.cpp:161:3: warning: type 'struct lwip_event_packet_t' violates the C++ One Definition Rule [-Wodr] 161 | } lwip_event_packet_t; | ^ .pio/libdeps/oss-esp32/AsyncTCP/src/AsyncTCP.cpp:121:3: note: a different type is defined in another translation unit 121 | } lwip_event_packet_t; | ^ /Users/mat/.platformio/packages/framework-arduinoespressif32-src-77c8e93767360b28deee4aedf5d0a1ab/libraries/AsyncUDP/src/AsyncUDP.cpp:155:9: note: the first difference of corresponding definitions is field 'arg' 155 | void *arg; | ^ .pio/libdeps/oss-esp32/AsyncTCP/src/AsyncTCP.cpp:87:18: note: a field with different name is defined in another translation unit 87 | lwip_event_t event; | ^ ``` --- src/AsyncTCP.cpp | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/AsyncTCP.cpp b/src/AsyncTCP.cpp index 0761144..89ff79c 100644 --- a/src/AsyncTCP.cpp +++ b/src/AsyncTCP.cpp @@ -81,10 +81,10 @@ typedef enum { LWIP_TCP_ACCEPT, LWIP_TCP_CONNECTED, LWIP_TCP_DNS -} lwip_event_t; +} lwip_tcp_event_t; typedef struct { - lwip_event_t event; + lwip_tcp_event_t event; void* arg; union { struct { @@ -118,7 +118,7 @@ typedef struct { ip_addr_t addr; } dns; }; -} lwip_event_packet_t; +} lwip_tcp_event_packet_t; static QueueHandle_t _async_queue; static TaskHandle_t _async_service_task_handle = NULL; @@ -137,7 +137,7 @@ static uint32_t _closed_index = []() { static inline bool _init_async_event_queue() { if (!_async_queue) { - _async_queue = xQueueCreate(CONFIG_ASYNC_TCP_QUEUE_SIZE, sizeof(lwip_event_packet_t*)); + _async_queue = xQueueCreate(CONFIG_ASYNC_TCP_QUEUE_SIZE, sizeof(lwip_tcp_event_packet_t*)); if (!_async_queue) { return false; } @@ -145,15 +145,15 @@ static inline bool _init_async_event_queue() { return true; } -static inline bool _send_async_event(lwip_event_packet_t** e, TickType_t wait = portMAX_DELAY) { +static inline bool _send_async_event(lwip_tcp_event_packet_t** e, TickType_t wait = portMAX_DELAY) { return _async_queue && xQueueSend(_async_queue, e, wait) == pdPASS; } -static inline bool _prepend_async_event(lwip_event_packet_t** e, TickType_t wait = portMAX_DELAY) { +static inline bool _prepend_async_event(lwip_tcp_event_packet_t** e, TickType_t wait = portMAX_DELAY) { return _async_queue && xQueueSendToFront(_async_queue, e, wait) == pdPASS; } -static inline bool _get_async_event(lwip_event_packet_t** e) { +static inline bool _get_async_event(lwip_tcp_event_packet_t** e) { if (!_async_queue) { return false; } @@ -178,7 +178,7 @@ static inline bool _get_async_event(lwip_event_packet_t** e) { It won't be effective if user would run multiple simultaneous long running callbacks due to message interleaving. todo: implement some kind of fair dequeing or (better) simply punish user for a bad designed callbacks by resetting hog connections */ - lwip_event_packet_t* next_pkt = NULL; + lwip_tcp_event_packet_t* next_pkt = NULL; while (xQueuePeek(_async_queue, &next_pkt, 0) == pdPASS) { if (next_pkt->arg == (*e)->arg && next_pkt->event == LWIP_TCP_POLL) { if (xQueueReceive(_async_queue, &next_pkt, 0) == pdPASS) { @@ -219,8 +219,8 @@ static bool _remove_events_with_arg(void* arg) { return false; } - lwip_event_packet_t* first_packet = NULL; - lwip_event_packet_t* packet = NULL; + lwip_tcp_event_packet_t* first_packet = NULL; + lwip_tcp_event_packet_t* packet = NULL; // figure out which is the first non-matching packet so we can keep the order while (!first_packet) { @@ -261,7 +261,7 @@ static bool _remove_events_with_arg(void* arg) { return true; } -static void _handle_async_event(lwip_event_packet_t* e) { +static void _handle_async_event(lwip_tcp_event_packet_t* e) { if (e->arg == NULL) { // do nothing when arg is NULL // ets_printf("event arg == NULL: 0x%08x\n", e->recv.pcb); @@ -301,7 +301,7 @@ static void _async_service_task(void* pvParameters) { log_w("Failed to add async task to WDT"); } #endif - lwip_event_packet_t* packet = NULL; + lwip_tcp_event_packet_t* packet = NULL; for (;;) { if (_get_async_event(&packet)) { _handle_async_event(packet); @@ -362,7 +362,7 @@ static bool _start_async_task() { * */ static int8_t _tcp_clear_events(void* arg) { - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); + lwip_tcp_event_packet_t* e = (lwip_tcp_event_packet_t*)malloc(sizeof(lwip_tcp_event_packet_t)); e->event = LWIP_TCP_CLEAR; e->arg = arg; if (!_prepend_async_event(&e)) { @@ -373,7 +373,7 @@ static int8_t _tcp_clear_events(void* arg) { static int8_t _tcp_connected(void* arg, tcp_pcb* pcb, int8_t err) { // ets_printf("+C: 0x%08x\n", pcb); - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); + lwip_tcp_event_packet_t* e = (lwip_tcp_event_packet_t*)malloc(sizeof(lwip_tcp_event_packet_t)); e->event = LWIP_TCP_CONNECTED; e->arg = arg; e->connected.pcb = pcb; @@ -393,7 +393,7 @@ static int8_t _tcp_poll(void* arg, struct tcp_pcb* pcb) { } // ets_printf("+P: 0x%08x\n", pcb); - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); + lwip_tcp_event_packet_t* e = (lwip_tcp_event_packet_t*)malloc(sizeof(lwip_tcp_event_packet_t)); e->event = LWIP_TCP_POLL; e->arg = arg; e->poll.pcb = pcb; @@ -405,7 +405,7 @@ static int8_t _tcp_poll(void* arg, struct tcp_pcb* pcb) { } static int8_t _tcp_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* pb, int8_t err) { - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); + lwip_tcp_event_packet_t* e = (lwip_tcp_event_packet_t*)malloc(sizeof(lwip_tcp_event_packet_t)); e->arg = arg; if (pb) { // ets_printf("+R: 0x%08x\n", pcb); @@ -429,7 +429,7 @@ static int8_t _tcp_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* pb, int8_t static int8_t _tcp_sent(void* arg, struct tcp_pcb* pcb, uint16_t len) { // ets_printf("+S: 0x%08x\n", pcb); - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); + lwip_tcp_event_packet_t* e = (lwip_tcp_event_packet_t*)malloc(sizeof(lwip_tcp_event_packet_t)); e->event = LWIP_TCP_SENT; e->arg = arg; e->sent.pcb = pcb; @@ -442,7 +442,7 @@ static int8_t _tcp_sent(void* arg, struct tcp_pcb* pcb, uint16_t len) { static void _tcp_error(void* arg, int8_t err) { // ets_printf("+E: 0x%08x\n", arg); - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); + lwip_tcp_event_packet_t* e = (lwip_tcp_event_packet_t*)malloc(sizeof(lwip_tcp_event_packet_t)); e->event = LWIP_TCP_ERROR; e->arg = arg; e->error.err = err; @@ -452,7 +452,7 @@ static void _tcp_error(void* arg, int8_t err) { } static void _tcp_dns_found(const char* name, struct ip_addr* ipaddr, void* arg) { - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); + lwip_tcp_event_packet_t* e = (lwip_tcp_event_packet_t*)malloc(sizeof(lwip_tcp_event_packet_t)); // ets_printf("+DNS: name=%s ipaddr=0x%08x arg=%x\n", name, ipaddr, arg); e->event = LWIP_TCP_DNS; e->arg = arg; @@ -469,7 +469,7 @@ static void _tcp_dns_found(const char* name, struct ip_addr* ipaddr, void* arg) // Used to switch out from LwIP thread static int8_t _tcp_accept(void* arg, AsyncClient* client) { - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); + lwip_tcp_event_packet_t* e = (lwip_tcp_event_packet_t*)malloc(sizeof(lwip_tcp_event_packet_t)); e->event = LWIP_TCP_ACCEPT; e->arg = arg; e->accept.client = client; From 70b8e8bb4676ef501127bc6799fafd8c41e79e24 Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Sun, 26 Jan 2025 14:17:50 +0100 Subject: [PATCH 12/32] Disable blank issue creation --- .github/ISSUE_TEMPLATE/config.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..02f38c3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,2 @@ +# https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository +blank_issues_enabled: false From f7693f86f17f4229dd40b4f1e66f21aca2ff73e0 Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Mon, 27 Jan 2025 10:47:55 +0100 Subject: [PATCH 13/32] remove duplicate file --- .clang-format copy | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 .clang-format copy diff --git a/.clang-format copy b/.clang-format copy deleted file mode 100644 index 63f29c0..0000000 --- a/.clang-format copy +++ /dev/null @@ -1,22 +0,0 @@ -Language: Cpp -BasedOnStyle: LLVM - -AccessModifierOffset: -2 -AlignConsecutiveMacros: true -AllowAllArgumentsOnNextLine: false -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortIfStatementsOnASingleLine: false -AllowShortLambdasOnASingleLine: Inline -BinPackArguments: false -ColumnLimit: 0 -ContinuationIndentWidth: 2 -FixNamespaceComments: false -IndentAccessModifiers: true -IndentCaseLabels: true -IndentPPDirectives: BeforeHash -IndentWidth: 2 -NamespaceIndentation: All -PointerAlignment: Left -ReferenceAlignment: Left -TabWidth: 2 -UseTab: Never From 2c60166bf4585aa22755948c02f3c423351e81db Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Mon, 27 Jan 2025 22:53:09 +0100 Subject: [PATCH 14/32] Only run Ci on main and release branches --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d73198..fd8d647 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,9 @@ name: Async TCP CI on: push: + branches: + - main + - release/* pull_request: workflow_dispatch: From 2c379f08a85f8bbb1cf6c518d2f88de5fc0e9860 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Tue, 28 Jan 2025 13:00:11 -0300 Subject: [PATCH 15/32] feat: Add pre-commit CI and sync formatting settings (#8) * feat: Add pre-commit CI and sync formatting settings * fix: Apply new formatting * fix: Ignore platformio.ini * fix: Fix spelling and add spell checker --- .clang-format | 254 ++++++++++- .codespellrc | 8 + .editorconfig | 60 +++ .github/workflows/pre-commit-status.yml | 64 +++ .github/workflows/pre-commit.yml | 80 ++++ .pre-commit-config.yaml | 42 ++ CODE_OF_CONDUCT.md | 2 +- examples/Client/Client.ino | 21 +- pre-commit.requirements.txt | 1 + src/AsyncTCP.cpp | 579 ++++++++++++------------ src/AsyncTCP.h | 416 ++++++++--------- 11 files changed, 996 insertions(+), 531 deletions(-) create mode 100644 .codespellrc create mode 100644 .editorconfig create mode 100644 .github/workflows/pre-commit-status.yml create mode 100644 .github/workflows/pre-commit.yml create mode 100644 .pre-commit-config.yaml create mode 100644 pre-commit.requirements.txt diff --git a/.clang-format b/.clang-format index 63f29c0..8f47348 100644 --- a/.clang-format +++ b/.clang-format @@ -1,22 +1,246 @@ -Language: Cpp +# Clang format version: 18.1.3 +--- BasedOnStyle: LLVM - AccessModifierOffset: -2 -AlignConsecutiveMacros: true -AllowAllArgumentsOnNextLine: false -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortIfStatementsOnASingleLine: false -AllowShortLambdasOnASingleLine: Inline -BinPackArguments: false -ColumnLimit: 0 +AlignAfterOpenBracket: BlockIndent +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCaseColons: false +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowBreakBeforeNoexceptSpecifier: Never +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: true +AllowShortCompoundRequirementOnASingleLine: true +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: Empty +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: true + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAdjacentStringLiterals: true +BreakAfterAttributes: Always +BreakAfterJavaFieldAnnotations: false +BreakArrays: false +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BreakBeforeConceptDeclarations: Always +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +ColumnLimit: 160 +CommentPragmas: "" +CompactNamespaces: false +ConstructorInitializerIndentWidth: 2 ContinuationIndentWidth: 2 -FixNamespaceComments: false -IndentAccessModifiers: true +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: ^"(llvm|llvm-c|clang|clang-c)/ + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: ^(<|"(gtest|gmock|isl|json)/) + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: .* + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: "" +IncludeIsMainSourceRegex: "" +IndentAccessModifiers: false +IndentCaseBlocks: false IndentCaseLabels: true -IndentPPDirectives: BeforeHash +IndentExternBlock: NoIndent +IndentGotoLabels: false +IndentPPDirectives: None +IndentRequiresClause: false IndentWidth: 2 -NamespaceIndentation: All -PointerAlignment: Left -ReferenceAlignment: Left +IndentWrappedFunctionNames: true +InsertBraces: true +InsertNewlineAtEOF: true +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtEOF: false +KeepEmptyLinesAtTheStartOfBlocks: true +LambdaBodyIndentation: Signature +Language: Cpp +LineEnding: LF +MacroBlockBegin: "" +MacroBlockEnd: "" +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PPIndentWidth: -1 +PackConstructorInitializers: BinPack +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakScopeResolution: 500 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +QualifierAlignment: Leave +ReferenceAlignment: Pointer +ReflowComments: false +RemoveBracesLLVM: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SkipMacroDefinitionBody: false +SortIncludes: Never +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDeclarationName: false + AfterFunctionDefinitionName: false + AfterIfMacros: true + AfterOverloadedOperator: true + AfterPlacementOperator: true + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: Never +SpacesInContainerLiterals: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + InConditionalStatements: false + InCStyleCasts: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: Auto +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION TabWidth: 2 UseTab: Never +VerilogBreakBetweenInstancePorts: true +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE +BracedInitializerIndentWidth: 2 diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 0000000..46c1122 --- /dev/null +++ b/.codespellrc @@ -0,0 +1,8 @@ +[codespell] +# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/spell-check/.codespellrc +# In the event of a false positive, add the problematic word, in all lowercase, to a comma-separated list here: +ignore-words-list = ba,licence +skip = ./.git,./.licenses,__pycache__,.clang-format,.codespellrc,.editorconfig,.flake8,.prettierignore,.yamllint.yml,.gitignore +builtin = clear,informal,en-GB_to_en-US +check-filenames = +check-hidden = diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e22936c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,60 @@ +# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/general/.editorconfig +# See: https://editorconfig.org/ +# The formatting style defined in this file is the official standardized style to be used in all Arduino Tooling +# projects and should not be modified. +# Note: indent style for each file type is defined even when it matches the universal config in order to make it clear +# that this type has an official style. + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{adoc,asc,asciidoc}] +indent_size = 2 +indent_style = space + +[*.{bash,sh}] +indent_size = 4 +indent_style = space + +[*.{c,cc,cp,cpp,cxx,h,hh,hpp,hxx,ii,inl,ino,ixx,pde,tpl,tpp,txx}] +indent_size = 2 +indent_style = space + +[*.{go,mod}] +indent_style = tab + +[*.java] +indent_size = 2 +indent_style = space + +[*.{js,jsx,json,jsonc,json5,ts,tsx}] +indent_size = 2 +indent_style = space + +[*.{md,mdx,mkdn,mdown,markdown}] +indent_size = unset +indent_style = space + +[*.proto] +indent_size = 2 +indent_style = space + +[*.py] +indent_size = 4 +indent_style = space + +[*.svg] +indent_size = 2 +indent_style = space + +[*.{yaml,yml}] +indent_size = 2 +indent_style = space + +[{.gitconfig,.gitmodules}] +indent_style = tab diff --git a/.github/workflows/pre-commit-status.yml b/.github/workflows/pre-commit-status.yml new file mode 100644 index 0000000..d006066 --- /dev/null +++ b/.github/workflows/pre-commit-status.yml @@ -0,0 +1,64 @@ +# This needs to be in a separate workflow because it requires higher permissions than the calling workflow +name: Report Pre-commit Check Status + +on: + workflow_run: + workflows: [Pre-commit hooks] + types: + - completed + +permissions: + statuses: write + +jobs: + report-success: + name: Report pre-commit success + if: github.event.workflow_run.conclusion == 'success' + runs-on: ubuntu-latest + steps: + - name: Report success + uses: actions/github-script@v7 + with: + script: | + const owner = '${{ github.repository_owner }}'; + const repo = '${{ github.repository }}'.split('/')[1]; + const sha = '${{ github.event.workflow_run.head_sha }}'; + core.debug(`owner: ${owner}`); + core.debug(`repo: ${repo}`); + core.debug(`sha: ${sha}`); + const { context: name, state } = (await github.rest.repos.createCommitStatus({ + context: 'Pre-commit checks', + description: 'Pre-commit checks successful', + owner: owner, + repo: repo, + sha: sha, + state: 'success', + target_url: 'https://github.com/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}' + })).data; + core.info(`${name} is ${state}`); + + report-pending: + name: Report pre-commit pending + if: github.event.workflow_run.conclusion != 'success' + runs-on: ubuntu-latest + steps: + - name: Report pending + uses: actions/github-script@v7 + with: + script: | + const owner = '${{ github.repository_owner }}'; + const repo = '${{ github.repository }}'.split('/')[1]; + const sha = '${{ github.event.workflow_run.head_sha }}'; + core.debug(`owner: ${owner}`); + core.debug(`repo: ${repo}`); + core.debug(`sha: ${sha}`); + const { context: name, state } = (await github.rest.repos.createCommitStatus({ + context: 'Pre-commit checks', + description: 'The pre-commit checks need to be successful before merging', + owner: owner, + repo: repo, + sha: sha, + state: 'pending', + target_url: 'https://github.com/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}' + })).data; + core.info(`${name} is ${state}`); diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 0000000..b92f283 --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,80 @@ +name: Pre-commit hooks + +on: + workflow_dispatch: + push: + branches: + - main + pull_request: + types: [opened, reopened, synchronize, labeled] + +concurrency: + group: pre-commit-${{github.event.pull_request.number || github.ref}} + cancel-in-progress: true + +jobs: + lint: + if: | + github.event_name != 'pull_request' || + contains(github.event.pull_request.labels.*.name, 'Status: Pending Merge') || + contains(github.event.pull_request.labels.*.name, 'Re-trigger Pre-commit') + + name: Check if fixes are needed + runs-on: ubuntu-latest + steps: + - name: Checkout latest commit + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Remove Label + if: contains(github.event.pull_request.labels.*.name, 'Re-trigger Pre-commit') + run: gh pr edit ${{ github.event.number }} --remove-label 'Re-trigger Pre-commit' + env: + GH_TOKEN: ${{ github.token }} + + - name: Set up Python 3 + uses: actions/setup-python@v5 + with: + cache-dependency-path: pre-commit.requirements.txt + cache: "pip" + python-version: "3.x" + + - name: Get Python version hash + run: | + echo "Using $(python -VV)" + echo "PY_HASH=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV + + - name: Restore pre-commit cache + uses: actions/cache/restore@v4 + id: restore-cache + with: + path: | + ~/.cache/pre-commit + key: pre-commit-${{ env.PY_HASH }}-${{ hashFiles('.pre-commit-config.yaml', '.github/workflows/pre-commit.yml', 'pre-commit.requirements.txt') }} + + - name: Install python dependencies + run: python -m pip install -r pre-commit.requirements.txt + + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v42.0.2 + + - name: Run pre-commit hooks in changed files + run: pre-commit run --color=always --show-diff-on-failure --files ${{ steps.changed-files.outputs.all_changed_files }} + + - name: Save pre-commit cache + uses: actions/cache/save@v4 + if: ${{ always() && steps.restore-cache.outputs.cache-hit != 'true' }} + continue-on-error: true + with: + path: | + ~/.cache/pre-commit + key: ${{ steps.restore-cache.outputs.cache-primary-key }} + + - name: Push changes using pre-commit-ci-lite + uses: pre-commit-ci/lite-action@v1.1.0 + # Only push changes in PRs + if: ${{ always() && github.event_name == 'pull_request' }} + with: + msg: "ci(pre-commit): Apply automatic fixes" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..660d000 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,42 @@ +exclude: | + (?x)( + ^\.github\/| + LICENSE\.md$ + ) + +default_language_version: + # force all unspecified python hooks to run python3 + python: python3 + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: "v5.0.0" + hooks: + # Generic checks + - id: check-case-conflict + - id: check-symlinks + - id: debug-statements + - id: destroyed-symlinks + - id: detect-private-key + - id: end-of-file-fixer + exclude: ^.*\.(bin|BIN)$ + - id: mixed-line-ending + args: [--fix=lf] + - id: trailing-whitespace + args: [--markdown-linebreak-ext=md] + exclude: ^platformio\.ini$ + + - repo: https://github.com/codespell-project/codespell + rev: "v2.3.0" + hooks: + # Spell checking + - id: codespell + exclude: ^.*\.(svd|SVD)$ + + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: "v18.1.3" + hooks: + # C/C++ formatting + - id: clang-format + types_or: [c, c++] + exclude: ^.*\/build_opt\.h$ diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 0a5f914..4fcdc2f 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -6,7 +6,7 @@ We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, +identity and expression, level of experience, education, socioeconomic status, nationality, personal appearance, race, religion, or sexual identity and orientation. diff --git a/examples/Client/Client.ino b/examples/Client/Client.ino index 609af6d..61987af 100644 --- a/examples/Client/Client.ino +++ b/examples/Client/Client.ino @@ -2,7 +2,7 @@ #include #include -// Run a server at the root of the project with: +// Run a server at the root of the project with: // > python3 -m http.server 3333 // Now you can open a browser and test it works by visiting http://192.168.125.122:3333/ or http://192.168.125.122:3333/README.md #define HOST "192.168.125.122" @@ -18,24 +18,25 @@ size_t permits = MAX_CLIENTS; void makeRequest() { - if (!permits) + if (!permits) { return; + } Serial.printf("** permits: %d\n", permits); - AsyncClient* client = new AsyncClient; + AsyncClient *client = new AsyncClient; - client->onError([](void* arg, AsyncClient* client, int8_t error) { + client->onError([](void *arg, AsyncClient *client, int8_t error) { Serial.printf("** error occurred %s \n", client->errorToString(error)); client->close(true); delete client; }); - client->onConnect([](void* arg, AsyncClient* client) { + client->onConnect([](void *arg, AsyncClient *client) { permits--; Serial.printf("** client has been connected: %" PRIu16 "\n", client->localPort()); - client->onDisconnect([](void* arg, AsyncClient* client) { + client->onDisconnect([](void *arg, AsyncClient *client) { Serial.printf("** client has been disconnected: %" PRIu16 "\n", client->localPort()); client->close(true); delete client; @@ -44,7 +45,7 @@ void makeRequest() { makeRequest(); }); - client->onData([](void* arg, AsyncClient* client, void* data, size_t len) { + client->onData([](void *arg, AsyncClient *client, void *data, size_t len) { Serial.printf("** data received by client: %" PRIu16 ": len=%u\n", client->localPort(), len); }); @@ -59,8 +60,9 @@ void makeRequest() { void setup() { Serial.begin(115200); - while (!Serial) + while (!Serial) { continue; + } WiFi.mode(WIFI_STA); WiFi.begin(WIFI_SSID); @@ -71,8 +73,9 @@ void setup() { Serial.println("** connected to WiFi"); Serial.println(WiFi.localIP()); - for (size_t i = 0; i < MAX_CLIENTS; i++) + for (size_t i = 0; i < MAX_CLIENTS; i++) { makeRequest(); + } } void loop() { diff --git a/pre-commit.requirements.txt b/pre-commit.requirements.txt new file mode 100644 index 0000000..40a16fa --- /dev/null +++ b/pre-commit.requirements.txt @@ -0,0 +1 @@ +pre-commit==4.1.0 diff --git a/src/AsyncTCP.cpp b/src/AsyncTCP.cpp index 89ff79c..6498b5c 100644 --- a/src/AsyncTCP.cpp +++ b/src/AsyncTCP.cpp @@ -32,32 +32,32 @@ extern "C" { } #if CONFIG_ASYNC_TCP_USE_WDT - #include "esp_task_wdt.h" +#include "esp_task_wdt.h" #endif // Required for: // https://github.com/espressif/arduino-esp32/blob/3.0.3/libraries/Network/src/NetworkInterface.cpp#L37-L47 #if ESP_IDF_VERSION_MAJOR >= 5 - #include +#include #endif #define TAG "AsyncTCP" // https://github.com/espressif/arduino-esp32/issues/10526 #ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING - #define TCP_MUTEX_LOCK() \ - if (!sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) { \ - LOCK_TCPIP_CORE(); \ - } +#define TCP_MUTEX_LOCK() \ + if (!sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) { \ + LOCK_TCPIP_CORE(); \ + } - #define TCP_MUTEX_UNLOCK() \ - if (sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) { \ - UNLOCK_TCPIP_CORE(); \ - } -#else // CONFIG_LWIP_TCPIP_CORE_LOCKING - #define TCP_MUTEX_LOCK() - #define TCP_MUTEX_UNLOCK() -#endif // CONFIG_LWIP_TCPIP_CORE_LOCKING +#define TCP_MUTEX_UNLOCK() \ + if (sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) { \ + UNLOCK_TCPIP_CORE(); \ + } +#else // CONFIG_LWIP_TCPIP_CORE_LOCKING +#define TCP_MUTEX_LOCK() +#define TCP_MUTEX_UNLOCK() +#endif // CONFIG_LWIP_TCPIP_CORE_LOCKING #define INVALID_CLOSED_SLOT -1 @@ -84,40 +84,40 @@ typedef enum { } lwip_tcp_event_t; typedef struct { - lwip_tcp_event_t event; - void* arg; - union { - struct { - tcp_pcb* pcb; - int8_t err; - } connected; - struct { - int8_t err; - } error; - struct { - tcp_pcb* pcb; - uint16_t len; - } sent; - struct { - tcp_pcb* pcb; - pbuf* pb; - int8_t err; - } recv; - struct { - tcp_pcb* pcb; - int8_t err; - } fin; - struct { - tcp_pcb* pcb; - } poll; - struct { - AsyncClient* client; - } accept; - struct { - const char* name; - ip_addr_t addr; - } dns; - }; + lwip_tcp_event_t event; + void *arg; + union { + struct { + tcp_pcb *pcb; + int8_t err; + } connected; + struct { + int8_t err; + } error; + struct { + tcp_pcb *pcb; + uint16_t len; + } sent; + struct { + tcp_pcb *pcb; + pbuf *pb; + int8_t err; + } recv; + struct { + tcp_pcb *pcb; + int8_t err; + } fin; + struct { + tcp_pcb *pcb; + } poll; + struct { + AsyncClient *client; + } accept; + struct { + const char *name; + ip_addr_t addr; + } dns; + }; } lwip_tcp_event_packet_t; static QueueHandle_t _async_queue; @@ -137,7 +137,7 @@ static uint32_t _closed_index = []() { static inline bool _init_async_event_queue() { if (!_async_queue) { - _async_queue = xQueueCreate(CONFIG_ASYNC_TCP_QUEUE_SIZE, sizeof(lwip_tcp_event_packet_t*)); + _async_queue = xQueueCreate(CONFIG_ASYNC_TCP_QUEUE_SIZE, sizeof(lwip_tcp_event_packet_t *)); if (!_async_queue) { return false; } @@ -145,30 +145,33 @@ static inline bool _init_async_event_queue() { return true; } -static inline bool _send_async_event(lwip_tcp_event_packet_t** e, TickType_t wait = portMAX_DELAY) { +static inline bool _send_async_event(lwip_tcp_event_packet_t **e, TickType_t wait = portMAX_DELAY) { return _async_queue && xQueueSend(_async_queue, e, wait) == pdPASS; } -static inline bool _prepend_async_event(lwip_tcp_event_packet_t** e, TickType_t wait = portMAX_DELAY) { +static inline bool _prepend_async_event(lwip_tcp_event_packet_t **e, TickType_t wait = portMAX_DELAY) { return _async_queue && xQueueSendToFront(_async_queue, e, wait) == pdPASS; } -static inline bool _get_async_event(lwip_tcp_event_packet_t** e) { +static inline bool _get_async_event(lwip_tcp_event_packet_t **e) { if (!_async_queue) { return false; } #if CONFIG_ASYNC_TCP_USE_WDT // need to return periodically to feed the dog - if (xQueueReceive(_async_queue, e, pdMS_TO_TICKS(1000)) != pdPASS) + if (xQueueReceive(_async_queue, e, pdMS_TO_TICKS(1000)) != pdPASS) { return false; + } #else - if (xQueueReceive(_async_queue, e, portMAX_DELAY) != pdPASS) + if (xQueueReceive(_async_queue, e, portMAX_DELAY) != pdPASS) { return false; + } #endif - if ((*e)->event != LWIP_TCP_POLL) + if ((*e)->event != LWIP_TCP_POLL) { return true; + } /* Let's try to coalesce two (or more) consecutive poll events into one @@ -176,9 +179,9 @@ static inline bool _get_async_event(lwip_tcp_event_packet_t** e) { if consecutive user callback for a same connection runs longer that poll time then it will fill the queue with events until it deadlocks. This is a workaround to mitigate such poor designs and won't let other events/connections to starve the task time. It won't be effective if user would run multiple simultaneous long running callbacks due to message interleaving. - todo: implement some kind of fair dequeing or (better) simply punish user for a bad designed callbacks by resetting hog connections + todo: implement some kind of fair dequeuing or (better) simply punish user for a bad designed callbacks by resetting hog connections */ - lwip_tcp_event_packet_t* next_pkt = NULL; + lwip_tcp_event_packet_t *next_pkt = NULL; while (xQueuePeek(_async_queue, &next_pkt, 0) == pdPASS) { if (next_pkt->arg == (*e)->arg && next_pkt->event == LWIP_TCP_POLL) { if (xQueueReceive(_async_queue, &next_pkt, 0) == pdPASS) { @@ -214,13 +217,13 @@ static inline bool _get_async_event(lwip_tcp_event_packet_t** e) { return true; } -static bool _remove_events_with_arg(void* arg) { +static bool _remove_events_with_arg(void *arg) { if (!_async_queue) { return false; } - lwip_tcp_event_packet_t* first_packet = NULL; - lwip_tcp_event_packet_t* packet = NULL; + lwip_tcp_event_packet_t *first_packet = NULL; + lwip_tcp_event_packet_t *packet = NULL; // figure out which is the first non-matching packet so we can keep the order while (!first_packet) { @@ -261,7 +264,7 @@ static bool _remove_events_with_arg(void* arg) { return true; } -static void _handle_async_event(lwip_tcp_event_packet_t* e) { +static void _handle_async_event(lwip_tcp_event_packet_t *e) { if (e->arg == NULL) { // do nothing when arg is NULL // ets_printf("event arg == NULL: 0x%08x\n", e->recv.pcb); @@ -292,16 +295,16 @@ static void _handle_async_event(lwip_tcp_event_packet_t* e) { // ets_printf("D: 0x%08x %s = %s\n", e->arg, e->dns.name, ipaddr_ntoa(&e->dns.addr)); AsyncClient::_s_dns_found(e->dns.name, &e->dns.addr, e->arg); } - free((void*)(e)); + free((void *)(e)); } -static void _async_service_task(void* pvParameters) { +static void _async_service_task(void *pvParameters) { #if CONFIG_ASYNC_TCP_USE_WDT if (esp_task_wdt_add(NULL) != ESP_OK) { log_w("Failed to add async task to WDT"); } #endif - lwip_tcp_event_packet_t* packet = NULL; + lwip_tcp_event_packet_t *packet = NULL; for (;;) { if (_get_async_event(&packet)) { _handle_async_event(packet); @@ -326,13 +329,9 @@ static void _stop_async_task(){ */ static bool customTaskCreateUniversal( - TaskFunction_t pxTaskCode, - const char* const pcName, - const uint32_t usStackDepth, - void* const pvParameters, - UBaseType_t uxPriority, - TaskHandle_t* const pxCreatedTask, - const BaseType_t xCoreID) { + TaskFunction_t pxTaskCode, const char *const pcName, const uint32_t usStackDepth, void *const pvParameters, UBaseType_t uxPriority, + TaskHandle_t *const pxCreatedTask, const BaseType_t xCoreID +) { #ifndef CONFIG_FREERTOS_UNICORE if (xCoreID >= 0 && xCoreID < 2) { return xTaskCreatePinnedToCore(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask, xCoreID); @@ -349,7 +348,9 @@ static bool _start_async_task() { return false; } if (!_async_service_task_handle) { - customTaskCreateUniversal(_async_service_task, "async_tcp", CONFIG_ASYNC_TCP_STACK_SIZE, NULL, CONFIG_ASYNC_TCP_PRIORITY, &_async_service_task_handle, CONFIG_ASYNC_TCP_RUNNING_CORE); + customTaskCreateUniversal( + _async_service_task, "async_tcp", CONFIG_ASYNC_TCP_STACK_SIZE, NULL, CONFIG_ASYNC_TCP_PRIORITY, &_async_service_task_handle, CONFIG_ASYNC_TCP_RUNNING_CORE + ); if (!_async_service_task_handle) { return false; } @@ -361,31 +362,31 @@ static bool _start_async_task() { * LwIP Callbacks * */ -static int8_t _tcp_clear_events(void* arg) { - lwip_tcp_event_packet_t* e = (lwip_tcp_event_packet_t*)malloc(sizeof(lwip_tcp_event_packet_t)); +static int8_t _tcp_clear_events(void *arg) { + lwip_tcp_event_packet_t *e = (lwip_tcp_event_packet_t *)malloc(sizeof(lwip_tcp_event_packet_t)); e->event = LWIP_TCP_CLEAR; e->arg = arg; if (!_prepend_async_event(&e)) { - free((void*)(e)); + free((void *)(e)); } return ERR_OK; } -static int8_t _tcp_connected(void* arg, tcp_pcb* pcb, int8_t err) { +static int8_t _tcp_connected(void *arg, tcp_pcb *pcb, int8_t err) { // ets_printf("+C: 0x%08x\n", pcb); - lwip_tcp_event_packet_t* e = (lwip_tcp_event_packet_t*)malloc(sizeof(lwip_tcp_event_packet_t)); + lwip_tcp_event_packet_t *e = (lwip_tcp_event_packet_t *)malloc(sizeof(lwip_tcp_event_packet_t)); e->event = LWIP_TCP_CONNECTED; e->arg = arg; e->connected.pcb = pcb; e->connected.err = err; if (!_prepend_async_event(&e)) { - free((void*)(e)); + free((void *)(e)); } return ERR_OK; } -static int8_t _tcp_poll(void* arg, struct tcp_pcb* pcb) { - // throttle polling events queing when event queue is getting filled up, let it handle _onack's +static int8_t _tcp_poll(void *arg, struct tcp_pcb *pcb) { + // throttle polling events queueing when event queue is getting filled up, let it handle _onack's // log_d("qs:%u", uxQueueMessagesWaiting(_async_queue)); if (uxQueueMessagesWaiting(_async_queue) > (rand() % CONFIG_ASYNC_TCP_QUEUE_SIZE / 2 + CONFIG_ASYNC_TCP_QUEUE_SIZE / 4)) { log_d("throttling"); @@ -393,19 +394,19 @@ static int8_t _tcp_poll(void* arg, struct tcp_pcb* pcb) { } // ets_printf("+P: 0x%08x\n", pcb); - lwip_tcp_event_packet_t* e = (lwip_tcp_event_packet_t*)malloc(sizeof(lwip_tcp_event_packet_t)); + lwip_tcp_event_packet_t *e = (lwip_tcp_event_packet_t *)malloc(sizeof(lwip_tcp_event_packet_t)); e->event = LWIP_TCP_POLL; e->arg = arg; e->poll.pcb = pcb; // poll events are not critical 'cause those are repetitive, so we may not wait the queue in any case if (!_send_async_event(&e, 0)) { - free((void*)(e)); + free((void *)(e)); } return ERR_OK; } -static int8_t _tcp_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* pb, int8_t err) { - lwip_tcp_event_packet_t* e = (lwip_tcp_event_packet_t*)malloc(sizeof(lwip_tcp_event_packet_t)); +static int8_t _tcp_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *pb, int8_t err) { + lwip_tcp_event_packet_t *e = (lwip_tcp_event_packet_t *)malloc(sizeof(lwip_tcp_event_packet_t)); e->arg = arg; if (pb) { // ets_printf("+R: 0x%08x\n", pcb); @@ -422,37 +423,37 @@ static int8_t _tcp_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* pb, int8_t AsyncClient::_s_lwip_fin(e->arg, e->fin.pcb, e->fin.err); } if (!_send_async_event(&e)) { - free((void*)(e)); + free((void *)(e)); } return ERR_OK; } -static int8_t _tcp_sent(void* arg, struct tcp_pcb* pcb, uint16_t len) { +static int8_t _tcp_sent(void *arg, struct tcp_pcb *pcb, uint16_t len) { // ets_printf("+S: 0x%08x\n", pcb); - lwip_tcp_event_packet_t* e = (lwip_tcp_event_packet_t*)malloc(sizeof(lwip_tcp_event_packet_t)); + lwip_tcp_event_packet_t *e = (lwip_tcp_event_packet_t *)malloc(sizeof(lwip_tcp_event_packet_t)); e->event = LWIP_TCP_SENT; e->arg = arg; e->sent.pcb = pcb; e->sent.len = len; if (!_send_async_event(&e)) { - free((void*)(e)); + free((void *)(e)); } return ERR_OK; } -static void _tcp_error(void* arg, int8_t err) { +static void _tcp_error(void *arg, int8_t err) { // ets_printf("+E: 0x%08x\n", arg); - lwip_tcp_event_packet_t* e = (lwip_tcp_event_packet_t*)malloc(sizeof(lwip_tcp_event_packet_t)); + lwip_tcp_event_packet_t *e = (lwip_tcp_event_packet_t *)malloc(sizeof(lwip_tcp_event_packet_t)); e->event = LWIP_TCP_ERROR; e->arg = arg; e->error.err = err; if (!_send_async_event(&e)) { - free((void*)(e)); + free((void *)(e)); } } -static void _tcp_dns_found(const char* name, struct ip_addr* ipaddr, void* arg) { - lwip_tcp_event_packet_t* e = (lwip_tcp_event_packet_t*)malloc(sizeof(lwip_tcp_event_packet_t)); +static void _tcp_dns_found(const char *name, struct ip_addr *ipaddr, void *arg) { + lwip_tcp_event_packet_t *e = (lwip_tcp_event_packet_t *)malloc(sizeof(lwip_tcp_event_packet_t)); // ets_printf("+DNS: name=%s ipaddr=0x%08x arg=%x\n", name, ipaddr, arg); e->event = LWIP_TCP_DNS; e->arg = arg; @@ -463,18 +464,18 @@ static void _tcp_dns_found(const char* name, struct ip_addr* ipaddr, void* arg) memset(&e->dns.addr, 0, sizeof(e->dns.addr)); } if (!_send_async_event(&e)) { - free((void*)(e)); + free((void *)(e)); } } // Used to switch out from LwIP thread -static int8_t _tcp_accept(void* arg, AsyncClient* client) { - lwip_tcp_event_packet_t* e = (lwip_tcp_event_packet_t*)malloc(sizeof(lwip_tcp_event_packet_t)); +static int8_t _tcp_accept(void *arg, AsyncClient *client) { + lwip_tcp_event_packet_t *e = (lwip_tcp_event_packet_t *)malloc(sizeof(lwip_tcp_event_packet_t)); e->event = LWIP_TCP_ACCEPT; e->arg = arg; e->accept.client = client; if (!_prepend_async_event(&e)) { - free((void*)(e)); + free((void *)(e)); } return ERR_OK; } @@ -486,32 +487,32 @@ static int8_t _tcp_accept(void* arg, AsyncClient* client) { #include "lwip/priv/tcpip_priv.h" typedef struct { - struct tcpip_api_call_data call; - tcp_pcb* pcb; - int8_t closed_slot; - int8_t err; - union { - struct { - const char* data; - size_t size; - uint8_t apiflags; - } write; - size_t received; - struct { - ip_addr_t* addr; - uint16_t port; - tcp_connected_fn cb; - } connect; - struct { - ip_addr_t* addr; - uint16_t port; - } bind; - uint8_t backlog; - }; + struct tcpip_api_call_data call; + tcp_pcb *pcb; + int8_t closed_slot; + int8_t err; + union { + struct { + const char *data; + size_t size; + uint8_t apiflags; + } write; + size_t received; + struct { + ip_addr_t *addr; + uint16_t port; + tcp_connected_fn cb; + } connect; + struct { + ip_addr_t *addr; + uint16_t port; + } bind; + uint8_t backlog; + }; } tcp_api_call_t; -static err_t _tcp_output_api(struct tcpip_api_call_data* api_call_msg) { - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; +static err_t _tcp_output_api(struct tcpip_api_call_data *api_call_msg) { + tcp_api_call_t *msg = (tcp_api_call_t *)api_call_msg; msg->err = ERR_CONN; if (msg->closed_slot == INVALID_CLOSED_SLOT || !_closed_slots[msg->closed_slot]) { msg->err = tcp_output(msg->pcb); @@ -519,19 +520,19 @@ static err_t _tcp_output_api(struct tcpip_api_call_data* api_call_msg) { return msg->err; } -static esp_err_t _tcp_output(tcp_pcb* pcb, int8_t closed_slot) { +static esp_err_t _tcp_output(tcp_pcb *pcb, int8_t closed_slot) { if (!pcb) { return ERR_CONN; } tcp_api_call_t msg; msg.pcb = pcb; msg.closed_slot = closed_slot; - tcpip_api_call(_tcp_output_api, (struct tcpip_api_call_data*)&msg); + tcpip_api_call(_tcp_output_api, (struct tcpip_api_call_data *)&msg); return msg.err; } -static err_t _tcp_write_api(struct tcpip_api_call_data* api_call_msg) { - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; +static err_t _tcp_write_api(struct tcpip_api_call_data *api_call_msg) { + tcp_api_call_t *msg = (tcp_api_call_t *)api_call_msg; msg->err = ERR_CONN; if (msg->closed_slot == INVALID_CLOSED_SLOT || !_closed_slots[msg->closed_slot]) { msg->err = tcp_write(msg->pcb, msg->write.data, msg->write.size, msg->write.apiflags); @@ -539,7 +540,7 @@ static err_t _tcp_write_api(struct tcpip_api_call_data* api_call_msg) { return msg->err; } -static esp_err_t _tcp_write(tcp_pcb* pcb, int8_t closed_slot, const char* data, size_t size, uint8_t apiflags) { +static esp_err_t _tcp_write(tcp_pcb *pcb, int8_t closed_slot, const char *data, size_t size, uint8_t apiflags) { if (!pcb) { return ERR_CONN; } @@ -549,12 +550,12 @@ static esp_err_t _tcp_write(tcp_pcb* pcb, int8_t closed_slot, const char* data, msg.write.data = data; msg.write.size = size; msg.write.apiflags = apiflags; - tcpip_api_call(_tcp_write_api, (struct tcpip_api_call_data*)&msg); + tcpip_api_call(_tcp_write_api, (struct tcpip_api_call_data *)&msg); return msg.err; } -static err_t _tcp_recved_api(struct tcpip_api_call_data* api_call_msg) { - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; +static err_t _tcp_recved_api(struct tcpip_api_call_data *api_call_msg) { + tcp_api_call_t *msg = (tcp_api_call_t *)api_call_msg; msg->err = ERR_CONN; if (msg->closed_slot == INVALID_CLOSED_SLOT || !_closed_slots[msg->closed_slot]) { // if(msg->closed_slot != INVALID_CLOSED_SLOT && !_closed_slots[msg->closed_slot]) { @@ -565,7 +566,7 @@ static err_t _tcp_recved_api(struct tcpip_api_call_data* api_call_msg) { return msg->err; } -static esp_err_t _tcp_recved(tcp_pcb* pcb, int8_t closed_slot, size_t len) { +static esp_err_t _tcp_recved(tcp_pcb *pcb, int8_t closed_slot, size_t len) { if (!pcb) { return ERR_CONN; } @@ -573,12 +574,12 @@ static esp_err_t _tcp_recved(tcp_pcb* pcb, int8_t closed_slot, size_t len) { msg.pcb = pcb; msg.closed_slot = closed_slot; msg.received = len; - tcpip_api_call(_tcp_recved_api, (struct tcpip_api_call_data*)&msg); + tcpip_api_call(_tcp_recved_api, (struct tcpip_api_call_data *)&msg); return msg.err; } -static err_t _tcp_close_api(struct tcpip_api_call_data* api_call_msg) { - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; +static err_t _tcp_close_api(struct tcpip_api_call_data *api_call_msg) { + tcp_api_call_t *msg = (tcp_api_call_t *)api_call_msg; msg->err = ERR_CONN; if (msg->closed_slot == INVALID_CLOSED_SLOT || !_closed_slots[msg->closed_slot]) { msg->err = tcp_close(msg->pcb); @@ -586,19 +587,19 @@ static err_t _tcp_close_api(struct tcpip_api_call_data* api_call_msg) { return msg->err; } -static esp_err_t _tcp_close(tcp_pcb* pcb, int8_t closed_slot) { +static esp_err_t _tcp_close(tcp_pcb *pcb, int8_t closed_slot) { if (!pcb) { return ERR_CONN; } tcp_api_call_t msg; msg.pcb = pcb; msg.closed_slot = closed_slot; - tcpip_api_call(_tcp_close_api, (struct tcpip_api_call_data*)&msg); + tcpip_api_call(_tcp_close_api, (struct tcpip_api_call_data *)&msg); return msg.err; } -static err_t _tcp_abort_api(struct tcpip_api_call_data* api_call_msg) { - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; +static err_t _tcp_abort_api(struct tcpip_api_call_data *api_call_msg) { + tcp_api_call_t *msg = (tcp_api_call_t *)api_call_msg; msg->err = ERR_CONN; if (msg->closed_slot == INVALID_CLOSED_SLOT || !_closed_slots[msg->closed_slot]) { tcp_abort(msg->pcb); @@ -606,24 +607,24 @@ static err_t _tcp_abort_api(struct tcpip_api_call_data* api_call_msg) { return msg->err; } -static esp_err_t _tcp_abort(tcp_pcb* pcb, int8_t closed_slot) { +static esp_err_t _tcp_abort(tcp_pcb *pcb, int8_t closed_slot) { if (!pcb) { return ERR_CONN; } tcp_api_call_t msg; msg.pcb = pcb; msg.closed_slot = closed_slot; - tcpip_api_call(_tcp_abort_api, (struct tcpip_api_call_data*)&msg); + tcpip_api_call(_tcp_abort_api, (struct tcpip_api_call_data *)&msg); return msg.err; } -static err_t _tcp_connect_api(struct tcpip_api_call_data* api_call_msg) { - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; +static err_t _tcp_connect_api(struct tcpip_api_call_data *api_call_msg) { + tcp_api_call_t *msg = (tcp_api_call_t *)api_call_msg; msg->err = tcp_connect(msg->pcb, msg->connect.addr, msg->connect.port, msg->connect.cb); return msg->err; } -static esp_err_t _tcp_connect(tcp_pcb* pcb, int8_t closed_slot, ip_addr_t* addr, uint16_t port, tcp_connected_fn cb) { +static esp_err_t _tcp_connect(tcp_pcb *pcb, int8_t closed_slot, ip_addr_t *addr, uint16_t port, tcp_connected_fn cb) { if (!pcb) { return ESP_FAIL; } @@ -633,17 +634,17 @@ static esp_err_t _tcp_connect(tcp_pcb* pcb, int8_t closed_slot, ip_addr_t* addr, msg.connect.addr = addr; msg.connect.port = port; msg.connect.cb = cb; - tcpip_api_call(_tcp_connect_api, (struct tcpip_api_call_data*)&msg); + tcpip_api_call(_tcp_connect_api, (struct tcpip_api_call_data *)&msg); return msg.err; } -static err_t _tcp_bind_api(struct tcpip_api_call_data* api_call_msg) { - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; +static err_t _tcp_bind_api(struct tcpip_api_call_data *api_call_msg) { + tcp_api_call_t *msg = (tcp_api_call_t *)api_call_msg; msg->err = tcp_bind(msg->pcb, msg->bind.addr, msg->bind.port); return msg->err; } -static esp_err_t _tcp_bind(tcp_pcb* pcb, ip_addr_t* addr, uint16_t port) { +static esp_err_t _tcp_bind(tcp_pcb *pcb, ip_addr_t *addr, uint16_t port) { if (!pcb) { return ESP_FAIL; } @@ -652,18 +653,18 @@ static esp_err_t _tcp_bind(tcp_pcb* pcb, ip_addr_t* addr, uint16_t port) { msg.closed_slot = -1; msg.bind.addr = addr; msg.bind.port = port; - tcpip_api_call(_tcp_bind_api, (struct tcpip_api_call_data*)&msg); + tcpip_api_call(_tcp_bind_api, (struct tcpip_api_call_data *)&msg); return msg.err; } -static err_t _tcp_listen_api(struct tcpip_api_call_data* api_call_msg) { - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; +static err_t _tcp_listen_api(struct tcpip_api_call_data *api_call_msg) { + tcp_api_call_t *msg = (tcp_api_call_t *)api_call_msg; msg->err = 0; msg->pcb = tcp_listen_with_backlog(msg->pcb, msg->backlog); return msg->err; } -static tcp_pcb* _tcp_listen_with_backlog(tcp_pcb* pcb, uint8_t backlog) { +static tcp_pcb *_tcp_listen_with_backlog(tcp_pcb *pcb, uint8_t backlog) { if (!pcb) { return NULL; } @@ -671,7 +672,7 @@ static tcp_pcb* _tcp_listen_with_backlog(tcp_pcb* pcb, uint8_t backlog) { msg.pcb = pcb; msg.closed_slot = -1; msg.backlog = backlog ? backlog : 0xFF; - tcpip_api_call(_tcp_listen_api, (struct tcpip_api_call_data*)&msg); + tcpip_api_call(_tcp_listen_api, (struct tcpip_api_call_data *)&msg); return msg.pcb; } @@ -679,8 +680,10 @@ static tcp_pcb* _tcp_listen_with_backlog(tcp_pcb* pcb, uint8_t backlog) { Async TCP Client */ -AsyncClient::AsyncClient(tcp_pcb* pcb) - : _connect_cb(0), _connect_cb_arg(0), _discard_cb(0), _discard_cb_arg(0), _sent_cb(0), _sent_cb_arg(0), _error_cb(0), _error_cb_arg(0), _recv_cb(0), _recv_cb_arg(0), _pb_cb(0), _pb_cb_arg(0), _timeout_cb(0), _timeout_cb_arg(0), _ack_pcb(true), _tx_last_packet(0), _rx_timeout(0), _rx_last_ack(0), _ack_timeout(CONFIG_ASYNC_TCP_MAX_ACK_TIME), _connect_port(0), prev(NULL), next(NULL) { +AsyncClient::AsyncClient(tcp_pcb *pcb) + : _connect_cb(0), _connect_cb_arg(0), _discard_cb(0), _discard_cb_arg(0), _sent_cb(0), _sent_cb_arg(0), _error_cb(0), _error_cb_arg(0), _recv_cb(0), + _recv_cb_arg(0), _pb_cb(0), _pb_cb_arg(0), _timeout_cb(0), _timeout_cb_arg(0), _ack_pcb(true), _tx_last_packet(0), _rx_timeout(0), _rx_last_ack(0), + _ack_timeout(CONFIG_ASYNC_TCP_MAX_ACK_TIME), _connect_port(0), prev(NULL), next(NULL) { _pcb = pcb; _closed_slot = INVALID_CLOSED_SLOT; if (_pcb) { @@ -707,7 +710,7 @@ AsyncClient::~AsyncClient() { * Operators * */ -AsyncClient& AsyncClient::operator=(const AsyncClient& other) { +AsyncClient &AsyncClient::operator=(const AsyncClient &other) { if (_pcb) { _close(); } @@ -725,20 +728,20 @@ AsyncClient& AsyncClient::operator=(const AsyncClient& other) { return *this; } -bool AsyncClient::operator==(const AsyncClient& other) { +bool AsyncClient::operator==(const AsyncClient &other) { return _pcb == other._pcb; } -AsyncClient& AsyncClient::operator+=(const AsyncClient& other) { +AsyncClient &AsyncClient::operator+=(const AsyncClient &other) { if (next == NULL) { - next = (AsyncClient*)(&other); + next = (AsyncClient *)(&other); next->prev = this; } else { - AsyncClient* c = next; + AsyncClient *c = next; while (c->next != NULL) { c = c->next; } - c->next = (AsyncClient*)(&other); + c->next = (AsyncClient *)(&other); c->next->prev = c; } return *this; @@ -748,42 +751,42 @@ AsyncClient& AsyncClient::operator+=(const AsyncClient& other) { * Callback Setters * */ -void AsyncClient::onConnect(AcConnectHandler cb, void* arg) { +void AsyncClient::onConnect(AcConnectHandler cb, void *arg) { _connect_cb = cb; _connect_cb_arg = arg; } -void AsyncClient::onDisconnect(AcConnectHandler cb, void* arg) { +void AsyncClient::onDisconnect(AcConnectHandler cb, void *arg) { _discard_cb = cb; _discard_cb_arg = arg; } -void AsyncClient::onAck(AcAckHandler cb, void* arg) { +void AsyncClient::onAck(AcAckHandler cb, void *arg) { _sent_cb = cb; _sent_cb_arg = arg; } -void AsyncClient::onError(AcErrorHandler cb, void* arg) { +void AsyncClient::onError(AcErrorHandler cb, void *arg) { _error_cb = cb; _error_cb_arg = arg; } -void AsyncClient::onData(AcDataHandler cb, void* arg) { +void AsyncClient::onData(AcDataHandler cb, void *arg) { _recv_cb = cb; _recv_cb_arg = arg; } -void AsyncClient::onPacket(AcPacketHandler cb, void* arg) { +void AsyncClient::onPacket(AcPacketHandler cb, void *arg) { _pb_cb = cb; _pb_cb_arg = arg; } -void AsyncClient::onTimeout(AcTimeoutHandler cb, void* arg) { +void AsyncClient::onTimeout(AcTimeoutHandler cb, void *arg) { _timeout_cb = cb; _timeout_cb_arg = arg; } -void AsyncClient::onPoll(AcConnectHandler cb, void* arg) { +void AsyncClient::onPoll(AcConnectHandler cb, void *arg) { _poll_cb = cb; _poll_cb_arg = arg; } @@ -808,7 +811,7 @@ bool AsyncClient::_connect(ip_addr_t addr, uint16_t port) { } TCP_MUTEX_LOCK(); - tcp_pcb* pcb = tcp_new_ip_type(addr.type); + tcp_pcb *pcb = tcp_new_ip_type(addr.type); if (!pcb) { TCP_MUTEX_UNLOCK(); log_e("pcb == NULL"); @@ -825,7 +828,7 @@ bool AsyncClient::_connect(ip_addr_t addr, uint16_t port) { return err == ESP_OK; } -bool AsyncClient::connect(const IPAddress& ip, uint16_t port) { +bool AsyncClient::connect(const IPAddress &ip, uint16_t port) { ip_addr_t addr; #if ESP_IDF_VERSION_MAJOR < 5 addr.u_addr.ip4.addr = ip; @@ -838,15 +841,15 @@ bool AsyncClient::connect(const IPAddress& ip, uint16_t port) { } #if LWIP_IPV6 && ESP_IDF_VERSION_MAJOR < 5 -bool AsyncClient::connect(const IPv6Address& ip, uint16_t port) { - auto ipaddr = static_cast(ip); +bool AsyncClient::connect(const IPv6Address &ip, uint16_t port) { + auto ipaddr = static_cast(ip); ip_addr_t addr = IPADDR6_INIT(ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); return _connect(addr, port); } #endif -bool AsyncClient::connect(const char* host, uint16_t port) { +bool AsyncClient::connect(const char *host, uint16_t port) { ip_addr_t addr; if (!_start_async_task()) { @@ -859,14 +862,14 @@ bool AsyncClient::connect(const char* host, uint16_t port) { TCP_MUTEX_UNLOCK(); if (err == ERR_OK) { #if ESP_IDF_VERSION_MAJOR < 5 - #if LWIP_IPV6 +#if LWIP_IPV6 if (addr.type == IPADDR_TYPE_V6) { return connect(IPv6Address(addr.u_addr.ip6.addr), port); } return connect(IPAddress(addr.u_addr.ip4.addr), port); - #else +#else return connect(IPAddress(addr.addr), port); - #endif +#endif #else return _connect(addr, port); #endif @@ -900,7 +903,7 @@ size_t AsyncClient::space() { return 0; } -size_t AsyncClient::add(const char* data, size_t size, uint8_t apiflags) { +size_t AsyncClient::add(const char *data, size_t size, uint8_t apiflags) { if (!_pcb || size == 0 || data == NULL) { return 0; } @@ -928,8 +931,9 @@ bool AsyncClient::send() { } size_t AsyncClient::ack(size_t len) { - if (len > _rx_ack_len) + if (len > _rx_ack_len) { len = _rx_ack_len; + } if (len) { _tcp_recved(_pcb, _closed_slot, len); } @@ -937,7 +941,7 @@ size_t AsyncClient::ack(size_t len) { return len; } -void AsyncClient::ackPacket(struct pbuf* pb) { +void AsyncClient::ackPacket(struct pbuf *pb) { if (!pb) { return; } @@ -1007,8 +1011,8 @@ void AsyncClient::_free_closed_slot() { * Private Callbacks * */ -int8_t AsyncClient::_connected(tcp_pcb* pcb, int8_t err) { - _pcb = reinterpret_cast(pcb); +int8_t AsyncClient::_connected(tcp_pcb *pcb, int8_t err) { + _pcb = reinterpret_cast(pcb); if (_pcb) { _rx_last_packet = millis(); } @@ -1041,7 +1045,7 @@ void AsyncClient::_error(int8_t err) { } // In LwIP Thread -int8_t AsyncClient::_lwip_fin(tcp_pcb* pcb, int8_t err) { +int8_t AsyncClient::_lwip_fin(tcp_pcb *pcb, int8_t err) { if (!_pcb || pcb != _pcb) { log_d("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb); return ERR_OK; @@ -1062,7 +1066,7 @@ int8_t AsyncClient::_lwip_fin(tcp_pcb* pcb, int8_t err) { } // In Async Thread -int8_t AsyncClient::_fin(tcp_pcb* pcb, int8_t err) { +int8_t AsyncClient::_fin(tcp_pcb *pcb, int8_t err) { _tcp_clear_events(this); if (_discard_cb) { _discard_cb(_discard_cb_arg, this); @@ -1070,7 +1074,7 @@ int8_t AsyncClient::_fin(tcp_pcb* pcb, int8_t err) { return ERR_OK; } -int8_t AsyncClient::_sent(tcp_pcb* pcb, uint16_t len) { +int8_t AsyncClient::_sent(tcp_pcb *pcb, uint16_t len) { _rx_last_ack = _rx_last_packet = millis(); if (_sent_cb) { _sent_cb(_sent_cb_arg, this, len, (_rx_last_packet - _tx_last_packet)); @@ -1078,12 +1082,12 @@ int8_t AsyncClient::_sent(tcp_pcb* pcb, uint16_t len) { return ERR_OK; } -int8_t AsyncClient::_recv(tcp_pcb* pcb, pbuf* pb, int8_t err) { +int8_t AsyncClient::_recv(tcp_pcb *pcb, pbuf *pb, int8_t err) { while (pb != NULL) { _rx_last_packet = millis(); // we should not ack before we assimilate the data _ack_pcb = true; - pbuf* b = pb; + pbuf *b = pb; pb = b->next; b->next = NULL; if (_pb_cb) { @@ -1103,7 +1107,7 @@ int8_t AsyncClient::_recv(tcp_pcb* pcb, pbuf* pb, int8_t err) { return ERR_OK; } -int8_t AsyncClient::_poll(tcp_pcb* pcb) { +int8_t AsyncClient::_poll(tcp_pcb *pcb) { if (!_pcb) { // log_d("pcb is NULL"); return ERR_OK; @@ -1121,8 +1125,9 @@ int8_t AsyncClient::_poll(tcp_pcb* pcb) { bool last_tx_is_after_last_ack = (_rx_last_ack - _tx_last_packet + one_day) < one_day; if (last_tx_is_after_last_ack && (now - _tx_last_packet) >= _ack_timeout) { log_d("ack timeout %d", pcb->state); - if (_timeout_cb) + if (_timeout_cb) { _timeout_cb(_timeout_cb_arg, this, (now - _tx_last_packet)); + } return ERR_OK; } } @@ -1139,14 +1144,14 @@ int8_t AsyncClient::_poll(tcp_pcb* pcb) { return ERR_OK; } -void AsyncClient::_dns_found(struct ip_addr* ipaddr) { +void AsyncClient::_dns_found(struct ip_addr *ipaddr) { #if ESP_IDF_VERSION_MAJOR < 5 if (ipaddr && IP_IS_V4(ipaddr)) { connect(IPAddress(ip_addr_get_ip4_u32(ipaddr)), _connect_port); - #if LWIP_IPV6 +#if LWIP_IPV6 } else if (ipaddr && ipaddr->u_addr.ip6.addr) { connect(IPv6Address(ipaddr->u_addr.ip6.addr), _connect_port); - #endif +#endif #else if (ipaddr) { IPAddress ip; @@ -1177,7 +1182,7 @@ bool AsyncClient::free() { return false; } -size_t AsyncClient::write(const char* data, size_t size, uint8_t apiflags) { +size_t AsyncClient::write(const char *data, size_t size, uint8_t apiflags) { size_t will_send = add(data, size, apiflags); if (!will_send || !send()) { return 0; @@ -1221,13 +1226,13 @@ bool AsyncClient::getNoDelay() { void AsyncClient::setKeepAlive(uint32_t ms, uint8_t cnt) { if (ms != 0) { - _pcb->so_options |= SOF_KEEPALIVE; // Turn on TCP Keepalive for the given pcb + _pcb->so_options |= SOF_KEEPALIVE; // Turn on TCP Keepalive for the given pcb // Set the time between keepalive messages in milli-seconds _pcb->keep_idle = ms; _pcb->keep_intvl = ms; - _pcb->keep_cnt = cnt; // The number of unanswered probes required to force closure of the socket + _pcb->keep_cnt = cnt; // The number of unanswered probes required to force closure of the socket } else { - _pcb->so_options &= ~SOF_KEEPALIVE; // Turn off TCP Keepalive for the given pcb + _pcb->so_options &= ~SOF_KEEPALIVE; // Turn off TCP Keepalive for the given pcb } } @@ -1267,7 +1272,7 @@ ip6_addr_t AsyncClient::getLocalAddress6() { } return _pcb->local_ip.u_addr.ip6; } - #if ESP_IDF_VERSION_MAJOR < 5 +#if ESP_IDF_VERSION_MAJOR < 5 IPv6Address AsyncClient::remoteIP6() { return IPv6Address(getRemoteAddress6().addr); } @@ -1275,7 +1280,7 @@ IPv6Address AsyncClient::remoteIP6() { IPv6Address AsyncClient::localIP6() { return IPv6Address(getLocalAddress6().addr); } - #else +#else IPAddress AsyncClient::remoteIP6() { if (!_pcb) { return IPAddress(IPType::IPv6); @@ -1293,7 +1298,7 @@ IPAddress AsyncClient::localIP6() { ip.from_ip_addr_t(&(_pcb->local_ip)); return ip; } - #endif +#endif #endif uint16_t AsyncClient::getRemotePort() { @@ -1401,73 +1406,43 @@ bool AsyncClient::canSend() { return space() > 0; } -const char* AsyncClient::errorToString(int8_t error) { +const char *AsyncClient::errorToString(int8_t error) { switch (error) { - case ERR_OK: - return "OK"; - case ERR_MEM: - return "Out of memory error"; - case ERR_BUF: - return "Buffer error"; - case ERR_TIMEOUT: - return "Timeout"; - case ERR_RTE: - return "Routing problem"; - case ERR_INPROGRESS: - return "Operation in progress"; - case ERR_VAL: - return "Illegal value"; - case ERR_WOULDBLOCK: - return "Operation would block"; - case ERR_USE: - return "Address in use"; - case ERR_ALREADY: - return "Already connected"; - case ERR_CONN: - return "Not connected"; - case ERR_IF: - return "Low-level netif error"; - case ERR_ABRT: - return "Connection aborted"; - case ERR_RST: - return "Connection reset"; - case ERR_CLSD: - return "Connection closed"; - case ERR_ARG: - return "Illegal argument"; - case -55: - return "DNS failed"; - default: - return "UNKNOWN"; - } -} - -const char* AsyncClient::stateToString() { + case ERR_OK: return "OK"; + case ERR_MEM: return "Out of memory error"; + case ERR_BUF: return "Buffer error"; + case ERR_TIMEOUT: return "Timeout"; + case ERR_RTE: return "Routing problem"; + case ERR_INPROGRESS: return "Operation in progress"; + case ERR_VAL: return "Illegal value"; + case ERR_WOULDBLOCK: return "Operation would block"; + case ERR_USE: return "Address in use"; + case ERR_ALREADY: return "Already connected"; + case ERR_CONN: return "Not connected"; + case ERR_IF: return "Low-level netif error"; + case ERR_ABRT: return "Connection aborted"; + case ERR_RST: return "Connection reset"; + case ERR_CLSD: return "Connection closed"; + case ERR_ARG: return "Illegal argument"; + case -55: return "DNS failed"; + default: return "UNKNOWN"; + } +} + +const char *AsyncClient::stateToString() { switch (state()) { - case 0: - return "Closed"; - case 1: - return "Listen"; - case 2: - return "SYN Sent"; - case 3: - return "SYN Received"; - case 4: - return "Established"; - case 5: - return "FIN Wait 1"; - case 6: - return "FIN Wait 2"; - case 7: - return "Close Wait"; - case 8: - return "Closing"; - case 9: - return "Last ACK"; - case 10: - return "Time Wait"; - default: - return "UNKNOWN"; + case 0: return "Closed"; + case 1: return "Listen"; + case 2: return "SYN Sent"; + case 3: return "SYN Received"; + case 4: return "Established"; + case 5: return "FIN Wait 1"; + case 6: return "FIN Wait 2"; + case 7: return "Close Wait"; + case 8: return "Closing"; + case 9: return "Last ACK"; + case 10: return "Time Wait"; + default: return "UNKNOWN"; } } @@ -1475,36 +1450,36 @@ const char* AsyncClient::stateToString() { * Static Callbacks (LwIP C2C++ interconnect) * */ -void AsyncClient::_s_dns_found(const char* name, struct ip_addr* ipaddr, void* arg) { - reinterpret_cast(arg)->_dns_found(ipaddr); +void AsyncClient::_s_dns_found(const char *name, struct ip_addr *ipaddr, void *arg) { + reinterpret_cast(arg)->_dns_found(ipaddr); } -int8_t AsyncClient::_s_poll(void* arg, struct tcp_pcb* pcb) { - return reinterpret_cast(arg)->_poll(pcb); +int8_t AsyncClient::_s_poll(void *arg, struct tcp_pcb *pcb) { + return reinterpret_cast(arg)->_poll(pcb); } -int8_t AsyncClient::_s_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* pb, int8_t err) { - return reinterpret_cast(arg)->_recv(pcb, pb, err); +int8_t AsyncClient::_s_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *pb, int8_t err) { + return reinterpret_cast(arg)->_recv(pcb, pb, err); } -int8_t AsyncClient::_s_fin(void* arg, struct tcp_pcb* pcb, int8_t err) { - return reinterpret_cast(arg)->_fin(pcb, err); +int8_t AsyncClient::_s_fin(void *arg, struct tcp_pcb *pcb, int8_t err) { + return reinterpret_cast(arg)->_fin(pcb, err); } -int8_t AsyncClient::_s_lwip_fin(void* arg, struct tcp_pcb* pcb, int8_t err) { - return reinterpret_cast(arg)->_lwip_fin(pcb, err); +int8_t AsyncClient::_s_lwip_fin(void *arg, struct tcp_pcb *pcb, int8_t err) { + return reinterpret_cast(arg)->_lwip_fin(pcb, err); } -int8_t AsyncClient::_s_sent(void* arg, struct tcp_pcb* pcb, uint16_t len) { - return reinterpret_cast(arg)->_sent(pcb, len); +int8_t AsyncClient::_s_sent(void *arg, struct tcp_pcb *pcb, uint16_t len) { + return reinterpret_cast(arg)->_sent(pcb, len); } -void AsyncClient::_s_error(void* arg, int8_t err) { - reinterpret_cast(arg)->_error(err); +void AsyncClient::_s_error(void *arg, int8_t err) { + reinterpret_cast(arg)->_error(err); } -int8_t AsyncClient::_s_connected(void* arg, struct tcp_pcb* pcb, int8_t err) { - return reinterpret_cast(arg)->_connected(pcb, err); +int8_t AsyncClient::_s_connected(void *arg, struct tcp_pcb *pcb, int8_t err) { + return reinterpret_cast(arg)->_connected(pcb, err); } /* @@ -1512,38 +1487,38 @@ int8_t AsyncClient::_s_connected(void* arg, struct tcp_pcb* pcb, int8_t err) { */ AsyncServer::AsyncServer(IPAddress addr, uint16_t port) - : _port(port) + : _port(port) #if ESP_IDF_VERSION_MAJOR < 5 - , - _bind4(true), _bind6(false) + , + _bind4(true), _bind6(false) #else - , - _bind4(addr.type() != IPType::IPv6), _bind6(addr.type() == IPType::IPv6) + , + _bind4(addr.type() != IPType::IPv6), _bind6(addr.type() == IPType::IPv6) #endif - , - _addr(addr), _noDelay(false), _pcb(0), _connect_cb(0), _connect_cb_arg(0) { + , + _addr(addr), _noDelay(false), _pcb(0), _connect_cb(0), _connect_cb_arg(0) { } #if ESP_IDF_VERSION_MAJOR < 5 AsyncServer::AsyncServer(IPv6Address addr, uint16_t port) - : _port(port), _bind4(false), _bind6(true), _addr6(addr), _noDelay(false), _pcb(0), _connect_cb(0), _connect_cb_arg(0) {} + : _port(port), _bind4(false), _bind6(true), _addr6(addr), _noDelay(false), _pcb(0), _connect_cb(0), _connect_cb_arg(0) {} #endif AsyncServer::AsyncServer(uint16_t port) - : _port(port), _bind4(true), _bind6(false), _addr((uint32_t)IPADDR_ANY) + : _port(port), _bind4(true), _bind6(false), _addr((uint32_t)IPADDR_ANY) #if ESP_IDF_VERSION_MAJOR < 5 - , - _addr6() + , + _addr6() #endif - , - _noDelay(false), _pcb(0), _connect_cb(0), _connect_cb_arg(0) { + , + _noDelay(false), _pcb(0), _connect_cb(0), _connect_cb_arg(0) { } AsyncServer::~AsyncServer() { end(); } -void AsyncServer::onClient(AcConnectHandler cb, void* arg) { +void AsyncServer::onClient(AcConnectHandler cb, void *arg) { _connect_cb = cb; _connect_cb_arg = arg; } @@ -1568,9 +1543,9 @@ void AsyncServer::begin() { ip_addr_t local_addr; #if ESP_IDF_VERSION_MAJOR < 5 - if (_bind6) { // _bind6 && _bind4 both at the same time is not supported on Arduino 2 in this lib API + if (_bind6) { // _bind6 && _bind4 both at the same time is not supported on Arduino 2 in this lib API local_addr.type = IPADDR_TYPE_V6; - memcpy(local_addr.u_addr.ip6.addr, static_cast(_addr6), sizeof(uint32_t) * 4); + memcpy(local_addr.u_addr.ip6.addr, static_cast(_addr6), sizeof(uint32_t) * 4); } else { local_addr.type = IPADDR_TYPE_V4; local_addr.u_addr.ip4.addr = _addr; @@ -1593,7 +1568,7 @@ void AsyncServer::begin() { return; } TCP_MUTEX_LOCK(); - tcp_arg(_pcb, (void*)this); + tcp_arg(_pcb, (void *)this); tcp_accept(_pcb, &_s_accept); TCP_MUTEX_UNLOCK(); } @@ -1614,10 +1589,10 @@ void AsyncServer::end() { } // runs on LwIP thread -int8_t AsyncServer::_accept(tcp_pcb* pcb, int8_t err) { +int8_t AsyncServer::_accept(tcp_pcb *pcb, int8_t err) { // ets_printf("+A: 0x%08x\n", pcb); if (_connect_cb) { - AsyncClient* c = new AsyncClient(pcb); + AsyncClient *c = new AsyncClient(pcb); if (c) { c->setNoDelay(_noDelay); return _tcp_accept(this, c); @@ -1630,7 +1605,7 @@ int8_t AsyncServer::_accept(tcp_pcb* pcb, int8_t err) { return ERR_OK; } -int8_t AsyncServer::_accepted(AsyncClient* client) { +int8_t AsyncServer::_accepted(AsyncClient *client) { if (_connect_cb) { _connect_cb(_connect_cb_arg, client); } @@ -1652,10 +1627,10 @@ uint8_t AsyncServer::status() { return _pcb->state; } -int8_t AsyncServer::_s_accept(void* arg, tcp_pcb* pcb, int8_t err) { - return reinterpret_cast(arg)->_accept(pcb, err); +int8_t AsyncServer::_s_accept(void *arg, tcp_pcb *pcb, int8_t err) { + return reinterpret_cast(arg)->_accept(pcb, err); } -int8_t AsyncServer::_s_accepted(void* arg, AsyncClient* client) { - return reinterpret_cast(arg)->_accepted(client); +int8_t AsyncServer::_s_accepted(void *arg, AsyncClient *client) { + return reinterpret_cast(arg)->_accepted(client); } diff --git a/src/AsyncTCP.h b/src/AsyncTCP.h index 431785f..7607ec3 100644 --- a/src/AsyncTCP.h +++ b/src/AsyncTCP.h @@ -30,102 +30,104 @@ #include "IPAddress.h" #if ESP_IDF_VERSION_MAJOR < 5 - #include "IPv6Address.h" +#include "IPv6Address.h" #endif #include "lwip/ip6_addr.h" #include "lwip/ip_addr.h" #include #ifndef LIBRETINY - #include "sdkconfig.h" +#include "sdkconfig.h" extern "C" { - #include "freertos/semphr.h" - #include "lwip/pbuf.h" +#include "freertos/semphr.h" +#include "lwip/pbuf.h" } #else extern "C" { - #include - #include +#include +#include } - #define CONFIG_ASYNC_TCP_RUNNING_CORE -1 // any available core +#define CONFIG_ASYNC_TCP_RUNNING_CORE -1 // any available core #endif // If core is not defined, then we are running in Arduino or PIO #ifndef CONFIG_ASYNC_TCP_RUNNING_CORE - #define CONFIG_ASYNC_TCP_RUNNING_CORE -1 // any available core +#define CONFIG_ASYNC_TCP_RUNNING_CORE -1 // any available core #endif // guard AsyncTCP task with watchdog #ifndef CONFIG_ASYNC_TCP_USE_WDT - #define CONFIG_ASYNC_TCP_USE_WDT 1 +#define CONFIG_ASYNC_TCP_USE_WDT 1 #endif #ifndef CONFIG_ASYNC_TCP_STACK_SIZE - #define CONFIG_ASYNC_TCP_STACK_SIZE 8192 * 2 +#define CONFIG_ASYNC_TCP_STACK_SIZE 8192 * 2 #endif #ifndef CONFIG_ASYNC_TCP_PRIORITY - #define CONFIG_ASYNC_TCP_PRIORITY 10 +#define CONFIG_ASYNC_TCP_PRIORITY 10 #endif #ifndef CONFIG_ASYNC_TCP_QUEUE_SIZE - #define CONFIG_ASYNC_TCP_QUEUE_SIZE 64 +#define CONFIG_ASYNC_TCP_QUEUE_SIZE 64 #endif #ifndef CONFIG_ASYNC_TCP_MAX_ACK_TIME - #define CONFIG_ASYNC_TCP_MAX_ACK_TIME 5000 +#define CONFIG_ASYNC_TCP_MAX_ACK_TIME 5000 #endif class AsyncClient; -#define ASYNC_WRITE_FLAG_COPY 0x01 // will allocate new buffer to hold the data while sending (else will hold reference to the data given) -#define ASYNC_WRITE_FLAG_MORE 0x02 // will not send PSH flag, meaning that there should be more data to be sent before the application should react. +#define ASYNC_WRITE_FLAG_COPY 0x01 // will allocate new buffer to hold the data while sending (else will hold reference to the data given) +#define ASYNC_WRITE_FLAG_MORE 0x02 // will not send PSH flag, meaning that there should be more data to be sent before the application should react. -typedef std::function AcConnectHandler; -typedef std::function AcAckHandler; -typedef std::function AcErrorHandler; -typedef std::function AcDataHandler; -typedef std::function AcPacketHandler; -typedef std::function AcTimeoutHandler; +typedef std::function AcConnectHandler; +typedef std::function AcAckHandler; +typedef std::function AcErrorHandler; +typedef std::function AcDataHandler; +typedef std::function AcPacketHandler; +typedef std::function AcTimeoutHandler; struct tcp_pcb; struct ip_addr; class AsyncClient { - public: - AsyncClient(tcp_pcb* pcb = 0); - ~AsyncClient(); +public: + AsyncClient(tcp_pcb *pcb = 0); + ~AsyncClient(); - AsyncClient& operator=(const AsyncClient& other); - AsyncClient& operator+=(const AsyncClient& other); + AsyncClient &operator=(const AsyncClient &other); + AsyncClient &operator+=(const AsyncClient &other); - bool operator==(const AsyncClient& other); + bool operator==(const AsyncClient &other); - bool operator!=(const AsyncClient& other) { - return !(*this == other); - } - bool connect(const IPAddress& ip, uint16_t port); + bool operator!=(const AsyncClient &other) { + return !(*this == other); + } + bool connect(const IPAddress &ip, uint16_t port); #if ESP_IDF_VERSION_MAJOR < 5 - bool connect(const IPv6Address& ip, uint16_t port); + bool connect(const IPv6Address &ip, uint16_t port); #endif - bool connect(const char* host, uint16_t port); - /** + bool connect(const char *host, uint16_t port); + /** * @brief close connection * * @param now - ignored */ - void close(bool now = false); - // same as close() - void stop() { close(false); }; - int8_t abort(); - bool free(); - - // ack is not pending - bool canSend(); - // TCP buffer space available - size_t space(); - - /** + void close(bool now = false); + // same as close() + void stop() { + close(false); + }; + int8_t abort(); + bool free(); + + // ack is not pending + bool canSend(); + // TCP buffer space available + size_t space(); + + /** * @brief add data to be send (but do not send yet) * @note add() would call lwip's tcp_write() By default apiflags=ASYNC_WRITE_FLAG_COPY @@ -140,17 +142,17 @@ class AsyncClient { * @param apiflags * @return size_t amount of data that has been copied */ - size_t add(const char* data, size_t size, uint8_t apiflags = ASYNC_WRITE_FLAG_COPY); + size_t add(const char *data, size_t size, uint8_t apiflags = ASYNC_WRITE_FLAG_COPY); - /** + /** * @brief send data previously add()'ed * * @return true on success * @return false on error */ - bool send(); + bool send(); - /** + /** * @brief add and enqueue data for sending * @note it is same as add() + send() * @note only make sense when canSend() == true @@ -160,185 +162,191 @@ class AsyncClient { * @param apiflags * @return size_t */ - size_t write(const char* data, size_t size, uint8_t apiflags = ASYNC_WRITE_FLAG_COPY); + size_t write(const char *data, size_t size, uint8_t apiflags = ASYNC_WRITE_FLAG_COPY); - /** - * @brief add and enque data for sending + /** + * @brief add and enqueue data for sending * @note treats data as null-terminated string * * @param data * @return size_t */ - size_t write(const char* data) { return data == NULL ? 0 : write(data, strlen(data)); }; + size_t write(const char *data) { + return data == NULL ? 0 : write(data, strlen(data)); + }; - uint8_t state(); - bool connecting(); - bool connected(); - bool disconnecting(); - bool disconnected(); + uint8_t state(); + bool connecting(); + bool connected(); + bool disconnecting(); + bool disconnected(); - // disconnected or disconnecting - bool freeable(); + // disconnected or disconnecting + bool freeable(); - uint16_t getMss(); + uint16_t getMss(); - uint32_t getRxTimeout(); - // no RX data timeout for the connection in seconds - void setRxTimeout(uint32_t timeout); + uint32_t getRxTimeout(); + // no RX data timeout for the connection in seconds + void setRxTimeout(uint32_t timeout); - uint32_t getAckTimeout(); - // no ACK timeout for the last sent packet in milliseconds - void setAckTimeout(uint32_t timeout); + uint32_t getAckTimeout(); + // no ACK timeout for the last sent packet in milliseconds + void setAckTimeout(uint32_t timeout); - void setNoDelay(bool nodelay); - bool getNoDelay(); + void setNoDelay(bool nodelay); + bool getNoDelay(); - void setKeepAlive(uint32_t ms, uint8_t cnt); + void setKeepAlive(uint32_t ms, uint8_t cnt); - uint32_t getRemoteAddress(); - uint16_t getRemotePort(); - uint32_t getLocalAddress(); - uint16_t getLocalPort(); + uint32_t getRemoteAddress(); + uint16_t getRemotePort(); + uint32_t getLocalAddress(); + uint16_t getLocalPort(); #if LWIP_IPV6 - ip6_addr_t getRemoteAddress6(); - ip6_addr_t getLocalAddress6(); - #if ESP_IDF_VERSION_MAJOR < 5 - IPv6Address remoteIP6(); - IPv6Address localIP6(); - #else - IPAddress remoteIP6(); - IPAddress localIP6(); - #endif + ip6_addr_t getRemoteAddress6(); + ip6_addr_t getLocalAddress6(); +#if ESP_IDF_VERSION_MAJOR < 5 + IPv6Address remoteIP6(); + IPv6Address localIP6(); +#else + IPAddress remoteIP6(); + IPAddress localIP6(); +#endif #endif - // compatibility - IPAddress remoteIP(); - uint16_t remotePort(); - IPAddress localIP(); - uint16_t localPort(); - - // set callback - on successful connect - void onConnect(AcConnectHandler cb, void* arg = 0); - // set callback - disconnected - void onDisconnect(AcConnectHandler cb, void* arg = 0); - // set callback - ack received - void onAck(AcAckHandler cb, void* arg = 0); - // set callback - unsuccessful connect or error - void onError(AcErrorHandler cb, void* arg = 0); - // set callback - data received (called if onPacket is not used) - void onData(AcDataHandler cb, void* arg = 0); - // set callback - data received - void onPacket(AcPacketHandler cb, void* arg = 0); - // set callback - ack timeout - void onTimeout(AcTimeoutHandler cb, void* arg = 0); - // set callback - every 125ms when connected - void onPoll(AcConnectHandler cb, void* arg = 0); - - // ack pbuf from onPacket - void ackPacket(struct pbuf* pb); - // ack data that you have not acked using the method below - size_t ack(size_t len); - // will not ack the current packet. Call from onData - void ackLater() { _ack_pcb = false; } - - static const char* errorToString(int8_t error); - const char* stateToString(); - - // internal callbacks - Do NOT call any of the functions below in user code! - static int8_t _s_poll(void* arg, struct tcp_pcb* tpcb); - static int8_t _s_recv(void* arg, struct tcp_pcb* tpcb, struct pbuf* pb, int8_t err); - static int8_t _s_fin(void* arg, struct tcp_pcb* tpcb, int8_t err); - static int8_t _s_lwip_fin(void* arg, struct tcp_pcb* tpcb, int8_t err); - static void _s_error(void* arg, int8_t err); - static int8_t _s_sent(void* arg, struct tcp_pcb* tpcb, uint16_t len); - static int8_t _s_connected(void* arg, struct tcp_pcb* tpcb, int8_t err); - static void _s_dns_found(const char* name, struct ip_addr* ipaddr, void* arg); - - int8_t _recv(tcp_pcb* pcb, pbuf* pb, int8_t err); - tcp_pcb* pcb() { return _pcb; } - - protected: - bool _connect(ip_addr_t addr, uint16_t port); - - tcp_pcb* _pcb; - int8_t _closed_slot; - - AcConnectHandler _connect_cb; - void* _connect_cb_arg; - AcConnectHandler _discard_cb; - void* _discard_cb_arg; - AcAckHandler _sent_cb; - void* _sent_cb_arg; - AcErrorHandler _error_cb; - void* _error_cb_arg; - AcDataHandler _recv_cb; - void* _recv_cb_arg; - AcPacketHandler _pb_cb; - void* _pb_cb_arg; - AcTimeoutHandler _timeout_cb; - void* _timeout_cb_arg; - AcConnectHandler _poll_cb; - void* _poll_cb_arg; - - bool _ack_pcb; - uint32_t _tx_last_packet; - uint32_t _rx_ack_len; - uint32_t _rx_last_packet; - uint32_t _rx_timeout; - uint32_t _rx_last_ack; - uint32_t _ack_timeout; - uint16_t _connect_port; - - int8_t _close(); - void _free_closed_slot(); - bool _allocate_closed_slot(); - int8_t _connected(tcp_pcb* pcb, int8_t err); - void _error(int8_t err); - int8_t _poll(tcp_pcb* pcb); - int8_t _sent(tcp_pcb* pcb, uint16_t len); - int8_t _fin(tcp_pcb* pcb, int8_t err); - int8_t _lwip_fin(tcp_pcb* pcb, int8_t err); - void _dns_found(struct ip_addr* ipaddr); - - public: - AsyncClient* prev; - AsyncClient* next; + // compatibility + IPAddress remoteIP(); + uint16_t remotePort(); + IPAddress localIP(); + uint16_t localPort(); + + // set callback - on successful connect + void onConnect(AcConnectHandler cb, void *arg = 0); + // set callback - disconnected + void onDisconnect(AcConnectHandler cb, void *arg = 0); + // set callback - ack received + void onAck(AcAckHandler cb, void *arg = 0); + // set callback - unsuccessful connect or error + void onError(AcErrorHandler cb, void *arg = 0); + // set callback - data received (called if onPacket is not used) + void onData(AcDataHandler cb, void *arg = 0); + // set callback - data received + void onPacket(AcPacketHandler cb, void *arg = 0); + // set callback - ack timeout + void onTimeout(AcTimeoutHandler cb, void *arg = 0); + // set callback - every 125ms when connected + void onPoll(AcConnectHandler cb, void *arg = 0); + + // ack pbuf from onPacket + void ackPacket(struct pbuf *pb); + // ack data that you have not acked using the method below + size_t ack(size_t len); + // will not ack the current packet. Call from onData + void ackLater() { + _ack_pcb = false; + } + + static const char *errorToString(int8_t error); + const char *stateToString(); + + // internal callbacks - Do NOT call any of the functions below in user code! + static int8_t _s_poll(void *arg, struct tcp_pcb *tpcb); + static int8_t _s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, int8_t err); + static int8_t _s_fin(void *arg, struct tcp_pcb *tpcb, int8_t err); + static int8_t _s_lwip_fin(void *arg, struct tcp_pcb *tpcb, int8_t err); + static void _s_error(void *arg, int8_t err); + static int8_t _s_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len); + static int8_t _s_connected(void *arg, struct tcp_pcb *tpcb, int8_t err); + static void _s_dns_found(const char *name, struct ip_addr *ipaddr, void *arg); + + int8_t _recv(tcp_pcb *pcb, pbuf *pb, int8_t err); + tcp_pcb *pcb() { + return _pcb; + } + +protected: + bool _connect(ip_addr_t addr, uint16_t port); + + tcp_pcb *_pcb; + int8_t _closed_slot; + + AcConnectHandler _connect_cb; + void *_connect_cb_arg; + AcConnectHandler _discard_cb; + void *_discard_cb_arg; + AcAckHandler _sent_cb; + void *_sent_cb_arg; + AcErrorHandler _error_cb; + void *_error_cb_arg; + AcDataHandler _recv_cb; + void *_recv_cb_arg; + AcPacketHandler _pb_cb; + void *_pb_cb_arg; + AcTimeoutHandler _timeout_cb; + void *_timeout_cb_arg; + AcConnectHandler _poll_cb; + void *_poll_cb_arg; + + bool _ack_pcb; + uint32_t _tx_last_packet; + uint32_t _rx_ack_len; + uint32_t _rx_last_packet; + uint32_t _rx_timeout; + uint32_t _rx_last_ack; + uint32_t _ack_timeout; + uint16_t _connect_port; + + int8_t _close(); + void _free_closed_slot(); + bool _allocate_closed_slot(); + int8_t _connected(tcp_pcb *pcb, int8_t err); + void _error(int8_t err); + int8_t _poll(tcp_pcb *pcb); + int8_t _sent(tcp_pcb *pcb, uint16_t len); + int8_t _fin(tcp_pcb *pcb, int8_t err); + int8_t _lwip_fin(tcp_pcb *pcb, int8_t err); + void _dns_found(struct ip_addr *ipaddr); + +public: + AsyncClient *prev; + AsyncClient *next; }; class AsyncServer { - public: - AsyncServer(IPAddress addr, uint16_t port); +public: + AsyncServer(IPAddress addr, uint16_t port); #if ESP_IDF_VERSION_MAJOR < 5 - AsyncServer(IPv6Address addr, uint16_t port); + AsyncServer(IPv6Address addr, uint16_t port); #endif - AsyncServer(uint16_t port); - ~AsyncServer(); - void onClient(AcConnectHandler cb, void* arg); - void begin(); - void end(); - void setNoDelay(bool nodelay); - bool getNoDelay(); - uint8_t status(); - - // Do not use any of the functions below! - static int8_t _s_accept(void* arg, tcp_pcb* newpcb, int8_t err); - static int8_t _s_accepted(void* arg, AsyncClient* client); - - protected: - uint16_t _port; - bool _bind4 = false; - bool _bind6 = false; - IPAddress _addr; + AsyncServer(uint16_t port); + ~AsyncServer(); + void onClient(AcConnectHandler cb, void *arg); + void begin(); + void end(); + void setNoDelay(bool nodelay); + bool getNoDelay(); + uint8_t status(); + + // Do not use any of the functions below! + static int8_t _s_accept(void *arg, tcp_pcb *newpcb, int8_t err); + static int8_t _s_accepted(void *arg, AsyncClient *client); + +protected: + uint16_t _port; + bool _bind4 = false; + bool _bind6 = false; + IPAddress _addr; #if ESP_IDF_VERSION_MAJOR < 5 - IPv6Address _addr6; + IPv6Address _addr6; #endif - bool _noDelay; - tcp_pcb* _pcb; - AcConnectHandler _connect_cb; - void* _connect_cb_arg; + bool _noDelay; + tcp_pcb *_pcb; + AcConnectHandler _connect_cb; + void *_connect_cb_arg; - int8_t _accept(tcp_pcb* newpcb, int8_t err); - int8_t _accepted(AsyncClient* client); + int8_t _accept(tcp_pcb *newpcb, int8_t err); + int8_t _accepted(AsyncClient *client); }; #endif /* ASYNCTCP_H_ */ From 6751baee082af9ecb273f0a7893aa14541cfdf70 Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Mon, 27 Jan 2025 12:27:38 +0200 Subject: [PATCH 16/32] Add support for ESP-IDF component registry --- .github/workflows/upload-idf-component.yml | 50 +++++++++++ idf_component.yml | 28 +++++++ idf_component_examples/client/CMakeLists.txt | 8 ++ .../client/main/CMakeLists.txt | 2 + .../client/main/idf_component.yml | 6 ++ idf_component_examples/client/main/main.cpp | 83 +++++++++++++++++++ .../client/sdkconfig.defaults | 12 +++ 7 files changed, 189 insertions(+) create mode 100644 .github/workflows/upload-idf-component.yml create mode 100644 idf_component.yml create mode 100644 idf_component_examples/client/CMakeLists.txt create mode 100644 idf_component_examples/client/main/CMakeLists.txt create mode 100644 idf_component_examples/client/main/idf_component.yml create mode 100644 idf_component_examples/client/main/main.cpp create mode 100644 idf_component_examples/client/sdkconfig.defaults diff --git a/.github/workflows/upload-idf-component.yml b/.github/workflows/upload-idf-component.yml new file mode 100644 index 0000000..ec64e6f --- /dev/null +++ b/.github/workflows/upload-idf-component.yml @@ -0,0 +1,50 @@ +name: Publish ESP-IDF Component + +on: + workflow_dispatch: + inputs: + tag: + description: 'Version to push to the component registry' + required: true + git_ref: + description: 'Git ref with the source to push to the component registry' + required: true + +permissions: + contents: read + +jobs: + upload_components: + runs-on: ubuntu-latest + steps: + - name: Get the release tag + env: + head_branch: ${{ inputs.tag || github.event.workflow_run.head_branch }} + run: | + # Read and sanitize the branch/tag name + branch=$(echo "$head_branch" | tr -cd '[:alnum:]/_.-') + + if [[ $branch == refs/tags/* ]]; then + tag="${branch#refs/tags/}" + elif [[ $branch =~ ^[0-9]+\.[0-9]+\.[0-9]+.*$ ]]; then + tag=$branch + else + echo "Tag not found in $branch. Exiting..." + exit 1 + fi + + echo "Tag: $tag" + echo "RELEASE_TAG=$tag" >> $GITHUB_ENV + + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.git_ref || env.RELEASE_TAG }} + submodules: "recursive" + + - name: Upload components to the component registry + uses: espressif/upload-components-ci-action@v1 + with: + name: asynctcp + version: ${{ env.RELEASE_TAG }} + namespace: esp32async + api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }} diff --git a/idf_component.yml b/idf_component.yml new file mode 100644 index 0000000..b7cc074 --- /dev/null +++ b/idf_component.yml @@ -0,0 +1,28 @@ +description: "Async TCP Library for ESP32 Arduino" +url: "https://github.com/ESP32Async/AsyncTCP" +license: "LGPL-3.0-or-later" +tags: + - arduino +files: + exclude: + - "idf_component_examples/" + - "idf_component_examples/**/*" + - "examples/" + - "examples/**/*" + - ".gitignore" + - ".clang-format" + - ".gitpod.Dockerfile" + - ".gitpod.yml" + - "arduino-cli.yml" + - "arduino-cli-dev.yml" + - "CODE_OF_CONDUCT.md" + - "component.mk" + - "library.json" + - "library.properties" + - "platformio.ini" +dependencies: + espressif/arduino-esp32: + version: "^3.1.1" + require: public +examples: + - path: ./idf_component_examples/client diff --git a/idf_component_examples/client/CMakeLists.txt b/idf_component_examples/client/CMakeLists.txt new file mode 100644 index 0000000..664d458 --- /dev/null +++ b/idf_component_examples/client/CMakeLists.txt @@ -0,0 +1,8 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(main) diff --git a/idf_component_examples/client/main/CMakeLists.txt b/idf_component_examples/client/main/CMakeLists.txt new file mode 100644 index 0000000..9eb7ec4 --- /dev/null +++ b/idf_component_examples/client/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.cpp" + INCLUDE_DIRS ".") diff --git a/idf_component_examples/client/main/idf_component.yml b/idf_component_examples/client/main/idf_component.yml new file mode 100644 index 0000000..5a8dff8 --- /dev/null +++ b/idf_component_examples/client/main/idf_component.yml @@ -0,0 +1,6 @@ +## IDF Component Manager Manifest File +dependencies: + esp32async/asynctcp: + version: "*" + override_path: "../../../" + pre_release: true diff --git a/idf_component_examples/client/main/main.cpp b/idf_component_examples/client/main/main.cpp new file mode 100644 index 0000000..95633e4 --- /dev/null +++ b/idf_component_examples/client/main/main.cpp @@ -0,0 +1,83 @@ +#include "Arduino.h" +#include "AsyncTCP.h" +#include "WiFi.h" + +// Run a server at the root of the project with: +// > python3 -m http.server 3333 +// Now you can open a browser and test it works by visiting http://192.168.125.122:3333/ or http://192.168.125.122:3333/README.md +#define HOST "192.168.125.122" +#define PORT 3333 + +// WiFi SSID to connect to +#define WIFI_SSID "*********" +#define WIFI_PASS "*********" + +// 16 slots on esp32 (CONFIG_LWIP_MAX_ACTIVE_TCP) +#define MAX_CLIENTS CONFIG_LWIP_MAX_ACTIVE_TCP +// #define MAX_CLIENTS 1 + +size_t permits = MAX_CLIENTS; + +void makeRequest() { + if (!permits) { + return; + } + + Serial.printf("** permits: %d\n", permits); + + AsyncClient *client = new AsyncClient; + + client->onError([](void *arg, AsyncClient *client, int8_t error) { + Serial.printf("** error occurred %s \n", client->errorToString(error)); + client->close(true); + delete client; + }); + + client->onConnect([](void *arg, AsyncClient *client) { + permits--; + Serial.printf("** client has been connected: %" PRIu16 "\n", client->localPort()); + + client->onDisconnect([](void *arg, AsyncClient *client) { + Serial.printf("** client has been disconnected: %" PRIu16 "\n", client->localPort()); + client->close(true); + delete client; + + permits++; + makeRequest(); + }); + + client->onData([](void *arg, AsyncClient *client, void *data, size_t len) { + Serial.printf("** data received by client: %" PRIu16 ": len=%u\n", client->localPort(), len); + }); + + client->write("GET /README.md HTTP/1.1\r\nHost: " HOST "\r\nUser-Agent: ESP\r\nConnection: close\r\n\r\n"); + }); + + if (!client->connect(HOST, PORT)) { + Serial.println("** connection failed"); + } +} + +void setup() { + Serial.begin(115200); + + Serial.print("Connecting to "); + Serial.print(WIFI_SSID); + WiFi.begin(WIFI_SSID, WIFI_PASS); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(); + Serial.print("Connected to WiFi. IP: "); + Serial.println(WiFi.localIP()); + + for (size_t i = 0; i < MAX_CLIENTS; i++) { + makeRequest(); + } +} + +void loop() { + delay(1000); + Serial.printf("** free heap: %" PRIu32 "\n", ESP.getFreeHeap()); +} diff --git a/idf_component_examples/client/sdkconfig.defaults b/idf_component_examples/client/sdkconfig.defaults new file mode 100644 index 0000000..bb72365 --- /dev/null +++ b/idf_component_examples/client/sdkconfig.defaults @@ -0,0 +1,12 @@ +# +# Arduino ESP32 +# +CONFIG_AUTOSTART_ARDUINO=y +# end of Arduino ESP32 + +# +# FREERTOS +# +CONFIG_FREERTOS_HZ=1000 +# end of FREERTOS +# end of Component config From 07e919541b0e001023e2779040bc38619dd8d26f Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Sat, 25 Jan 2025 21:51:37 +0100 Subject: [PATCH 17/32] Introduce AsyncTCPVersion.h to more easily manage and use library version --- src/AsyncTCP.h | 5 +---- src/AsyncTCPVersion.h | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 src/AsyncTCPVersion.h diff --git a/src/AsyncTCP.h b/src/AsyncTCP.h index 7607ec3..dd11017 100644 --- a/src/AsyncTCP.h +++ b/src/AsyncTCP.h @@ -22,10 +22,7 @@ #ifndef ASYNCTCP_H_ #define ASYNCTCP_H_ -#define ASYNCTCP_VERSION "3.3.2" -#define ASYNCTCP_VERSION_MAJOR 3 -#define ASYNCTCP_VERSION_MINOR 3 -#define ASYNCTCP_VERSION_REVISION 2 +#include "AsyncTCPVersion.h" #define ASYNCTCP_FORK_ESP32Async #include "IPAddress.h" diff --git a/src/AsyncTCPVersion.h b/src/AsyncTCPVersion.h new file mode 100644 index 0000000..33b0790 --- /dev/null +++ b/src/AsyncTCPVersion.h @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** Major version number (X.x.x) */ +#define ASYNCTCP_VERSION_MAJOR 3 +/** Minor version number (x.X.x) */ +#define ASYNCTCP_VERSION_MINOR 3 +/** Patch version number (x.x.X) */ +#define ASYNCTCP_VERSION_PATCH 2 + +/** + * Macro to convert version number into an integer + * + * To be used in comparisons, such as ASYNCTCP_VERSION >= ASYNCTCP_VERSION_VAL(2, 0, 0) + */ +#define ASYNCTCP_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) + +/** + * Current version, as an integer + * + * To be used in comparisons, such as ASYNCTCP_VERSION_NUM >= ASYNCTCP_VERSION_VAL(2, 0, 0) + */ +#define ASYNCTCP_VERSION_NUM ASYNCTCP_VERSION_VAL(ASYNCTCP_VERSION_MAJOR, ASYNCTCP_VERSION_MINOR, ASYNCTCP_VERSION_PATCH) + +/** + * Current version, as string + */ +#define df2xstr(s) #s +#define df2str(s) df2xstr(s) +#define ASYNCTCP_VERSION df2str(ASYNCTCP_VERSION_MAJOR) "." df2str(ASYNCTCP_VERSION_MINOR) "." df2str(ASYNCTCP_VERSION_PATCH) + +#ifdef __cplusplus +} +#endif From 45cf45c724f73486952f000741bc098f70b1c247 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2025 16:27:34 +0000 Subject: [PATCH 18/32] ci(pre-commit): Apply automatic fixes --- src/AsyncTCPVersion.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AsyncTCPVersion.h b/src/AsyncTCPVersion.h index 33b0790..2c1148a 100644 --- a/src/AsyncTCPVersion.h +++ b/src/AsyncTCPVersion.h @@ -31,8 +31,8 @@ extern "C" { /** * Current version, as string */ -#define df2xstr(s) #s -#define df2str(s) df2xstr(s) +#define df2xstr(s) #s +#define df2str(s) df2xstr(s) #define ASYNCTCP_VERSION df2str(ASYNCTCP_VERSION_MAJOR) "." df2str(ASYNCTCP_VERSION_MINOR) "." df2str(ASYNCTCP_VERSION_PATCH) #ifdef __cplusplus From 8f22115713c75620955431c1623693e681299c4c Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Sat, 25 Jan 2025 21:54:30 +0100 Subject: [PATCH 19/32] Update license headers The project is under LGPL license so the source files should reflect that by having a license header compliant with LGPL, or at least have an appropriate SPDX header. This changed is required to allow the source files to be used in downstream project because some files are not licensed in accordance to the LPGL rules. Refs: - https://www.gnu.org/licenses/gpl-howto.html - https://spdx.dev/learn/handling-license-info/ - https://spdx.org/licenses/LGPL-3.0-or-later.html --- examples/Client/Client.ino | 3 +++ idf_component_examples/client/main/main.cpp | 3 +++ src/AsyncTCP.cpp | 22 ++------------------- src/AsyncTCP.h | 22 ++------------------- 4 files changed, 10 insertions(+), 40 deletions(-) diff --git a/examples/Client/Client.ino b/examples/Client/Client.ino index 61987af..abdfba8 100644 --- a/examples/Client/Client.ino +++ b/examples/Client/Client.ino @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov + #include #include #include diff --git a/idf_component_examples/client/main/main.cpp b/idf_component_examples/client/main/main.cpp index 95633e4..83b2d99 100644 --- a/idf_component_examples/client/main/main.cpp +++ b/idf_component_examples/client/main/main.cpp @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov + #include "Arduino.h" #include "AsyncTCP.h" #include "WiFi.h" diff --git a/src/AsyncTCP.cpp b/src/AsyncTCP.cpp index 6498b5c..d15e08e 100644 --- a/src/AsyncTCP.cpp +++ b/src/AsyncTCP.cpp @@ -1,23 +1,5 @@ -/* - Asynchronous TCP library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov #include "Arduino.h" diff --git a/src/AsyncTCP.h b/src/AsyncTCP.h index dd11017..e46abd0 100644 --- a/src/AsyncTCP.h +++ b/src/AsyncTCP.h @@ -1,23 +1,5 @@ -/* - Asynchronous TCP library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov #ifndef ASYNCTCP_H_ #define ASYNCTCP_H_ From 82105232e6fb8896ea74d68579eb94ff0748de34 Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Mon, 27 Jan 2025 23:36:38 +0200 Subject: [PATCH 20/32] Add fixes suggested by DeepSeek --- src/AsyncTCP.cpp | 171 ++++++++++++++++++++++++++++------------------- src/AsyncTCP.h | 1 + 2 files changed, 104 insertions(+), 68 deletions(-) diff --git a/src/AsyncTCP.cpp b/src/AsyncTCP.cpp index d15e08e..72abb27 100644 --- a/src/AsyncTCP.cpp +++ b/src/AsyncTCP.cpp @@ -102,14 +102,15 @@ typedef struct { }; } lwip_tcp_event_packet_t; -static QueueHandle_t _async_queue; +static QueueHandle_t _async_queue = NULL; static TaskHandle_t _async_service_task_handle = NULL; -SemaphoreHandle_t _slots_lock; -const int _number_of_closed_slots = CONFIG_LWIP_MAX_ACTIVE_TCP; +static SemaphoreHandle_t _slots_lock = NULL; +static const int _number_of_closed_slots = CONFIG_LWIP_MAX_ACTIVE_TCP; static uint32_t _closed_slots[_number_of_closed_slots]; static uint32_t _closed_index = []() { _slots_lock = xSemaphoreCreateBinary(); + configASSERT(_slots_lock); // Add sanity check xSemaphoreGive(_slots_lock); for (int i = 0; i < _number_of_closed_slots; ++i) { _closed_slots[i] = 1; @@ -136,67 +137,67 @@ static inline bool _prepend_async_event(lwip_tcp_event_packet_t **e, TickType_t } static inline bool _get_async_event(lwip_tcp_event_packet_t **e) { - if (!_async_queue) { - return false; - } + while (true) { + if (!_async_queue) { + break; + } #if CONFIG_ASYNC_TCP_USE_WDT - // need to return periodically to feed the dog - if (xQueueReceive(_async_queue, e, pdMS_TO_TICKS(1000)) != pdPASS) { - return false; - } + // need to return periodically to feed the dog + if (xQueueReceive(_async_queue, e, pdMS_TO_TICKS(1000)) != pdPASS) { + break; + } #else - if (xQueueReceive(_async_queue, e, portMAX_DELAY) != pdPASS) { - return false; - } + if (xQueueReceive(_async_queue, e, portMAX_DELAY) != pdPASS) { + break; + } #endif - if ((*e)->event != LWIP_TCP_POLL) { - return true; - } + if ((*e)->event != LWIP_TCP_POLL) { + return true; + } - /* - Let's try to coalesce two (or more) consecutive poll events into one - this usually happens with poor implemented user-callbacks that are runs too long and makes poll events to stack in the queue - if consecutive user callback for a same connection runs longer that poll time then it will fill the queue with events until it deadlocks. - This is a workaround to mitigate such poor designs and won't let other events/connections to starve the task time. - It won't be effective if user would run multiple simultaneous long running callbacks due to message interleaving. - todo: implement some kind of fair dequeuing or (better) simply punish user for a bad designed callbacks by resetting hog connections - */ - lwip_tcp_event_packet_t *next_pkt = NULL; - while (xQueuePeek(_async_queue, &next_pkt, 0) == pdPASS) { - if (next_pkt->arg == (*e)->arg && next_pkt->event == LWIP_TCP_POLL) { - if (xQueueReceive(_async_queue, &next_pkt, 0) == pdPASS) { - free(next_pkt); - next_pkt = NULL; - log_d("coalescing polls, network congestion or async callbacks might be too slow!"); - continue; + /* + Let's try to coalesce two (or more) consecutive poll events into one + this usually happens with poor implemented user-callbacks that are runs too long and makes poll events to stack in the queue + if consecutive user callback for a same connection runs longer that poll time then it will fill the queue with events until it deadlocks. + This is a workaround to mitigate such poor designs and won't let other events/connections to starve the task time. + It won't be effective if user would run multiple simultaneous long running callbacks due to message interleaving. + todo: implement some kind of fair dequeuing or (better) simply punish user for a bad designed callbacks by resetting hog connections + */ + lwip_tcp_event_packet_t *next_pkt = NULL; + while (xQueuePeek(_async_queue, &next_pkt, 0) == pdPASS) { + if (next_pkt->arg == (*e)->arg && next_pkt->event == LWIP_TCP_POLL) { + if (xQueueReceive(_async_queue, &next_pkt, 0) == pdPASS) { + free(next_pkt); + next_pkt = NULL; + log_d("coalescing polls, network congestion or async callbacks might be too slow!"); + continue; + } } - } - // quit while loop if next event can't be discarded - break; - } + // quit while loop if next event can't be discarded + break; + } - /* - now we have to decide if to proceed with poll callback handler or discard it? - poor designed apps using asynctcp without proper dataflow control could flood the queue with interleaved pool/ack events. - I.e. on each poll app would try to generate more data to send, which in turn results in additional ack event triggering chain effect - for long connections. Or poll callback could take long time starving other connections. Anyway our goal is to keep the queue length - grows under control (if possible) and poll events are the safest to discard. - Let's discard poll events processing using linear-increasing probability curve when queue size grows over 3/4 - Poll events are periodic and connection could get another chance next time - */ - if (uxQueueMessagesWaiting(_async_queue) > (rand() % CONFIG_ASYNC_TCP_QUEUE_SIZE / 4 + CONFIG_ASYNC_TCP_QUEUE_SIZE * 3 / 4)) { - free(*e); - *e = NULL; - log_d("discarding poll due to queue congestion"); - // evict next event from a queue - return _get_async_event(e); + /* + now we have to decide if to proceed with poll callback handler or discard it? + poor designed apps using asynctcp without proper dataflow control could flood the queue with interleaved pool/ack events. + I.e. on each poll app would try to generate more data to send, which in turn results in additional ack event triggering chain effect + for long connections. Or poll callback could take long time starving other connections. Anyway our goal is to keep the queue length + grows under control (if possible) and poll events are the safest to discard. + Let's discard poll events processing using linear-increasing probability curve when queue size grows over 3/4 + Poll events are periodic and connection could get another chance next time + */ + if (uxQueueMessagesWaiting(_async_queue) > (rand() % CONFIG_ASYNC_TCP_QUEUE_SIZE / 4 + CONFIG_ASYNC_TCP_QUEUE_SIZE * 3 / 4)) { + free(*e); + *e = NULL; + log_d("discarding poll due to queue congestion"); + continue; // Retry + } + return true; } - - // last resort return - return true; + return false; } static bool _remove_events_with_arg(void *arg) { @@ -213,7 +214,7 @@ static bool _remove_events_with_arg(void *arg) { return false; } // discard packet if matching - if ((int)first_packet->arg == (int)arg) { + if ((uintptr_t)first_packet->arg == (uintptr_t)arg) { free(first_packet); first_packet = NULL; } else if (xQueueSend(_async_queue, &first_packet, 0) != pdPASS) { @@ -230,7 +231,7 @@ static bool _remove_events_with_arg(void *arg) { if (xQueueReceive(_async_queue, &packet, 0) != pdPASS) { return false; } - if ((int)packet->arg == (int)arg) { + if ((uintptr_t)packet->arg == (uintptr_t)arg) { // remove matching event free(packet); packet = NULL; @@ -346,6 +347,10 @@ static bool _start_async_task() { static int8_t _tcp_clear_events(void *arg) { lwip_tcp_event_packet_t *e = (lwip_tcp_event_packet_t *)malloc(sizeof(lwip_tcp_event_packet_t)); + if (!e) { + log_e("Failed to allocate event packet"); + return ERR_MEM; + } e->event = LWIP_TCP_CLEAR; e->arg = arg; if (!_prepend_async_event(&e)) { @@ -357,6 +362,10 @@ static int8_t _tcp_clear_events(void *arg) { static int8_t _tcp_connected(void *arg, tcp_pcb *pcb, int8_t err) { // ets_printf("+C: 0x%08x\n", pcb); lwip_tcp_event_packet_t *e = (lwip_tcp_event_packet_t *)malloc(sizeof(lwip_tcp_event_packet_t)); + if (!e) { + log_e("Failed to allocate event packet"); + return ERR_MEM; + } e->event = LWIP_TCP_CONNECTED; e->arg = arg; e->connected.pcb = pcb; @@ -377,6 +386,10 @@ static int8_t _tcp_poll(void *arg, struct tcp_pcb *pcb) { // ets_printf("+P: 0x%08x\n", pcb); lwip_tcp_event_packet_t *e = (lwip_tcp_event_packet_t *)malloc(sizeof(lwip_tcp_event_packet_t)); + if (!e) { + log_e("Failed to allocate event packet"); + return ERR_MEM; + } e->event = LWIP_TCP_POLL; e->arg = arg; e->poll.pcb = pcb; @@ -389,6 +402,10 @@ static int8_t _tcp_poll(void *arg, struct tcp_pcb *pcb) { static int8_t _tcp_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *pb, int8_t err) { lwip_tcp_event_packet_t *e = (lwip_tcp_event_packet_t *)malloc(sizeof(lwip_tcp_event_packet_t)); + if (!e) { + log_e("Failed to allocate event packet"); + return ERR_MEM; + } e->arg = arg; if (pb) { // ets_printf("+R: 0x%08x\n", pcb); @@ -413,6 +430,10 @@ static int8_t _tcp_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *pb, int8_t static int8_t _tcp_sent(void *arg, struct tcp_pcb *pcb, uint16_t len) { // ets_printf("+S: 0x%08x\n", pcb); lwip_tcp_event_packet_t *e = (lwip_tcp_event_packet_t *)malloc(sizeof(lwip_tcp_event_packet_t)); + if (!e) { + log_e("Failed to allocate event packet"); + return ERR_MEM; + } e->event = LWIP_TCP_SENT; e->arg = arg; e->sent.pcb = pcb; @@ -426,6 +447,10 @@ static int8_t _tcp_sent(void *arg, struct tcp_pcb *pcb, uint16_t len) { static void _tcp_error(void *arg, int8_t err) { // ets_printf("+E: 0x%08x\n", arg); lwip_tcp_event_packet_t *e = (lwip_tcp_event_packet_t *)malloc(sizeof(lwip_tcp_event_packet_t)); + if (!e) { + log_e("Failed to allocate event packet"); + return; + } e->event = LWIP_TCP_ERROR; e->arg = arg; e->error.err = err; @@ -436,6 +461,10 @@ static void _tcp_error(void *arg, int8_t err) { static void _tcp_dns_found(const char *name, struct ip_addr *ipaddr, void *arg) { lwip_tcp_event_packet_t *e = (lwip_tcp_event_packet_t *)malloc(sizeof(lwip_tcp_event_packet_t)); + if (!e) { + log_e("Failed to allocate event packet"); + return; + } // ets_printf("+DNS: name=%s ipaddr=0x%08x arg=%x\n", name, ipaddr, arg); e->event = LWIP_TCP_DNS; e->arg = arg; @@ -453,6 +482,10 @@ static void _tcp_dns_found(const char *name, struct ip_addr *ipaddr, void *arg) // Used to switch out from LwIP thread static int8_t _tcp_accept(void *arg, AsyncClient *client) { lwip_tcp_event_packet_t *e = (lwip_tcp_event_packet_t *)malloc(sizeof(lwip_tcp_event_packet_t)); + if (!e) { + log_e("Failed to allocate event packet"); + return ERR_MEM; + } e->event = LWIP_TCP_ACCEPT; e->arg = arg; e->accept.client = client; @@ -664,8 +697,8 @@ static tcp_pcb *_tcp_listen_with_backlog(tcp_pcb *pcb, uint8_t backlog) { AsyncClient::AsyncClient(tcp_pcb *pcb) : _connect_cb(0), _connect_cb_arg(0), _discard_cb(0), _discard_cb_arg(0), _sent_cb(0), _sent_cb_arg(0), _error_cb(0), _error_cb_arg(0), _recv_cb(0), - _recv_cb_arg(0), _pb_cb(0), _pb_cb_arg(0), _timeout_cb(0), _timeout_cb_arg(0), _ack_pcb(true), _tx_last_packet(0), _rx_timeout(0), _rx_last_ack(0), - _ack_timeout(CONFIG_ASYNC_TCP_MAX_ACK_TIME), _connect_port(0), prev(NULL), next(NULL) { + _recv_cb_arg(0), _pb_cb(0), _pb_cb_arg(0), _timeout_cb(0), _timeout_cb_arg(0), _poll_cb(0), _poll_cb_arg(0), _ack_pcb(true), _tx_last_packet(0), + _rx_timeout(0), _rx_last_ack(0), _ack_timeout(CONFIG_ASYNC_TCP_MAX_ACK_TIME), _connect_port(0), prev(NULL), next(NULL) { _pcb = pcb; _closed_slot = INVALID_CLOSED_SLOT; if (_pcb) { @@ -964,18 +997,19 @@ bool AsyncClient::_allocate_closed_slot() { if (_closed_slot != INVALID_CLOSED_SLOT) { return true; } - xSemaphoreTake(_slots_lock, portMAX_DELAY); - uint32_t closed_slot_min_index = 0; - for (int i = 0; i < _number_of_closed_slots; ++i) { - if ((_closed_slot == INVALID_CLOSED_SLOT || _closed_slots[i] <= closed_slot_min_index) && _closed_slots[i] != 0) { - closed_slot_min_index = _closed_slots[i]; - _closed_slot = i; + if (xSemaphoreTake(_slots_lock, portMAX_DELAY) == pdTRUE) { + uint32_t closed_slot_min_index = 0; + for (int i = 0; i < _number_of_closed_slots; ++i) { + if ((_closed_slot == INVALID_CLOSED_SLOT || _closed_slots[i] <= closed_slot_min_index) && _closed_slots[i] != 0) { + closed_slot_min_index = _closed_slots[i]; + _closed_slot = i; + } } + if (_closed_slot != INVALID_CLOSED_SLOT) { + _closed_slots[_closed_slot] = 0; + } + xSemaphoreGive(_slots_lock); } - if (_closed_slot != INVALID_CLOSED_SLOT) { - _closed_slots[_closed_slot] = 0; - } - xSemaphoreGive(_slots_lock); return (_closed_slot != INVALID_CLOSED_SLOT); } @@ -1539,6 +1573,7 @@ void AsyncServer::begin() { if (err != ERR_OK) { _tcp_close(_pcb, -1); + _pcb = NULL; log_e("bind error: %d", err); return; } diff --git a/src/AsyncTCP.h b/src/AsyncTCP.h index e46abd0..389692f 100644 --- a/src/AsyncTCP.h +++ b/src/AsyncTCP.h @@ -211,6 +211,7 @@ class AsyncClient { // set callback - data received (called if onPacket is not used) void onData(AcDataHandler cb, void *arg = 0); // set callback - data received + // !!! You MUST call ackPacket() or free the pbuf yourself to prevent memory leaks void onPacket(AcPacketHandler cb, void *arg = 0); // set callback - ack timeout void onTimeout(AcTimeoutHandler cb, void *arg = 0); From a65d158c5f3bcb20e3427988acaf963622adbbe3 Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Tue, 28 Jan 2025 23:42:57 +0200 Subject: [PATCH 21/32] Prevent concurrency in _allocate_closed_slot --- src/AsyncTCP.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/AsyncTCP.cpp b/src/AsyncTCP.cpp index 72abb27..932af57 100644 --- a/src/AsyncTCP.cpp +++ b/src/AsyncTCP.cpp @@ -994,23 +994,25 @@ int8_t AsyncClient::_close() { } bool AsyncClient::_allocate_closed_slot() { - if (_closed_slot != INVALID_CLOSED_SLOT) { - return true; - } + bool allocated = false; if (xSemaphoreTake(_slots_lock, portMAX_DELAY) == pdTRUE) { uint32_t closed_slot_min_index = 0; - for (int i = 0; i < _number_of_closed_slots; ++i) { - if ((_closed_slot == INVALID_CLOSED_SLOT || _closed_slots[i] <= closed_slot_min_index) && _closed_slots[i] != 0) { - closed_slot_min_index = _closed_slots[i]; - _closed_slot = i; + allocated = _closed_slot != INVALID_CLOSED_SLOT; + if (!allocated) { + for (int i = 0; i < _number_of_closed_slots; ++i) { + if ((_closed_slot == INVALID_CLOSED_SLOT || _closed_slots[i] <= closed_slot_min_index) && _closed_slots[i] != 0) { + closed_slot_min_index = _closed_slots[i]; + _closed_slot = i; + } + } + allocated = _closed_slot != INVALID_CLOSED_SLOT; + if (allocated) { + _closed_slots[_closed_slot] = 0; } - } - if (_closed_slot != INVALID_CLOSED_SLOT) { - _closed_slots[_closed_slot] = 0; } xSemaphoreGive(_slots_lock); } - return (_closed_slot != INVALID_CLOSED_SLOT); + return allocated; } void AsyncClient::_free_closed_slot() { From 74e15f544e37bd816c90329e65501cbd2f95dd1b Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Tue, 28 Jan 2025 23:21:36 +0100 Subject: [PATCH 22/32] (ci): Add update-version.sh script to prepare a next release --- .github/scripts/update-version.sh | 40 +++++++++++++++++++++++++++++++ README.md | 14 ++++------- 2 files changed, 44 insertions(+), 10 deletions(-) create mode 100755 .github/scripts/update-version.sh diff --git a/.github/scripts/update-version.sh b/.github/scripts/update-version.sh new file mode 100755 index 0000000..95044e8 --- /dev/null +++ b/.github/scripts/update-version.sh @@ -0,0 +1,40 @@ + +#!/bin/bash +# shellcheck disable=SC2002 + +# fail the script if any command unexpectedly fails +set -e + +if [ ! $# -eq 3 ]; then + echo "Bad number of arguments: $#" >&2 + echo "usage: $0 " >&2 + exit 1 +fi + +re='^[0-9]+$' +if [[ ! $1 =~ $re ]] || [[ ! $2 =~ $re ]] || [[ ! $3 =~ $re ]] ; then + echo "error: Not a valid version: $1.$2.$3" >&2 + echo "usage: $0 " >&2 + exit 1 +fi + +ASYNCTCP_VERSION_MAJOR="$1" +ASYNCTCP_VERSION_MINOR="$2" +ASYNCTCP_VERSION_PATCH="$3" +ASYNCTCP_VERSION="$ASYNCTCP_VERSION_MAJOR.$ASYNCTCP_VERSION_MINOR.$ASYNCTCP_VERSION_PATCH" + +echo "New AsyncTCP version: $ASYNCTCP_VERSION" + +echo "Updating library.properties..." +cat library.properties | sed "s/version=.*/version=$ASYNCTCP_VERSION/g" > __library.properties && mv __library.properties library.properties + +echo "Updating library.json..." +cat library.json | sed "s/^ \"version\":.*/ \"version\": \"$ASYNCTCP_VERSION\",/g" > __library.json && mv __library.json library.json + +echo "Updating src/AsyncTCPVersion.h..." +cat src/AsyncTCPVersion.h | \ +sed "s/#define ASYNCTCP_VERSION_MAJOR.*/#define ASYNCTCP_VERSION_MAJOR $ASYNCTCP_VERSION_MAJOR/g" | \ +sed "s/#define ASYNCTCP_VERSION_MINOR.*/#define ASYNCTCP_VERSION_MINOR $ASYNCTCP_VERSION_MINOR/g" | \ +sed "s/#define ASYNCTCP_VERSION_PATCH.*/#define ASYNCTCP_VERSION_PATCH $ASYNCTCP_VERSION_PATCH/g" > src/__AsyncTCPVersion.h && mv src/__AsyncTCPVersion.h src/AsyncTCPVersion.h + +exit 0 \ No newline at end of file diff --git a/README.md b/README.md index f098ef5..9d17311 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,11 @@ Discord Server: [https://discord.gg/X7zpGdyUcY](https://discord.gg/X7zpGdyUcY) Please see the new links: -- `ESP32Async/ESPAsyncWebServer @ 3.6.2` (ESP32, ESP8266, RP2040) -- `ESP32Async/AsyncTCP @ 3.3.2` (ESP32) -- `ESP32Async/ESPAsyncTCP @ 2.0.0` (ESP8266) +- `ESP32Async/ESPAsyncWebServer` (ESP32, ESP8266, RP2040) +- `ESP32Async/AsyncTCP` (ESP32) +- `ESP32Async/ESPAsyncTCP` (ESP8266) - `https://github.com/ESP32Async/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip` (AsyncTCP alternative for ESP32) -- `khoih-prog/AsyncTCP_RP2040W @ 1.2.0` (RP2040) +- `khoih-prog/AsyncTCP_RP2040W` (RP2040) ### Async TCP Library for ESP32 Arduino @@ -50,12 +50,6 @@ The base classes on which everything else is built. They expose all possible sce - Use IPADDR6_INIT() macro to set connecting IPv6 address - xTaskCreateUniversal function -## Coordinates - -``` -ESP32Async/AsyncTCP @ ^3.3.2 -``` - ## Important recommendations Most of the crashes are caused by improper configuration of the library for the project. From afc39c18eee65a7e1d8ef809feb93764918b3de7 Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Wed, 29 Jan 2025 12:08:17 +0200 Subject: [PATCH 23/32] Update library version to 3.3.3 --- library.json | 2 +- library.properties | 2 +- src/AsyncTCPVersion.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library.json b/library.json index 7e41f19..85f09ca 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "AsyncTCP", - "version": "3.3.2", + "version": "3.3.3", "description": "Asynchronous TCP Library for ESP32", "keywords": "async,tcp", "repository": { diff --git a/library.properties b/library.properties index 6bde633..0543aef 100644 --- a/library.properties +++ b/library.properties @@ -1,6 +1,6 @@ name=Async TCP includes=AsyncTCP.h -version=3.3.2 +version=3.3.3 author=ESP32Async maintainer=ESP32Async sentence=Async TCP Library for ESP32 diff --git a/src/AsyncTCPVersion.h b/src/AsyncTCPVersion.h index 2c1148a..78f0cf9 100644 --- a/src/AsyncTCPVersion.h +++ b/src/AsyncTCPVersion.h @@ -12,7 +12,7 @@ extern "C" { /** Minor version number (x.X.x) */ #define ASYNCTCP_VERSION_MINOR 3 /** Patch version number (x.x.X) */ -#define ASYNCTCP_VERSION_PATCH 2 +#define ASYNCTCP_VERSION_PATCH 3 /** * Macro to convert version number into an integer From 432aa4d42844a38665f66faf82e0a953fa1b88e9 Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Wed, 29 Jan 2025 12:26:26 +0200 Subject: [PATCH 24/32] Fix IDF component publishing and example --- .github/workflows/upload-idf-component.yml | 2 +- idf_component.yml | 8 ++++-- idf_component_examples/client/README.md | 1 + idf_component_examples/client/main/main.cpp | 30 +++++++++------------ 4 files changed, 20 insertions(+), 21 deletions(-) create mode 100644 idf_component_examples/client/README.md diff --git a/.github/workflows/upload-idf-component.yml b/.github/workflows/upload-idf-component.yml index ec64e6f..708cc69 100644 --- a/.github/workflows/upload-idf-component.yml +++ b/.github/workflows/upload-idf-component.yml @@ -26,7 +26,7 @@ jobs: if [[ $branch == refs/tags/* ]]; then tag="${branch#refs/tags/}" - elif [[ $branch =~ ^[0-9]+\.[0-9]+\.[0-9]+.*$ ]]; then + elif [[ $branch =~ ^[v]*[0-9]+\.[0-9]+\.[0-9]+.*$ ]]; then tag=$branch else echo "Tag not found in $branch. Exiting..." diff --git a/idf_component.yml b/idf_component.yml index b7cc074..10c0478 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -13,13 +13,17 @@ files: - ".clang-format" - ".gitpod.Dockerfile" - ".gitpod.yml" - - "arduino-cli.yml" - - "arduino-cli-dev.yml" + - ".codespellrc" + - ".editorconfig" + - ".pre-commit-config.yaml" + - "arduino-cli.yaml" + - "arduino-cli-dev.yaml" - "CODE_OF_CONDUCT.md" - "component.mk" - "library.json" - "library.properties" - "platformio.ini" + - "pre-commit.requirements.txt" dependencies: espressif/arduino-esp32: version: "^3.1.1" diff --git a/idf_component_examples/client/README.md b/idf_component_examples/client/README.md new file mode 100644 index 0000000..e409b28 --- /dev/null +++ b/idf_component_examples/client/README.md @@ -0,0 +1 @@ +### Basic example to show how AsyncTCP client works diff --git a/idf_component_examples/client/main/main.cpp b/idf_component_examples/client/main/main.cpp index 83b2d99..7bbbf4c 100644 --- a/idf_component_examples/client/main/main.cpp +++ b/idf_component_examples/client/main/main.cpp @@ -15,38 +15,32 @@ #define WIFI_SSID "*********" #define WIFI_PASS "*********" -// 16 slots on esp32 (CONFIG_LWIP_MAX_ACTIVE_TCP) -#define MAX_CLIENTS CONFIG_LWIP_MAX_ACTIVE_TCP -// #define MAX_CLIENTS 1 - -size_t permits = MAX_CLIENTS; +bool client_running = false; void makeRequest() { - if (!permits) { + client_running = true; + AsyncClient *client = new AsyncClient; + if (client == nullptr) { + Serial.println("** could not allocate client"); + client_running = false; return; } - Serial.printf("** permits: %d\n", permits); - - AsyncClient *client = new AsyncClient; - client->onError([](void *arg, AsyncClient *client, int8_t error) { Serial.printf("** error occurred %s \n", client->errorToString(error)); client->close(true); delete client; + client_running = false; }); client->onConnect([](void *arg, AsyncClient *client) { - permits--; Serial.printf("** client has been connected: %" PRIu16 "\n", client->localPort()); client->onDisconnect([](void *arg, AsyncClient *client) { Serial.printf("** client has been disconnected: %" PRIu16 "\n", client->localPort()); client->close(true); delete client; - - permits++; - makeRequest(); + client_running = false; }); client->onData([](void *arg, AsyncClient *client, void *data, size_t len) { @@ -58,6 +52,7 @@ void makeRequest() { if (!client->connect(HOST, PORT)) { Serial.println("** connection failed"); + client_running = false; } } @@ -74,13 +69,12 @@ void setup() { Serial.println(); Serial.print("Connected to WiFi. IP: "); Serial.println(WiFi.localIP()); - - for (size_t i = 0; i < MAX_CLIENTS; i++) { - makeRequest(); - } } void loop() { + if (!client_running) { + makeRequest(); + } delay(1000); Serial.printf("** free heap: %" PRIu32 "\n", ESP.getFreeHeap()); } From d90a3d8f217c5a05d293339eb00fd2c0eed56401 Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Wed, 29 Jan 2025 11:27:12 +0100 Subject: [PATCH 25/32] (ci): Rename workflow filename to publish-pio-registry.yml --- .github/workflows/{publish.yml => publish-pio-registry.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{publish.yml => publish-pio-registry.yml} (100%) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish-pio-registry.yml similarity index 100% rename from .github/workflows/publish.yml rename to .github/workflows/publish-pio-registry.yml From d57e3721ac08479bfb4981858d10d8b2fa4b42ca Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Wed, 29 Jan 2025 11:45:36 +0100 Subject: [PATCH 26/32] (doc) Documentation update --- README.md | 55 ++++++++++++++++++++++--------------------------------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 9d17311..edda626 100644 --- a/README.md +++ b/README.md @@ -6,50 +6,34 @@ [![Continuous Integration](https://github.com/ESP32Async/AsyncTCP/actions/workflows/ci.yml/badge.svg)](https://github.com/ESP32Async/AsyncTCP/actions/workflows/ci.yml) [![PlatformIO Registry](https://badges.registry.platformio.org/packages/ESP32Async/library/AsyncTCP.svg)](https://registry.platformio.org/libraries/ESP32Async/AsyncTCP) -Project moved to [ESP32Async](https://github.com/ESP32Async) organization at [https://github.com/ESP32Async/AsyncTCP](https://github.com/ESP32Async/AsyncTCP) - Discord Server: [https://discord.gg/X7zpGdyUcY](https://discord.gg/X7zpGdyUcY) -Please see the new links: - -- `ESP32Async/ESPAsyncWebServer` (ESP32, ESP8266, RP2040) -- `ESP32Async/AsyncTCP` (ESP32) -- `ESP32Async/ESPAsyncTCP` (ESP8266) -- `https://github.com/ESP32Async/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip` (AsyncTCP alternative for ESP32) -- `khoih-prog/AsyncTCP_RP2040W` (RP2040) - -### Async TCP Library for ESP32 Arduino +## Async TCP Library for ESP32 Arduino This is a fully asynchronous TCP library, aimed at enabling trouble-free, multi-connection network environment for Espressif's ESP32 MCUs. This library is the base for [ESPAsyncWebServer](https://github.com/ESP32Async/ESPAsyncWebServer) +## How to install + +The library can be downloaded from the releases page at [https://github.com/ESP32Async/AsyncTCP/releases](https://github.com/ESP32Async/AsyncTCP/releases). + +It is also deployed in these registries: + +- Arduino Library Registry: [https://github.com/arduino/library-registry](https://github.com/arduino/library-registry) + +- ESP Component Registry [https://components.espressif.com/components/esp32async/asynctcp/](https://components.espressif.com/components/esp32async/asynctcp/) + +- PlatformIO Registry: [https://registry.platformio.org/libraries/esp32async/AsyncTCP](https://registry.platformio.org/libraries/esp32async/AsyncTCP) + + - Use: `lib_deps=ESP32Async/AsyncTCP` to point to latest version + - Use: `lib_deps=ESP32Async/AsyncTCP @ ^` to point to latest version with the same major version + - Use: `lib_deps=ESP32Async/AsyncTCP @ ` to always point to the same version (reproductible build) + ## AsyncClient and AsyncServer The base classes on which everything else is built. They expose all possible scenarios, but are really raw and require more skills to use. -## Changes - -- `library.properties` for Arduino IDE users -- Add `CONFIG_ASYNC_TCP_MAX_ACK_TIME` -- Add `CONFIG_ASYNC_TCP_PRIORITY` -- Add `CONFIG_ASYNC_TCP_QUEUE_SIZE` -- Add `setKeepAlive()` -- Arduino 3 / ESP-IDF 5 compatibility -- Better CI -- Better example -- Customizable macros -- Fix for "Required to lock TCPIP core functionality". Ref: https://github.com/ESP32Async/AsyncTCP/issues/27 and https://github.com/espressif/arduino-esp32/issues/10526 -- Fix for "ack timeout 4" client disconnects. -- Fix from https://github.com/me-no-dev/AsyncTCP/pull/173 (partially applied) -- Fix from https://github.com/me-no-dev/AsyncTCP/pull/184 -- IPv6 -- LIBRETINY support -- LibreTuya -- Reduce logging of non critical messages -- Use IPADDR6_INIT() macro to set connecting IPv6 address -- xTaskCreateUniversal function - ## Important recommendations Most of the crashes are caused by improper configuration of the library for the project. @@ -64,3 +48,8 @@ I personally use the following configuration in my projects: -D CONFIG_ASYNC_TCP_RUNNING_CORE=1 // force async_tcp task to be on same core as the app (default is core 0) -D CONFIG_ASYNC_TCP_STACK_SIZE=4096 // reduce the stack size (default is 16K) ``` + +## Compatibility + +- ESP32 +- Arduino Core 2.x and 3.x From 0879085cbc731c6e7265a239a22da39cc4538998 Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Thu, 30 Jan 2025 09:25:46 +0200 Subject: [PATCH 27/32] Update component upload workflow inputs --- .github/workflows/upload-idf-component.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/upload-idf-component.yml b/.github/workflows/upload-idf-component.yml index 708cc69..71228f0 100644 --- a/.github/workflows/upload-idf-component.yml +++ b/.github/workflows/upload-idf-component.yml @@ -4,10 +4,10 @@ on: workflow_dispatch: inputs: tag: - description: 'Version to push to the component registry' + description: 'Component version (1.2.3, 1.2.3-rc1 or 1.2.3.4)' required: true git_ref: - description: 'Git ref with the source to push to the component registry' + description: 'Git ref with the source (branch, tag or commit)' required: true permissions: From 99d07d0118dc1de9549b718d94af65a279cc3ea6 Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Thu, 30 Jan 2025 20:56:28 +0100 Subject: [PATCH 28/32] New issue report template --- .github/ISSUE_TEMPLATE/issue-report.yml | 79 +++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/issue.md | 33 ----------- 2 files changed, 79 insertions(+), 33 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/issue-report.yml delete mode 100644 .github/ISSUE_TEMPLATE/issue.md diff --git a/.github/ISSUE_TEMPLATE/issue-report.yml b/.github/ISSUE_TEMPLATE/issue-report.yml new file mode 100644 index 0000000..c48fd1a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/issue-report.yml @@ -0,0 +1,79 @@ +name: 🐛 Bug Report +description: File a bug report +labels: ["Status: Awaiting triage"] +body: + - type: markdown + attributes: + value: > + ### ⚠️ Please remember: issues are for *bugs* only! + + - type: markdown + attributes: + value: | + #### Unsure? Have a questions? 👉 [Start a new discussion](https://github.com/ESP32Async/AsyncTCP/discussions/new) + + #### Before opening a new issue, please make sure you have searched: + + - In the [documentation](https://github.com/ESP32Async/AsyncTCP) + - In the [discussions](https://github.com/ESP32Async/AsyncTCP/discussions) + - In the [issues](https://github.com/ESP32Async/AsyncTCP/issues) + - In the [examples](https://github.com/ESP32Async/AsyncTCP/tree/main/examples) + + #### Make sure you are using: + + - The [latest version of AsyncTCP](https://github.com/ESP32Async/AsyncTCP/releases) + + - type: dropdown + id: tooling + attributes: + label: IDE / Tooling + options: + - Arduino (IDE/CLI) + - pioarduino + - ESP-IDF + - PlatformIO + validations: + required: true + + - type: textarea + id: what-happened + attributes: + label: What happened? + description: A clear and concise description of the issue. + placeholder: A clear and concise description of the issue. + validations: + required: true + + - type: textarea + id: stack-trace + attributes: + label: Stack Trace + description: Please provide a debug message or error message. If you have a Guru Meditation Error or Backtrace, [please decode it](https://maximeborges.github.io/esp-stacktrace-decoder/). + placeholder: For Arduino IDE, Enable Core debug level - Debug on tools menu, then put the serial output here. + validations: + required: true + + - type: textarea + id: how-to-reproduce + attributes: + label: Minimal Reproductible Example (MRE) + description: Post the code or the steps to reproduce the issue. + placeholder: Post the code or the steps to reproduce the issue. + validations: + required: true + + - type: checkboxes + id: confirmation + attributes: + label: "I confirm that:" + options: + - label: I have read the documentation. + required: true + - label: I have searched for similar discussions. + required: true + - label: I have searched for similar issues. + required: true + - label: I have looked at the examples. + required: true + - label: I have upgraded to the lasted version of AsyncTCP. + required: true diff --git a/.github/ISSUE_TEMPLATE/issue.md b/.github/ISSUE_TEMPLATE/issue.md deleted file mode 100644 index b314bb5..0000000 --- a/.github/ISSUE_TEMPLATE/issue.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -name: Bug Report -about: Only for bugs founds in the library! Otherwise, please go to the discussions section! -title: "[BUG] " -labels: triage -assignees: "" ---- - -PLEASE MAKE SURE TO READ THE RECOMMENDATIONS BEFORE OPENING A BUG REPORT: - -https://github.com/ESP32Async/AsyncTCP?tab=readme-ov-file#important-recommendations-for-build-options - -PLEASE READ THE FOLLOWING BEFORE OPENING A BUG REPORT: - -If you want to discuss a feature, improvement, or as a question, please create a discussion here: https://github.com/ESP32Async/AsyncTCP/discussions/categories/general - -If you have a crash, regression or found an issue, please give the following information: - -- Which board: -- AsyncTCP version (if applicable): -- AsyncTCP repository location you use (if applicable and if you use one coming from elsewhere): - -Please post a complete decoded stack trace below in case of a crash (if applicable): - -```plaintext - -``` - -Please post some code, ino file, minimal reproductible use case (if applicable): - -```cpp - -``` From ba077b3f0dda2472162562d5bba720da9164debf Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Wed, 5 Feb 2025 09:42:49 +0100 Subject: [PATCH 29/32] fix(mem): Code cleanup and potential memory leak --- src/AsyncTCP.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/AsyncTCP.cpp b/src/AsyncTCP.cpp index 932af57..fe5e056 100644 --- a/src/AsyncTCP.cpp +++ b/src/AsyncTCP.cpp @@ -167,6 +167,7 @@ static inline bool _get_async_event(lwip_tcp_event_packet_t **e) { */ lwip_tcp_event_packet_t *next_pkt = NULL; while (xQueuePeek(_async_queue, &next_pkt, 0) == pdPASS) { + // if the next event that will come is a poll event for the same connection, we can discard it and continue if (next_pkt->arg == (*e)->arg && next_pkt->event == LWIP_TCP_POLL) { if (xQueueReceive(_async_queue, &next_pkt, 0) == pdPASS) { free(next_pkt); @@ -176,7 +177,7 @@ static inline bool _get_async_event(lwip_tcp_event_packet_t **e) { } } - // quit while loop if next event can't be discarded + // quit while loop if next incoming event can't be discarded (not a poll event) break; } @@ -193,9 +194,9 @@ static inline bool _get_async_event(lwip_tcp_event_packet_t **e) { free(*e); *e = NULL; log_d("discarding poll due to queue congestion"); - continue; // Retry + continue; // continue main loop to dequeue next event which we know is not a poll event } - return true; + return true; // queue not nearly full, caller can process the poll event } return false; } @@ -355,6 +356,7 @@ static int8_t _tcp_clear_events(void *arg) { e->arg = arg; if (!_prepend_async_event(&e)) { free((void *)(e)); + return ERR_TIMEOUT; } return ERR_OK; } @@ -372,6 +374,7 @@ static int8_t _tcp_connected(void *arg, tcp_pcb *pcb, int8_t err) { e->connected.err = err; if (!_prepend_async_event(&e)) { free((void *)(e)); + return ERR_TIMEOUT; } return ERR_OK; } @@ -396,6 +399,7 @@ static int8_t _tcp_poll(void *arg, struct tcp_pcb *pcb) { // poll events are not critical 'cause those are repetitive, so we may not wait the queue in any case if (!_send_async_event(&e, 0)) { free((void *)(e)); + return ERR_TIMEOUT; } return ERR_OK; } @@ -423,6 +427,7 @@ static int8_t _tcp_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *pb, int8_t } if (!_send_async_event(&e)) { free((void *)(e)); + return ERR_TIMEOUT; } return ERR_OK; } @@ -440,6 +445,7 @@ static int8_t _tcp_sent(void *arg, struct tcp_pcb *pcb, uint16_t len) { e->sent.len = len; if (!_send_async_event(&e)) { free((void *)(e)); + return ERR_TIMEOUT; } return ERR_OK; } @@ -491,6 +497,7 @@ static int8_t _tcp_accept(void *arg, AsyncClient *client) { e->accept.client = client; if (!_prepend_async_event(&e)) { free((void *)(e)); + return ERR_TIMEOUT; } return ERR_OK; } @@ -1614,7 +1621,11 @@ int8_t AsyncServer::_accept(tcp_pcb *pcb, int8_t err) { AsyncClient *c = new AsyncClient(pcb); if (c) { c->setNoDelay(_noDelay); - return _tcp_accept(this, c); + const int8_t err = _tcp_accept(this, c); + if (err != ERR_OK) { + delete c; + } + return err; } } if (tcp_close(pcb) != ERR_OK) { From 3293f352cae06179db2aa7a3e0a0dc880a4f85ad Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Wed, 5 Feb 2025 22:44:22 +0100 Subject: [PATCH 30/32] v3.3.4 --- library.json | 2 +- library.properties | 2 +- src/AsyncTCPVersion.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library.json b/library.json index 85f09ca..b2cb352 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "AsyncTCP", - "version": "3.3.3", + "version": "3.3.4", "description": "Asynchronous TCP Library for ESP32", "keywords": "async,tcp", "repository": { diff --git a/library.properties b/library.properties index 0543aef..3646e64 100644 --- a/library.properties +++ b/library.properties @@ -1,6 +1,6 @@ name=Async TCP includes=AsyncTCP.h -version=3.3.3 +version=3.3.4 author=ESP32Async maintainer=ESP32Async sentence=Async TCP Library for ESP32 diff --git a/src/AsyncTCPVersion.h b/src/AsyncTCPVersion.h index 78f0cf9..791524e 100644 --- a/src/AsyncTCPVersion.h +++ b/src/AsyncTCPVersion.h @@ -12,7 +12,7 @@ extern "C" { /** Minor version number (x.X.x) */ #define ASYNCTCP_VERSION_MINOR 3 /** Patch version number (x.x.X) */ -#define ASYNCTCP_VERSION_PATCH 3 +#define ASYNCTCP_VERSION_PATCH 4 /** * Macro to convert version number into an integer From a0614422682e74fbdf937fe7266029783e811ac3 Mon Sep 17 00:00:00 2001 From: vortigont Date: Thu, 6 Feb 2025 18:04:15 +0900 Subject: [PATCH 31/32] abort connection on accept if _tcp_accept failed (#22) * abort connection on accept if _tcp_accept failed * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- src/AsyncTCP.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/AsyncTCP.cpp b/src/AsyncTCP.cpp index fe5e056..a92ed1a 100644 --- a/src/AsyncTCP.cpp +++ b/src/AsyncTCP.cpp @@ -1618,20 +1618,19 @@ void AsyncServer::end() { int8_t AsyncServer::_accept(tcp_pcb *pcb, int8_t err) { // ets_printf("+A: 0x%08x\n", pcb); if (_connect_cb) { - AsyncClient *c = new AsyncClient(pcb); + AsyncClient *c = new (std::nothrow) AsyncClient(pcb); if (c) { c->setNoDelay(_noDelay); const int8_t err = _tcp_accept(this, c); if (err != ERR_OK) { + tcp_abort(pcb); delete c; } return err; } } - if (tcp_close(pcb) != ERR_OK) { - tcp_abort(pcb); - } - log_d("FAIL"); + tcp_abort(pcb); + log_d("_accept failed"); return ERR_OK; } From 90065c8104f38706764679aa6131120928b8edc3 Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Thu, 6 Feb 2025 10:09:10 +0100 Subject: [PATCH 32/32] v3.3.5 --- library.json | 2 +- library.properties | 2 +- src/AsyncTCPVersion.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library.json b/library.json index b2cb352..6e72573 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "AsyncTCP", - "version": "3.3.4", + "version": "3.3.5", "description": "Asynchronous TCP Library for ESP32", "keywords": "async,tcp", "repository": { diff --git a/library.properties b/library.properties index 3646e64..8c2495f 100644 --- a/library.properties +++ b/library.properties @@ -1,6 +1,6 @@ name=Async TCP includes=AsyncTCP.h -version=3.3.4 +version=3.3.5 author=ESP32Async maintainer=ESP32Async sentence=Async TCP Library for ESP32 diff --git a/src/AsyncTCPVersion.h b/src/AsyncTCPVersion.h index 791524e..f2c4c37 100644 --- a/src/AsyncTCPVersion.h +++ b/src/AsyncTCPVersion.h @@ -12,7 +12,7 @@ extern "C" { /** Minor version number (x.X.x) */ #define ASYNCTCP_VERSION_MINOR 3 /** Patch version number (x.x.X) */ -#define ASYNCTCP_VERSION_PATCH 4 +#define ASYNCTCP_VERSION_PATCH 5 /** * Macro to convert version number into an integer