Arduino Core 3 (#407)

* Add and remove libs and components for Arduino Core 3

* Arduino Core 3

* Add back Solo1

* Change ESP32-S3 to 4MB build

* Update README.md

* Fix retain and number of retries

* Fix rolling log

* Fix defaults

* Fix BleScanner on Solo1

* Export settings

* Import settings

* Fix HA Battery voltage

* Change submodule

* Update espMqttClient and AsyncTCP

* Webserial and MQTT/Network reconnecting

* Update nuki_ble

---------

Co-authored-by: iranl <iranl@github.com>
This commit is contained in:
iranl
2024-07-05 18:45:39 +02:00
committed by GitHub
parent 193ebb5f91
commit 6b0100fd61
236 changed files with 16390 additions and 9740 deletions

View File

@@ -0,0 +1,35 @@
---
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/mathieucarbou/AsyncTCP?tab=readme-ov-file#important-recommendations](https://github.com/mathieucarbou/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.

View File

@@ -0,0 +1,10 @@
---
name: Question
about: Describe your question
title: "[Q]"
labels: question
assignees: ''
---

10
lib/AsyncTCP/.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
# Set update schedule for GitHub Actions
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
# Check for updates to GitHub Actions every week
interval: "weekly"

View File

@@ -1,36 +0,0 @@
#!/bin/bash
export ARDUINO_ESP32_PATH="$ARDUINO_USR_PATH/hardware/espressif/esp32"
if [ ! -d "$ARDUINO_ESP32_PATH" ]; then
echo "Installing ESP32 Arduino Core ..."
script_init_path="$PWD"
mkdir -p "$ARDUINO_USR_PATH/hardware/espressif"
cd "$ARDUINO_USR_PATH/hardware/espressif"
echo "Installing Python Serial ..."
pip install pyserial > /dev/null
if [ "$OS_IS_WINDOWS" == "1" ]; then
echo "Installing Python Requests ..."
pip install requests > /dev/null
fi
if [ "$GITHUB_REPOSITORY" == "espressif/arduino-esp32" ]; then
echo "Linking Core..."
ln -s $GITHUB_WORKSPACE esp32
else
echo "Cloning Core Repository..."
git clone https://github.com/espressif/arduino-esp32.git esp32 > /dev/null 2>&1
fi
echo "Updating Submodules ..."
cd esp32
git submodule update --init --recursive > /dev/null 2>&1
echo "Installing Platform Tools ..."
cd tools && python get.py
cd $script_init_path
echo "ESP32 Arduino has been installed in '$ARDUINO_ESP32_PATH'"
echo ""
fi

View File

@@ -1,220 +0,0 @@
#!/bin/bash
#OSTYPE: 'linux-gnu', ARCH: 'x86_64' => linux64
#OSTYPE: 'msys', ARCH: 'x86_64' => win32
#OSTYPE: 'darwin18', ARCH: 'i386' => macos
OSBITS=`arch`
if [[ "$OSTYPE" == "linux"* ]]; then
export OS_IS_LINUX="1"
ARCHIVE_FORMAT="tar.xz"
if [[ "$OSBITS" == "i686" ]]; then
OS_NAME="linux32"
elif [[ "$OSBITS" == "x86_64" ]]; then
OS_NAME="linux64"
elif [[ "$OSBITS" == "armv7l" || "$OSBITS" == "aarch64" ]]; then
OS_NAME="linuxarm"
else
OS_NAME="$OSTYPE-$OSBITS"
echo "Unknown OS '$OS_NAME'"
exit 1
fi
elif [[ "$OSTYPE" == "darwin"* ]]; then
export OS_IS_MACOS="1"
ARCHIVE_FORMAT="zip"
OS_NAME="macosx"
elif [[ "$OSTYPE" == "cygwin" ]] || [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then
export OS_IS_WINDOWS="1"
ARCHIVE_FORMAT="zip"
OS_NAME="windows"
else
OS_NAME="$OSTYPE-$OSBITS"
echo "Unknown OS '$OS_NAME'"
exit 1
fi
export OS_NAME
ARDUINO_BUILD_DIR="$HOME/.arduino/build.tmp"
ARDUINO_CACHE_DIR="$HOME/.arduino/cache.tmp"
if [ "$OS_IS_MACOS" == "1" ]; then
export ARDUINO_IDE_PATH="/Applications/Arduino.app/Contents/Java"
export ARDUINO_USR_PATH="$HOME/Documents/Arduino"
elif [ "$OS_IS_WINDOWS" == "1" ]; then
export ARDUINO_IDE_PATH="$HOME/arduino_ide"
export ARDUINO_USR_PATH="$HOME/Documents/Arduino"
else
export ARDUINO_IDE_PATH="$HOME/arduino_ide"
export ARDUINO_USR_PATH="$HOME/Arduino"
fi
if [ ! -d "$ARDUINO_IDE_PATH" ]; then
echo "Installing Arduino IDE on $OS_NAME ..."
echo "Downloading 'arduino-nightly-$OS_NAME.$ARCHIVE_FORMAT' to 'arduino.$ARCHIVE_FORMAT' ..."
if [ "$OS_IS_LINUX" == "1" ]; then
wget -O "arduino.$ARCHIVE_FORMAT" "https://www.arduino.cc/download.php?f=/arduino-nightly-$OS_NAME.$ARCHIVE_FORMAT" > /dev/null 2>&1
echo "Extracting 'arduino.$ARCHIVE_FORMAT' ..."
tar xf "arduino.$ARCHIVE_FORMAT" > /dev/null
mv arduino-nightly "$ARDUINO_IDE_PATH"
else
curl -o "arduino.$ARCHIVE_FORMAT" -L "https://www.arduino.cc/download.php?f=/arduino-nightly-$OS_NAME.$ARCHIVE_FORMAT" > /dev/null 2>&1
echo "Extracting 'arduino.$ARCHIVE_FORMAT' ..."
unzip "arduino.$ARCHIVE_FORMAT" > /dev/null
if [ "$OS_IS_MACOS" == "1" ]; then
mv "Arduino.app" "/Applications/Arduino.app"
else
mv arduino-nightly "$ARDUINO_IDE_PATH"
fi
fi
rm -rf "arduino.$ARCHIVE_FORMAT"
mkdir -p "$ARDUINO_USR_PATH/libraries"
mkdir -p "$ARDUINO_USR_PATH/hardware"
echo "Arduino IDE Installed in '$ARDUINO_IDE_PATH'"
echo ""
fi
function build_sketch(){ # build_sketch <fqbn> <path-to-ino> [extra-options]
if [ "$#" -lt 2 ]; then
echo "ERROR: Illegal number of parameters"
echo "USAGE: build_sketch <fqbn> <path-to-ino> [extra-options]"
return 1
fi
local fqbn="$1"
local sketch="$2"
local xtra_opts="$3"
local win_opts=""
if [ "$OS_IS_WINDOWS" == "1" ]; then
local ctags_version=`ls "$ARDUINO_IDE_PATH/tools-builder/ctags/"`
local preprocessor_version=`ls "$ARDUINO_IDE_PATH/tools-builder/arduino-preprocessor/"`
win_opts="-prefs=runtime.tools.ctags.path=$ARDUINO_IDE_PATH/tools-builder/ctags/$ctags_version -prefs=runtime.tools.arduino-preprocessor.path=$ARDUINO_IDE_PATH/tools-builder/arduino-preprocessor/$preprocessor_version"
fi
echo ""
echo "Compiling '"$(basename "$sketch")"' ..."
mkdir -p "$ARDUINO_BUILD_DIR"
mkdir -p "$ARDUINO_CACHE_DIR"
$ARDUINO_IDE_PATH/arduino-builder -compile -logger=human -core-api-version=10810 \
-fqbn=$fqbn \
-warnings="all" \
-tools "$ARDUINO_IDE_PATH/tools-builder" \
-tools "$ARDUINO_IDE_PATH/tools" \
-built-in-libraries "$ARDUINO_IDE_PATH/libraries" \
-hardware "$ARDUINO_IDE_PATH/hardware" \
-hardware "$ARDUINO_USR_PATH/hardware" \
-libraries "$ARDUINO_USR_PATH/libraries" \
-build-cache "$ARDUINO_CACHE_DIR" \
-build-path "$ARDUINO_BUILD_DIR" \
$win_opts $xtra_opts "$sketch"
}
function count_sketches() # count_sketches <examples-path>
{
local examples="$1"
rm -rf sketches.txt
if [ ! -d "$examples" ]; then
touch sketches.txt
return 0
fi
local sketches=$(find $examples -name *.ino)
local sketchnum=0
for sketch in $sketches; do
local sketchdir=$(dirname $sketch)
local sketchdirname=$(basename $sketchdir)
local sketchname=$(basename $sketch)
if [[ "${sketchdirname}.ino" != "$sketchname" ]]; then
continue
fi;
if [[ -f "$sketchdir/.test.skip" ]]; then
continue
fi
echo $sketch >> sketches.txt
sketchnum=$(($sketchnum + 1))
done
return $sketchnum
}
function build_sketches() # build_sketches <fqbn> <examples-path> <chunk> <total-chunks> [extra-options]
{
local fqbn=$1
local examples=$2
local chunk_idex=$3
local chunks_num=$4
local xtra_opts=$5
if [ "$#" -lt 2 ]; then
echo "ERROR: Illegal number of parameters"
echo "USAGE: build_sketches <fqbn> <examples-path> [<chunk> <total-chunks>] [extra-options]"
return 1
fi
if [ "$#" -lt 4 ]; then
chunk_idex="0"
chunks_num="1"
xtra_opts=$3
fi
if [ "$chunks_num" -le 0 ]; then
echo "ERROR: Chunks count must be positive number"
return 1
fi
if [ "$chunk_idex" -ge "$chunks_num" ]; then
echo "ERROR: Chunk index must be less than chunks count"
return 1
fi
set +e
count_sketches "$examples"
local sketchcount=$?
set -e
local sketches=$(cat sketches.txt)
rm -rf sketches.txt
local chunk_size=$(( $sketchcount / $chunks_num ))
local all_chunks=$(( $chunks_num * $chunk_size ))
if [ "$all_chunks" -lt "$sketchcount" ]; then
chunk_size=$(( $chunk_size + 1 ))
fi
local start_index=$(( $chunk_idex * $chunk_size ))
if [ "$sketchcount" -le "$start_index" ]; then
echo "Skipping job"
return 0
fi
local end_index=$(( $(( $chunk_idex + 1 )) * $chunk_size ))
if [ "$end_index" -gt "$sketchcount" ]; then
end_index=$sketchcount
fi
local start_num=$(( $start_index + 1 ))
echo "Found $sketchcount Sketches";
echo "Chunk Count : $chunks_num"
echo "Chunk Size : $chunk_size"
echo "Start Sketch: $start_num"
echo "End Sketch : $end_index"
local sketchnum=0
for sketch in $sketches; do
local sketchdir=$(dirname $sketch)
local sketchdirname=$(basename $sketchdir)
local sketchname=$(basename $sketch)
if [ "${sketchdirname}.ino" != "$sketchname" ] \
|| [ -f "$sketchdir/.test.skip" ]; then
continue
fi
sketchnum=$(($sketchnum + 1))
if [ "$sketchnum" -le "$start_index" ] \
|| [ "$sketchnum" -gt "$end_index" ]; then
continue
fi
build_sketch "$fqbn" "$sketch" "$xtra_opts"
local result=$?
if [ $result -ne 0 ]; then
return $result
fi
done
return 0
}

View File

@@ -1,133 +0,0 @@
#!/bin/bash
echo "Installing Python Wheel ..."
pip install wheel > /dev/null 2>&1
echo "Installing PlatformIO ..."
pip install -U platformio > /dev/null 2>&1
echo "PlatformIO has been installed"
echo ""
function build_pio_sketch(){ # build_pio_sketch <board> <path-to-ino>
if [ "$#" -lt 2 ]; then
echo "ERROR: Illegal number of parameters"
echo "USAGE: build_pio_sketch <board> <path-to-ino>"
return 1
fi
local board="$1"
local sketch="$2"
local sketch_dir=$(dirname "$sketch")
echo ""
echo "Compiling '"$(basename "$sketch")"' ..."
python -m platformio ci -l '.' --board "$board" "$sketch_dir" --project-option="board_build.partitions = huge_app.csv"
}
function count_sketches() # count_sketches <examples-path>
{
local examples="$1"
rm -rf sketches.txt
if [ ! -d "$examples" ]; then
touch sketches.txt
return 0
fi
local sketches=$(find $examples -name *.ino)
local sketchnum=0
for sketch in $sketches; do
local sketchdir=$(dirname $sketch)
local sketchdirname=$(basename $sketchdir)
local sketchname=$(basename $sketch)
if [[ "${sketchdirname}.ino" != "$sketchname" ]]; then
continue
fi;
if [[ -f "$sketchdir/.test.skip" ]]; then
continue
fi
echo $sketch >> sketches.txt
sketchnum=$(($sketchnum + 1))
done
return $sketchnum
}
function build_pio_sketches() # build_pio_sketches <board> <examples-path> <chunk> <total-chunks>
{
if [ "$#" -lt 2 ]; then
echo "ERROR: Illegal number of parameters"
echo "USAGE: build_pio_sketches <board> <examples-path> [<chunk> <total-chunks>]"
return 1
fi
local board=$1
local examples=$2
local chunk_idex=$3
local chunks_num=$4
if [ "$#" -lt 4 ]; then
chunk_idex="0"
chunks_num="1"
fi
if [ "$chunks_num" -le 0 ]; then
echo "ERROR: Chunks count must be positive number"
return 1
fi
if [ "$chunk_idex" -ge "$chunks_num" ]; then
echo "ERROR: Chunk index must be less than chunks count"
return 1
fi
set +e
count_sketches "$examples"
local sketchcount=$?
set -e
local sketches=$(cat sketches.txt)
rm -rf sketches.txt
local chunk_size=$(( $sketchcount / $chunks_num ))
local all_chunks=$(( $chunks_num * $chunk_size ))
if [ "$all_chunks" -lt "$sketchcount" ]; then
chunk_size=$(( $chunk_size + 1 ))
fi
local start_index=$(( $chunk_idex * $chunk_size ))
if [ "$sketchcount" -le "$start_index" ]; then
echo "Skipping job"
return 0
fi
local end_index=$(( $(( $chunk_idex + 1 )) * $chunk_size ))
if [ "$end_index" -gt "$sketchcount" ]; then
end_index=$sketchcount
fi
local start_num=$(( $start_index + 1 ))
echo "Found $sketchcount Sketches";
echo "Chunk Count : $chunks_num"
echo "Chunk Size : $chunk_size"
echo "Start Sketch: $start_num"
echo "End Sketch : $end_index"
local sketchnum=0
for sketch in $sketches; do
local sketchdir=$(dirname $sketch)
local sketchdirname=$(basename $sketchdir)
local sketchname=$(basename $sketch)
if [ "${sketchdirname}.ino" != "$sketchname" ] \
|| [ -f "$sketchdir/.test.skip" ]; then
continue
fi
sketchnum=$(($sketchnum + 1))
if [ "$sketchnum" -le "$start_index" ] \
|| [ "$sketchnum" -gt "$end_index" ]; then
continue
fi
build_pio_sketch "$board" "$sketch"
local result=$?
if [ $result -ne 0 ]; then
return $result
fi
done
return 0
}

View File

@@ -1,64 +0,0 @@
#!/bin/bash
set -e
if [ ! -z "$TRAVIS_BUILD_DIR" ]; then
export GITHUB_WORKSPACE="$TRAVIS_BUILD_DIR"
export GITHUB_REPOSITORY="$TRAVIS_REPO_SLUG"
elif [ -z "$GITHUB_WORKSPACE" ]; then
export GITHUB_WORKSPACE="$PWD"
export GITHUB_REPOSITORY="me-no-dev/AsyncTCP"
fi
CHUNK_INDEX=$1
CHUNKS_CNT=$2
BUILD_PIO=0
if [ "$#" -lt 2 ] || [ "$CHUNKS_CNT" -le 0 ]; then
CHUNK_INDEX=0
CHUNKS_CNT=1
elif [ "$CHUNK_INDEX" -gt "$CHUNKS_CNT" ]; then
CHUNK_INDEX=$CHUNKS_CNT
elif [ "$CHUNK_INDEX" -eq "$CHUNKS_CNT" ]; then
BUILD_PIO=1
fi
if [ "$BUILD_PIO" -eq 0 ]; then
# ArduinoIDE Test
source ./.github/scripts/install-arduino-ide.sh
source ./.github/scripts/install-arduino-core-esp32.sh
echo "Installing AsyncTCP ..."
cp -rf "$GITHUB_WORKSPACE" "$ARDUINO_USR_PATH/libraries/AsyncTCP"
FQBN="espressif:esp32:esp32:PSRAM=enabled,PartitionScheme=huge_app"
build_sketches "$FQBN" "$GITHUB_WORKSPACE/examples"
if [ ! "$OS_IS_WINDOWS" == "1" ]; then
echo "Installing ESPAsyncWebServer ..."
git clone https://github.com/me-no-dev/ESPAsyncWebServer "$ARDUINO_USR_PATH/libraries/ESPAsyncWebServer" > /dev/null 2>&1
echo "Installing ArduinoJson ..."
git clone https://github.com/bblanchon/ArduinoJson "$ARDUINO_USR_PATH/libraries/ArduinoJson" > /dev/null 2>&1
build_sketches "$FQBN" "$ARDUINO_USR_PATH/libraries/ESPAsyncWebServer/examples"
fi
else
# PlatformIO Test
source ./.github/scripts/install-platformio.sh
echo "Installing AsyncTCP ..."
python -m platformio lib --storage-dir "$GITHUB_WORKSPACE" install
BOARD="esp32dev"
build_pio_sketches "$BOARD" "$GITHUB_WORKSPACE/examples"
if [[ "$OSTYPE" != "cygwin" ]] && [[ "$OSTYPE" != "msys" ]] && [[ "$OSTYPE" != "win32" ]]; then
echo "Installing ESPAsyncWebServer ..."
python -m platformio lib -g install https://github.com/me-no-dev/ESPAsyncWebServer.git > /dev/null 2>&1
git clone https://github.com/me-no-dev/ESPAsyncWebServer "$HOME/ESPAsyncWebServer" > /dev/null 2>&1
echo "Installing ArduinoJson ..."
python -m platformio lib -g install https://github.com/bblanchon/ArduinoJson.git > /dev/null 2>&1
build_pio_sketches "$BOARD" "$HOME/ESPAsyncWebServer/examples"
fi
fi

View File

@@ -2,31 +2,51 @@ name: Async TCP CI
on:
push:
branches:
- master
- release/*
pull_request:
workflow_dispatch:
jobs:
build-arduino:
name: Arduino on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
name: ${{ matrix.config }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macOS-latest]
config: [arduino-cli.yaml, arduino-cli-dev.yaml]
steps:
- uses: actions/checkout@v1
- name: Build Tests
run: bash ./.github/scripts/on-push.sh 0 1
- uses: actions/checkout@v4
- uses: arduino/setup-arduino-cli@v1
- name: Download board
run: |
arduino-cli --config-file ${{ matrix.config }} core update-index
arduino-cli --config-file ${{ matrix.config }} board listall
arduino-cli --config-file ${{ matrix.config }} core install esp32:esp32
- name: Compile Sketch
run: arduino-cli --config-file ${{ matrix.config }} --library ./src/ compile --fqbn esp32:esp32:esp32 ./examples/ClientServer/Client/Client.ino
- name: Compile Sketch with IPv6
env:
LWIP_IPV6: true
run: arduino-cli --config-file ${{ matrix.config }} --library ./src/ compile --fqbn esp32:esp32:esp32 ./examples/ClientServer/Client/Client.ino
build-pio:
name: PlatformIO on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
name: ${{ matrix.board }} ${{ matrix.env }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macOS-latest]
board: [esp32dev, esp32-s3-devkitc-1]
env: [arduino-2, arduino-3]
steps:
- uses: actions/checkout@v1
- name: Build Tests
run: bash ./.github/scripts/on-push.sh 1 1
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: |
~/.platformio
~/.cache/pip
key: ${{ matrix.env }}
- uses: actions/setup-python@v5
with:
python-version: "3.x"
- run: pip install platformio
- run: sed -i -e 's/esp32dev/${{ matrix.board }}/g' platformio.ini
- run: pio run -e ${{ matrix.env }}

View File

@@ -1,2 +1,6 @@
.DS_Store
.lh
/.pio
/.vscode/*
!/.vscode/settings.json
/logs

View File

@@ -1,34 +0,0 @@
sudo: false
language: python
os:
- linux
git:
depth: false
stages:
- build
jobs:
include:
- name: "Arduino Build"
if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
stage: build
script: bash $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh
- name: "PlatformIO Build"
if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
stage: build
script: bash $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh 1 1
notifications:
email:
on_success: change
on_failure: change
webhooks:
urls:
- https://webhooks.gitter.im/e/60e65d0c78ea0a920347
on_success: change # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: false # default: false

View File

@@ -1,13 +1,52 @@
# AsyncTCP
[![Build Status](https://travis-ci.org/me-no-dev/AsyncTCP.svg?branch=master)](https://travis-ci.org/me-no-dev/AsyncTCP) ![](https://github.com/me-no-dev/AsyncTCP/workflows/Async%20TCP%20CI/badge.svg) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/2f7e4d1df8b446d192cbfec6dc174d2d)](https://www.codacy.com/manual/me-no-dev/AsyncTCP?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=me-no-dev/AsyncTCP&amp;utm_campaign=Badge_Grade)
# 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/mathieucarbou/AsyncTCP/actions/workflows/push.yml/badge.svg)](https://github.com/mathieucarbou/AsyncTCP/actions/workflows/push.yml)
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/mathieucarbou/library/Async%20TCP.svg)](https://registry.platformio.org/libraries/mathieucarbou/Async%20TCP)
A fork of the [AsyncTCP](https://github.com/me-no-dev/AsyncTCP) library by [@me-no-dev](https://github.com/me-no-dev) for [ESPHome](https://esphome.io).
### Async TCP Library for ESP32 Arduino
[![Join the chat at https://gitter.im/me-no-dev/ESPAsyncWebServer](https://badges.gitter.im/me-no-dev/ESPAsyncWebServer.svg)](https://gitter.im/me-no-dev/ESPAsyncWebServer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
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/me-no-dev/ESPAsyncWebServer)
This library is the base for [ESPAsyncWebServer](https://github.com/mathieucarbou/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 in this fork
- All improvements from [ESPHome fork](https://github.com/esphome/AsyncTCP)
- Reverted back `library.properties` for Arduino IDE users
- Arduino 3 / ESP-IDF 5 compatibility
- Changed lib name: `AsyncTCP` -> `Async TCP`
- Point to `mathieucarbou/Async TCP @ ^3.1.4`
- IPv6 support
## Important recommendations
Most of the crashes are caused by improper configuration of the library for the project.
Here are some recommendations to avoid them.
1. Set the running core to be on the same core of your application (usually core 1) `-D CONFIG_ASYNC_TCP_RUNNING_CORE=1`
2. Set the stack size appropriately with `-D CONFIG_ASYNC_TCP_STACK_SIZE=16384`.
The default value of `16384` might be too much for your project.
You can look at the [MycilaTaskMonitor](https://oss.carbou.me/MycilaTaskMonitor) project to monitor the stack usage.
3. You can change **if you know what you are doing** the task priority with `-D CONFIG_ASYNC_TCP_PRIORITY=10`.
Default is `10`.
4. You can increase the queue size with `-D CONFIG_ASYNC_TCP_QUEUE_SIZE=128`.
Default is `64`.
5. You can decrease the maximum ack time `-D CONFIG_ASYNC_TCP_MAX_ACK_TIME=3000`.
Default is `5000`.
I personally use the following configuration in my projects:
```c++
-D CONFIG_ASYNC_TCP_MAX_ACK_TIME=3000
-D CONFIG_ASYNC_TCP_PRIORITY=10
-D CONFIG_ASYNC_TCP_QUEUE_SIZE=128
-D CONFIG_ASYNC_TCP_RUNNING_CORE=1
-D CONFIG_ASYNC_TCP_STACK_SIZE=4096
```

View File

@@ -0,0 +1,25 @@
board_manager:
additional_urls:
- https://espressif.github.io/arduino-esp32/package_esp32_dev_index.json
directories:
builtin.libraries: ./src/
build_cache:
compilations_before_purge: 10
ttl: 720h0m0s
daemon:
port: "50051"
library:
enable_unsafe_install: false
logging:
file: ""
format: text
level: info
metrics:
addr: :9090
enabled: true
output:
no_color: false
sketch:
always_export_binaries: false
updater:
enable_notification: true

View File

@@ -0,0 +1,25 @@
board_manager:
additional_urls:
- https://espressif.github.io/arduino-esp32/package_esp32_index.json
directories:
builtin.libraries: ./src/
build_cache:
compilations_before_purge: 10
ttl: 720h0m0s
daemon:
port: "50051"
library:
enable_unsafe_install: false
logging:
file: ""
format: text
level: info
metrics:
addr: :9090
enabled: true
output:
no_color: false
sketch:
always_export_binaries: false
updater:
enable_notification: true

View File

@@ -0,0 +1,42 @@
#include <AsyncTCP.h>
#include "config.h"
static void replyToServer(void* arg) {
AsyncClient* client = reinterpret_cast<AsyncClient*>(arg);
// send reply
if (client->space() > 32 && client->canSend()) {
char message[32];
client->add(message, strlen(message));
client->send();
}
}
/* event callbacks */
static void handleData(void* arg, AsyncClient* client, void *data, size_t len) {
Serial.printf("\n data received from %s \n", client->remoteIP().toString().c_str());
Serial.write((uint8_t*)data, len);
}
void onConnect(void* arg, AsyncClient* client) {
Serial.printf("\n client has been connected to %s on port %d \n", SERVER_HOST_NAME, TCP_PORT);
replyToServer(client);
}
void setup() {
Serial.begin(115200);
delay(20);
AsyncClient* client = new AsyncClient;
client->onData(&handleData, client);
client->onConnect(&onConnect, client);
client->connect(SERVER_HOST_NAME, TCP_PORT);
}
void loop() {
}

View File

@@ -0,0 +1,23 @@
#ifndef CONFIG_H
#define CONFIG_H
/*
* This example demonstrate how to use asynchronous client & server APIs
* in order to establish tcp socket connections in client server manner.
* server is running (on port 7050) on one ESP, acts as AP, and other clients running on
* remaining ESPs acts as STAs. after connection establishment between server and clients
* there is a simple message transfer in every 2s. clients connect to server via it's host name
* (in this case 'esp_server') with help of DNS service running on server side.
*
* Note: default MSS for ESPAsyncTCP is 536 byte and defualt ACK timeout is 5s.
*/
#define SSID "ESP-TEST"
#define PASSWORD "123456789"
#define SERVER_HOST_NAME "esp_server"
#define TCP_PORT 7050
#define DNS_PORT 53
#endif // CONFIG_H

View File

@@ -1,22 +1,38 @@
{
"name":"AsyncTCP",
"description":"Asynchronous TCP Library for ESP32",
"keywords":"async,tcp",
"authors":
{
"name": "Hristo Gochkov",
"maintainer": true
},
"repository":
{
"name": "Async TCP",
"version": "3.1.4",
"description": "Asynchronous TCP Library for ESP32",
"keywords": "async,tcp",
"repository": {
"type": "git",
"url": "https://github.com/me-no-dev/AsyncTCP.git"
"url": "https://github.com/mathieucarbou/AsyncTCP.git"
},
"version": "1.1.1",
"authors": [
{
"name": "Hristo Gochkov"
},
{
"name": "Mathieu Carbou",
"maintainer": true
}
],
"license": "LGPL-3.0",
"frameworks": "arduino",
"platforms": "espressif32",
"platforms": [
"espressif32",
"libretiny"
],
"build": {
"libCompatMode": 2
}
}
},
"export": {
"include": [
"examples",
"src",
"library.json",
"library.properties",
"LICENSE",
"README.md"
]
}
}

View File

@@ -1,9 +1,9 @@
name=AsyncTCP
version=1.1.1
name=Async TCP
version=3.1.4
author=Me-No-Dev
maintainer=Me-No-Dev
maintainer=Mathieu Carbou <mathieu.carbou@gmail.com>
sentence=Async TCP Library for ESP32
paragraph=Async TCP Library for ESP32
category=Other
url=https://github.com/me-no-dev/AsyncTCP
url=https://github.com/mathieucarbou/AsyncTCP.git
architectures=*

View File

@@ -0,0 +1,28 @@
[env]
framework = arduino
build_flags =
-Wall -Wextra
-D CONFIG_ARDUHAL_LOG_COLORS
-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
upload_protocol = esptool
monitor_speed = 115200
monitor_filters = esp32_exception_decoder, log2file
[platformio]
lib_dir = .
src_dir = examples/ClientServer/Client
[env:arduino]
platform = espressif32
board = esp32dev
[env:arduino-2]
platform = espressif32@6.7.0
board = esp32dev
[env:arduino-3]
platform = espressif32
platform_packages=
platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.2
platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.2/esp32-arduino-libs-3.0.2.zip
board = esp32dev

View File

@@ -29,7 +29,9 @@ extern "C"{
#include "lwip/dns.h"
#include "lwip/err.h"
}
#if CONFIG_ASYNC_TCP_USE_WDT
#include "esp_task_wdt.h"
#endif
/*
* TCP/IP Event Task
@@ -44,7 +46,7 @@ typedef struct {
void *arg;
union {
struct {
void * pcb;
tcp_pcb * pcb;
int8_t err;
} connected;
struct {
@@ -76,7 +78,7 @@ typedef struct {
};
} lwip_event_packet_t;
static xQueueHandle _async_queue;
static QueueHandle_t _async_queue;
static TaskHandle_t _async_service_task_handle = NULL;
@@ -95,7 +97,7 @@ static uint32_t _closed_index = []() {
static inline bool _init_async_event_queue(){
if(!_async_queue){
_async_queue = xQueueCreate(32, sizeof(lwip_event_packet_t *));
_async_queue = xQueueCreate(CONFIG_ASYNC_TCP_QUEUE_SIZE, sizeof(lwip_event_packet_t *));
if(!_async_queue){
return false;
}
@@ -213,12 +215,32 @@ 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) {
#ifndef CONFIG_FREERTOS_UNICORE
if(xCoreID >= 0 && xCoreID < 2) {
return xTaskCreatePinnedToCore(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask, xCoreID);
} else {
#endif
return xTaskCreate(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask);
#ifndef CONFIG_FREERTOS_UNICORE
}
#endif
}
static bool _start_async_task(){
if(!_init_async_event_queue()){
return false;
}
if(!_async_service_task_handle){
xTaskCreateUniversal(_async_service_task, "async_tcp", 8192 * 2, NULL, 3, &_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;
}
@@ -555,12 +577,11 @@ AsyncClient::AsyncClient(tcp_pcb* pcb)
, _pb_cb_arg(0)
, _timeout_cb(0)
, _timeout_cb_arg(0)
, _pcb_busy(false)
, _pcb_sent_at(0)
, _ack_pcb(true)
, _rx_last_packet(0)
, _rx_since_timeout(0)
, _ack_timeout(ASYNC_MAX_ACK_TIME)
, _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)
@@ -568,13 +589,15 @@ AsyncClient::AsyncClient(tcp_pcb* pcb)
_pcb = pcb;
_closed_slot = -1;
if(_pcb){
_allocate_closed_slot();
_rx_last_packet = millis();
tcp_arg(_pcb, this);
tcp_recv(_pcb, &_tcp_recv);
tcp_sent(_pcb, &_tcp_sent);
tcp_err(_pcb, &_tcp_error);
tcp_poll(_pcb, &_tcp_poll, 1);
if(!_allocate_closed_slot()) {
_close();
}
}
}
@@ -674,9 +697,9 @@ void AsyncClient::onPoll(AcConnectHandler cb, void* arg){
* Main Public Methods
* */
bool AsyncClient::connect(IPAddress ip, uint16_t port){
bool AsyncClient::_connect(ip_addr_t addr, uint16_t port){
if (_pcb){
log_w("already connected, state %d", _pcb->state);
log_d("already connected, state %d", _pcb->state);
return false;
}
if(!_start_async_task()){
@@ -684,11 +707,12 @@ bool AsyncClient::connect(IPAddress ip, uint16_t port){
return false;
}
ip_addr_t addr;
addr.type = IPADDR_TYPE_V4;
addr.u_addr.ip4.addr = ip;
if(!_allocate_closed_slot()) {
log_e("failed to allocate: closed slot full");
return false;
}
tcp_pcb* pcb = tcp_new_ip_type(IPADDR_TYPE_V4);
tcp_pcb* pcb = tcp_new_ip_type(addr.type);
if (!pcb){
log_e("pcb == NULL");
return false;
@@ -699,27 +723,59 @@ bool AsyncClient::connect(IPAddress ip, uint16_t port){
tcp_recv(pcb, &_tcp_recv);
tcp_sent(pcb, &_tcp_sent);
tcp_poll(pcb, &_tcp_poll, 1);
//_tcp_connect(pcb, &addr, port,(tcp_connected_fn)&_s_connected);
_tcp_connect(pcb, _closed_slot, &addr, port,(tcp_connected_fn)&_tcp_connected);
return true;
}
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;
addr.type = IPADDR_TYPE_V4;
#else
ip.to_ip_addr_t(&addr);
#endif
return _connect(addr, port);
}
#if LWIP_IPV6 && ESP_IDF_VERSION_MAJOR < 5
bool AsyncClient::connect(const IPv6Address& ip, uint16_t port){
ip_addr_t addr;
addr.type = IPADDR_TYPE_V6;
memcpy(addr.u_addr.ip6.addr, static_cast<const uint32_t*>(ip), sizeof(uint32_t) * 4);
return _connect(addr, port);
}
#endif
bool AsyncClient::connect(const char* host, uint16_t port){
ip_addr_t addr;
if(!_start_async_task()){
log_e("failed to start task");
return false;
}
err_t err = dns_gethostbyname(host, &addr, (dns_found_callback)&_tcp_dns_found, this);
if(err == ERR_OK) {
#if ESP_IDF_VERSION_MAJOR < 5
#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
return connect(IPAddress(addr.addr), port);
#endif
#else
return _connect(addr, port);
#endif
} else if(err == ERR_INPROGRESS) {
_connect_port = port;
return true;
}
log_e("error: %d", err);
log_d("error: %d", err);
return false;
}
@@ -763,13 +819,12 @@ size_t AsyncClient::add(const char* data, size_t size, uint8_t apiflags) {
}
bool AsyncClient::send(){
int8_t err = ERR_OK;
err = _tcp_output(_pcb, _closed_slot);
if(err == ERR_OK){
_pcb_busy = true;
_pcb_sent_at = millis();
auto backup = _tx_last_packet;
_tx_last_packet = millis();
if (_tcp_output(_pcb, _closed_slot) == ERR_OK) {
return true;
}
_tx_last_packet = backup;
return false;
}
@@ -799,7 +854,6 @@ int8_t AsyncClient::_close(){
//ets_printf("X: 0x%08x\n", (uint32_t)this);
int8_t err = ERR_OK;
if(_pcb) {
//log_i("");
tcp_arg(_pcb, NULL);
tcp_sent(_pcb, NULL);
tcp_recv(_pcb, NULL);
@@ -810,6 +864,7 @@ int8_t AsyncClient::_close(){
if(err != ERR_OK) {
err = abort();
}
_free_closed_slot();
_pcb = NULL;
if(_discard_cb) {
_discard_cb(_discard_cb_arg, this);
@@ -818,7 +873,10 @@ int8_t AsyncClient::_close(){
return err;
}
void AsyncClient::_allocate_closed_slot(){
bool AsyncClient::_allocate_closed_slot(){
if (_closed_slot != -1) {
return true;
}
xSemaphoreTake(_slots_lock, portMAX_DELAY);
uint32_t closed_slot_min_index = 0;
for (int i = 0; i < _number_of_closed_slots; ++ i) {
@@ -831,28 +889,27 @@ void AsyncClient::_allocate_closed_slot(){
_closed_slots[_closed_slot] = 0;
}
xSemaphoreGive(_slots_lock);
return (_closed_slot != -1);
}
void AsyncClient::_free_closed_slot(){
xSemaphoreTake(_slots_lock, portMAX_DELAY);
if (_closed_slot != -1) {
_closed_slots[_closed_slot] = _closed_index;
_closed_slot = -1;
++ _closed_index;
}
xSemaphoreGive(_slots_lock);
}
/*
* Private Callbacks
* */
int8_t AsyncClient::_connected(void* pcb, int8_t err){
int8_t AsyncClient::_connected(tcp_pcb* pcb, int8_t err){
_pcb = reinterpret_cast<tcp_pcb*>(pcb);
if(_pcb){
_rx_last_packet = millis();
_pcb_busy = false;
// tcp_recv(_pcb, &_tcp_recv);
// tcp_sent(_pcb, &_tcp_sent);
// tcp_poll(_pcb, &_tcp_poll, 1);
}
if(_connect_cb) {
_connect_cb(_connect_cb_arg, this);
@@ -869,6 +926,7 @@ void AsyncClient::_error(int8_t err) {
tcp_err(_pcb, NULL);
tcp_poll(_pcb, NULL, 0);
}
_free_closed_slot();
_pcb = NULL;
}
if(_error_cb) {
@@ -882,7 +940,7 @@ void AsyncClient::_error(int8_t err) {
//In LwIP Thread
int8_t AsyncClient::_lwip_fin(tcp_pcb* pcb, int8_t err) {
if(!_pcb || pcb != _pcb){
log_e("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb);
log_d("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb);
return ERR_OK;
}
tcp_arg(_pcb, NULL);
@@ -910,23 +968,27 @@ int8_t AsyncClient::_fin(tcp_pcb* pcb, int8_t err) {
}
int8_t AsyncClient::_sent(tcp_pcb* pcb, uint16_t len) {
_rx_last_packet = millis();
//log_i("%u", len);
_pcb_busy = false;
_rx_last_ack = _rx_last_packet = millis();
if(_sent_cb) {
_sent_cb(_sent_cb_arg, this, len, (millis() - _pcb_sent_at));
_sent_cb(_sent_cb_arg, this, len, (_rx_last_packet - _tx_last_packet));
}
return ERR_OK;
}
int8_t AsyncClient::_recv(tcp_pcb* pcb, pbuf* pb, int8_t err) {
while(pb != NULL) {
if(!_pcb || pcb != _pcb){
log_d("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb);
return ERR_OK;
}
size_t total = 0;
while((pb != NULL) && (ERR_OK == err)) {
_rx_last_packet = millis();
//we should not ack before we assimilate the data
_ack_pcb = true;
pbuf *b = pb;
pb = b->next;
b->next = NULL;
total += b->len;
if(_pb_cb){
_pb_cb(_pb_cb_arg, this, b);
} else {
@@ -935,38 +997,39 @@ int8_t AsyncClient::_recv(tcp_pcb* pcb, pbuf* pb, int8_t err) {
}
if(!_ack_pcb) {
_rx_ack_len += b->len;
} else if(_pcb) {
_tcp_recved(_pcb, _closed_slot, b->len);
}
pbuf_free(b);
}
pbuf_free(b);
}
return ERR_OK;
return _tcp_recved(pcb, _closed_slot, total);
}
int8_t AsyncClient::_poll(tcp_pcb* pcb){
if(!_pcb){
log_w("pcb is NULL");
log_d("pcb is NULL");
return ERR_OK;
}
if(pcb != _pcb){
log_e("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb);
log_d("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb);
return ERR_OK;
}
uint32_t now = millis();
// ACK Timeout
if(_pcb_busy && _ack_timeout && (now - _pcb_sent_at) >= _ack_timeout){
_pcb_busy = false;
log_w("ack timeout %d", pcb->state);
if(_timeout_cb)
_timeout_cb(_timeout_cb_arg, this, (now - _pcb_sent_at));
return ERR_OK;
if(_ack_timeout){
const uint32_t one_day = 86400000;
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)
_timeout_cb(_timeout_cb_arg, this, (now - _tx_last_packet));
return ERR_OK;
}
}
// RX Timeout
if(_rx_since_timeout && (now - _rx_last_packet) >= (_rx_since_timeout * 1000)){
log_w("rx timeout %d", pcb->state);
if(_rx_timeout && (now - _rx_last_packet) >= (_rx_timeout * 1000)) {
log_d("rx timeout %d", pcb->state);
_close();
return ERR_OK;
}
@@ -978,8 +1041,19 @@ int8_t AsyncClient::_poll(tcp_pcb* pcb){
}
void AsyncClient::_dns_found(struct ip_addr *ipaddr){
if(ipaddr && ipaddr->u_addr.ip4.addr){
connect(IPAddress(ipaddr->u_addr.ip4.addr), _connect_port);
#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
} else if(ipaddr && ipaddr->u_addr.ip6.addr){
connect(IPv6Address(ipaddr->u_addr.ip6.addr), _connect_port);
#endif
#else
if(ipaddr) {
IPAddress ip;
ip.from_ip_addr_t(ipaddr);
connect(ip, _connect_port);
#endif
} else {
if(_error_cb) {
_error_cb(_error_cb_arg, this, -55);
@@ -1017,18 +1091,21 @@ size_t AsyncClient::write(const char* data) {
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()) {
if(!will_send) {
return 0;
}
while (connected() && !send()) {
taskYIELD();
}
return will_send;
}
void AsyncClient::setRxTimeout(uint32_t timeout){
_rx_since_timeout = timeout;
_rx_timeout = timeout;
}
uint32_t AsyncClient::getRxTimeout(){
return _rx_since_timeout;
return _rx_timeout;
}
uint32_t AsyncClient::getAckTimeout(){
@@ -1057,6 +1134,18 @@ bool AsyncClient::getNoDelay(){
return tcp_nagle_disabled(_pcb);
}
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
// 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
} else {
_pcb->so_options &= ~SOF_KEEPALIVE; //Turn off TCP Keepalive for the given pcb
}
}
uint16_t AsyncClient::getMss(){
if(!_pcb) {
return 0;
@@ -1068,9 +1157,60 @@ uint32_t AsyncClient::getRemoteAddress() {
if(!_pcb) {
return 0;
}
#if LWIP_IPV4 && LWIP_IPV6
return _pcb->remote_ip.u_addr.ip4.addr;
#else
return _pcb->remote_ip.addr;
#endif
}
#if LWIP_IPV6
ip6_addr_t AsyncClient::getRemoteAddress6() {
if(!_pcb) {
ip6_addr_t nulladdr;
ip6_addr_set_zero(&nulladdr);
return nulladdr;
}
return _pcb->remote_ip.u_addr.ip6;
}
ip6_addr_t AsyncClient::getLocalAddress6() {
if(!_pcb) {
ip6_addr_t nulladdr;
ip6_addr_set_zero(&nulladdr);
return nulladdr;
}
return _pcb->local_ip.u_addr.ip6;
}
#if ESP_IDF_VERSION_MAJOR < 5
IPv6Address AsyncClient::remoteIP6() {
return IPv6Address(getRemoteAddress6().addr);
}
IPv6Address AsyncClient::localIP6() {
return IPv6Address(getLocalAddress6().addr);
}
#else
IPAddress AsyncClient::remoteIP6() {
if (!_pcb) {
return IPAddress(IPType::IPv6);
}
IPAddress ip;
ip.from_ip_addr_t(&(_pcb->remote_ip));
return ip;
}
IPAddress AsyncClient::localIP6() {
if (!_pcb) {
return IPAddress(IPType::IPv6);
}
IPAddress ip;
ip.from_ip_addr_t(&(_pcb->local_ip));
return ip;
}
#endif
#endif
uint16_t AsyncClient::getRemotePort() {
if(!_pcb) {
return 0;
@@ -1082,7 +1222,11 @@ uint32_t AsyncClient::getLocalAddress() {
if(!_pcb) {
return 0;
}
#if LWIP_IPV4 && LWIP_IPV6
return _pcb->local_ip.u_addr.ip4.addr;
#else
return _pcb->local_ip.addr;
#endif
}
uint16_t AsyncClient::getLocalPort() {
@@ -1093,7 +1237,16 @@ uint16_t AsyncClient::getLocalPort() {
}
IPAddress AsyncClient::remoteIP() {
#if ESP_IDF_VERSION_MAJOR < 5
return IPAddress(getRemoteAddress());
#else
if (!_pcb) {
return IPAddress();
}
IPAddress ip;
ip.from_ip_addr_t(&(_pcb->remote_ip));
return ip;
#endif
}
uint16_t AsyncClient::remotePort() {
@@ -1101,9 +1254,19 @@ uint16_t AsyncClient::remotePort() {
}
IPAddress AsyncClient::localIP() {
#if ESP_IDF_VERSION_MAJOR < 5
return IPAddress(getLocalAddress());
#else
if (!_pcb) {
return IPAddress();
}
IPAddress ip;
ip.from_ip_addr_t(&(_pcb->local_ip));
return ip;
#endif
}
uint16_t AsyncClient::localPort() {
return getLocalPort();
}
@@ -1226,7 +1389,7 @@ void AsyncClient::_s_error(void * arg, int8_t err) {
reinterpret_cast<AsyncClient*>(arg)->_error(err);
}
int8_t AsyncClient::_s_connected(void * arg, void * pcb, int8_t err){
int8_t AsyncClient::_s_connected(void * arg, struct tcp_pcb * pcb, int8_t err){
return reinterpret_cast<AsyncClient*>(arg)->_connected(pcb, err);
}
@@ -1236,6 +1399,13 @@ int8_t AsyncClient::_s_connected(void * arg, void * pcb, int8_t err){
AsyncServer::AsyncServer(IPAddress addr, uint16_t port)
: _port(port)
#if ESP_IDF_VERSION_MAJOR < 5
, _bind4(true)
, _bind6(false)
#else
, _bind4(addr.type() != IPType::IPv6)
, _bind6(addr.type() == IPType::IPv6)
#endif
, _addr(addr)
, _noDelay(false)
, _pcb(0)
@@ -1243,9 +1413,27 @@ AsyncServer::AsyncServer(IPAddress addr, uint16_t port)
, _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)
{}
#endif
AsyncServer::AsyncServer(uint16_t port)
: _port(port)
, _bind4(true)
, _bind6(false)
, _addr((uint32_t) IPADDR_ANY)
#if ESP_IDF_VERSION_MAJOR < 5
, _addr6()
#endif
, _noDelay(false)
, _pcb(0)
, _connect_cb(0)
@@ -1271,15 +1459,24 @@ void AsyncServer::begin(){
return;
}
int8_t err;
_pcb = tcp_new_ip_type(IPADDR_TYPE_V4);
_pcb = tcp_new_ip_type(_bind4 && _bind6 ? IPADDR_TYPE_ANY : (_bind6 ? IPADDR_TYPE_V6 : IPADDR_TYPE_V4));
if (!_pcb){
log_e("_pcb == NULL");
return;
}
ip_addr_t local_addr;
local_addr.type = IPADDR_TYPE_V4;
local_addr.u_addr.ip4.addr = (uint32_t) _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
local_addr.type = IPADDR_TYPE_V6;
memcpy(local_addr.u_addr.ip6.addr, static_cast<const uint32_t*>(_addr6), sizeof(uint32_t) * 4);
} else {
local_addr.type = IPADDR_TYPE_V4;
local_addr.u_addr.ip4.addr = _addr;
}
#else
_addr.to_ip_addr_t(&local_addr);
#endif
err = _tcp_bind(_pcb, &local_addr, _port);
if (err != ERR_OK) {
@@ -1322,7 +1519,7 @@ int8_t AsyncServer::_accept(tcp_pcb* pcb, int8_t err){
if(tcp_close(pcb) != ERR_OK){
tcp_abort(pcb);
}
log_e("FAIL");
log_d("FAIL");
return ERR_OK;
}

View File

@@ -22,13 +22,34 @@
#ifndef ASYNCTCP_H_
#define ASYNCTCP_H_
#define ASYNCTCP_VERSION "3.1.4"
#define ASYNCTCP_VERSION_MAJOR 3
#define ASYNCTCP_VERSION_MINOR 1
#define ASYNCTCP_VERSION_REVISION 4
#define ASYNCTCP_FORK_mathieucarbou
#include "IPAddress.h"
#include "sdkconfig.h"
#if ESP_IDF_VERSION_MAJOR < 5
#include "IPv6Address.h"
#endif
#include <functional>
#include "lwip/ip_addr.h"
#include "lwip/ip6_addr.h"
#ifndef LIBRETINY
#include "sdkconfig.h"
extern "C" {
#include "freertos/semphr.h"
#include "lwip/pbuf.h"
}
#else
extern "C" {
#include <semphr.h>
#include <lwip/pbuf.h>
}
#define CONFIG_ASYNC_TCP_RUNNING_CORE -1 //any available core
#define CONFIG_ASYNC_TCP_USE_WDT 0
#endif
//If core is not defined, then we are running in Arduino or PIO
#ifndef CONFIG_ASYNC_TCP_RUNNING_CORE
@@ -36,9 +57,24 @@ extern "C" {
#define CONFIG_ASYNC_TCP_USE_WDT 1 //if enabled, adds between 33us and 200us per event
#endif
#ifndef CONFIG_ASYNC_TCP_STACK_SIZE
#define CONFIG_ASYNC_TCP_STACK_SIZE 8192 * 2
#endif
#ifndef CONFIG_ASYNC_TCP_PRIORITY
#define CONFIG_ASYNC_TCP_PRIORITY 10
#endif
#ifndef CONFIG_ASYNC_TCP_QUEUE_SIZE
#define CONFIG_ASYNC_TCP_QUEUE_SIZE 64
#endif
#ifndef CONFIG_ASYNC_TCP_MAX_ACK_TIME
#define CONFIG_ASYNC_TCP_MAX_ACK_TIME 5000
#endif
class AsyncClient;
#define ASYNC_MAX_ACK_TIME 5000
#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.
@@ -65,8 +101,11 @@ class AsyncClient {
bool operator!=(const AsyncClient &other) {
return !(*this == other);
}
bool connect(IPAddress ip, uint16_t port);
bool connect(const char* host, uint16_t port);
bool connect(const IPAddress& ip, uint16_t port);
#if ESP_IDF_VERSION_MAJOR < 5
bool connect(const IPv6Address& ip, uint16_t port);
#endif
bool connect(const char *host, uint16_t port);
void close(bool now = false);
void stop();
int8_t abort();
@@ -99,16 +138,29 @@ class AsyncClient {
void setNoDelay(bool nodelay);
bool getNoDelay();
void setKeepAlive(uint32_t ms, uint8_t cnt);
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
#endif
//compatibility
IPAddress remoteIP();
uint16_t remotePort();
uint16_t remotePort();
IPAddress localIP();
uint16_t localPort();
uint16_t localPort();
void onConnect(AcConnectHandler cb, void* arg = 0); //on successful connect
void onDisconnect(AcConnectHandler cb, void* arg = 0); //disconnected
@@ -133,13 +185,15 @@ class AsyncClient {
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, void* tpcb, int8_t err);
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;
@@ -160,19 +214,19 @@ class AsyncClient {
AcConnectHandler _poll_cb;
void* _poll_cb_arg;
bool _pcb_busy;
uint32_t _pcb_sent_at;
bool _ack_pcb;
uint32_t _tx_last_packet;
uint32_t _rx_ack_len;
uint32_t _rx_last_packet;
uint32_t _rx_since_timeout;
uint32_t _rx_timeout;
uint32_t _rx_last_ack;
uint32_t _ack_timeout;
uint16_t _connect_port;
int8_t _close();
void _free_closed_slot();
void _allocate_closed_slot();
int8_t _connected(void* pcb, int8_t err);
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);
@@ -188,6 +242,9 @@ class AsyncClient {
class AsyncServer {
public:
AsyncServer(IPAddress addr, uint16_t port);
#if ESP_IDF_VERSION_MAJOR < 5
AsyncServer(IPv6Address addr, uint16_t port);
#endif
AsyncServer(uint16_t port);
~AsyncServer();
void onClient(AcConnectHandler cb, void* arg);
@@ -203,7 +260,12 @@ class AsyncServer {
protected:
uint16_t _port;
bool _bind4 = false;
bool _bind6 = false;
IPAddress _addr;
#if ESP_IDF_VERSION_MAJOR < 5
IPv6Address _addr6;
#endif
bool _noDelay;
tcp_pcb* _pcb;
AcConnectHandler _connect_cb;

View File

@@ -1,90 +0,0 @@
/*
IPv6Address.cpp - Base class that provides IPv6Address
Copyright (c) 2011 Adrian McEwen. All right reserved.
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
*/
#include <Arduino.h>
#include <IPv6Address.h>
#include <Print.h>
IPv6Address::IPv6Address()
{
memset(_address.bytes, 0, sizeof(_address.bytes));
}
IPv6Address::IPv6Address(const uint8_t *address)
{
memcpy(_address.bytes, address, sizeof(_address.bytes));
}
IPv6Address::IPv6Address(const uint32_t *address)
{
memcpy(_address.bytes, (const uint8_t *)address, sizeof(_address.bytes));
}
IPv6Address& IPv6Address::operator=(const uint8_t *address)
{
memcpy(_address.bytes, address, sizeof(_address.bytes));
return *this;
}
bool IPv6Address::operator==(const uint8_t* addr) const
{
return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0;
}
size_t IPv6Address::printTo(Print& p) const
{
size_t n = 0;
for(int i = 0; i < 16; i+=2) {
if(i){
n += p.print(':');
}
n += p.printf("%02x", _address.bytes[i]);
n += p.printf("%02x", _address.bytes[i+1]);
}
return n;
}
String IPv6Address::toString() const
{
char szRet[40];
sprintf(szRet,"%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
_address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3],
_address.bytes[4], _address.bytes[5], _address.bytes[6], _address.bytes[7],
_address.bytes[8], _address.bytes[9], _address.bytes[10], _address.bytes[11],
_address.bytes[12], _address.bytes[13], _address.bytes[14], _address.bytes[15]);
return String(szRet);
}
bool IPv6Address::fromString(const char *address)
{
//format 0011:2233:4455:6677:8899:aabb:ccdd:eeff
if(strlen(address) != 39){
return false;
}
char * pos = (char *)address;
size_t i = 0;
for(i = 0; i < 16; i+=2) {
if(!sscanf(pos, "%2hhx", &_address.bytes[i]) || !sscanf(pos+2, "%2hhx", &_address.bytes[i+1])){
return false;
}
pos += 5;
}
return true;
}

View File

@@ -1,94 +0,0 @@
/*
IPv6Address.h - Base class that provides IPv6Address
Copyright (c) 2011 Adrian McEwen. All right reserved.
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
*/
#ifndef IPv6Address_h
#define IPv6Address_h
#include <stdint.h>
#include <WString.h>
#include <Printable.h>
// A class to make it easier to handle and pass around IP addresses
class IPv6Address: public Printable
{
private:
union {
uint8_t bytes[16]; // IPv4 address
uint32_t dword[4];
} _address;
// Access the raw byte array containing the address. Because this returns a pointer
// to the internal structure rather than a copy of the address this function should only
// be used when you know that the usage of the returned uint8_t* will be transient and not
// stored.
uint8_t* raw_address()
{
return _address.bytes;
}
public:
// Constructors
IPv6Address();
IPv6Address(const uint8_t *address);
IPv6Address(const uint32_t *address);
virtual ~IPv6Address() {}
bool fromString(const char *address);
bool fromString(const String &address) { return fromString(address.c_str()); }
operator const uint8_t*() const
{
return _address.bytes;
}
operator const uint32_t*() const
{
return _address.dword;
}
bool operator==(const IPv6Address& addr) const
{
return (_address.dword[0] == addr._address.dword[0])
&& (_address.dword[1] == addr._address.dword[1])
&& (_address.dword[2] == addr._address.dword[2])
&& (_address.dword[3] == addr._address.dword[3]);
}
bool operator==(const uint8_t* addr) const;
// Overloaded index operator to allow getting and setting individual octets of the address
uint8_t operator[](int index) const
{
return _address.bytes[index];
}
uint8_t& operator[](int index)
{
return _address.bytes[index];
}
// Overloaded copy operators to allow initialisation of IPv6Address objects from other types
IPv6Address& operator=(const uint8_t *address);
virtual size_t printTo(Print& p) const;
String toString() const;
friend class UDP;
friend class Client;
friend class Server;
};
#endif