chore: import local project into Gitea

This commit is contained in:
2026-05-20 10:03:16 -07:00
commit 7bd88d29b5
4043 changed files with 1056916 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
# See: https://github.com/codespell-project/codespell#using-a-config-file
[codespell]
# In the event of a false positive, add the problematic word, in all lowercase, to a comma-separated list here:
ignore-words-list = ,
check-filenames =
check-hidden =
skip = ./.git,./src,./examples,./Packages_Patches,./LibraryPatches

View File

@@ -0,0 +1,76 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
### Describe the bug
A clear and concise description of what the bug is.
### Steps to Reproduce
Steps to reproduce the behavior. Including the [MRE](https://stackoverflow.com/help/minimal-reproducible-example) sketches
### Expected behavior
A clear and concise description of what you expected to happen.
### Actual behavior
A clear and concise description of what you expected to happen.
### Debug and AT-command log (if applicable)
A clear and concise description of what you expected to happen.
### Screenshots
If applicable, add screenshots to help explain your problem.
---
### Information
Please ensure to specify the following:
* Arduino IDE version (e.g. 1.8.19) or Platform.io version
* `RP2040` Core Version (e.g. RP2040 core v2.7.1)
* `RP2040` Board type (e.g. RASPBERRY_PI_PICO_W)
* Contextual information (e.g. what you were trying to achieve)
* Simplest possible steps to reproduce
* Anything that might be relevant in your opinion, such as:
* Operating system (Windows, Ubuntu, etc.) and the output of `uname -a`
* Network configuration
Please be educated, civilized and constructive as you've always been. Disrespective posts against [GitHub Code of Conduct](https://docs.github.com/en/site-policy/github-terms/github-event-code-of-conduct) will be ignored and deleted.
---
### Example
```
Arduino IDE version: 1.8.19
RP2040 core v2.7.1
RASPBERRY_PI_PICO_W Module
OS: Ubuntu 20.04 LTS
Linux xy-Inspiron-3593 5.15.0-58-generic #64~20.04.1-Ubuntu SMP Fri Jan 6 16:42:31 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
Context:
I encountered a crash while using this library
Steps to reproduce:
1. ...
2. ...
3. ...
4. ...
```
### Additional context
Add any other context about the problem here.

View File

@@ -0,0 +1,23 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
### Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
### Describe the solution you'd like
A clear and concise description of what you want to happen.
### Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.
### Additional context
Add any other context or screenshots about the feature request here.

View File

@@ -0,0 +1,53 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
name: Build (Arduino)
on:
push:
branches:
- main
- dev
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
arduino-rpi:
name: Arduino RPI
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
board:
- rpipicow
- rpipico2w
steps:
- name: Install arduino-cli
run: curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR=/usr/local/bin sh
- name: Update core index
run: arduino-cli core update-index --additional-urls https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
- name: Install core
run: arduino-cli core install --additional-urls https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json rp2040:rp2040
- name: Install ArduinoJson
run: arduino-cli lib install ArduinoJson
- name: Install RPAsyncTCP
run: ARDUINO_LIBRARY_ENABLE_UNSAFE_INSTALL=true arduino-cli lib install --git-url https://github.com/ayushsharma82/RPAsyncTCP#v1.3.0
- name: Checkout
uses: actions/checkout@v4
- name: Build Examples
run: |
for i in `ls examples`; do
echo "============================================================="
echo "Building examples/$i..."
echo "============================================================="
arduino-cli compile --library . --warnings none -b rp2040:rp2040:${{ matrix.board }} "examples/$i/$i.ino"
done

View File

@@ -0,0 +1,57 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
name: Build (pioarduino)
on:
push:
branches:
- main
- dev
- release/*
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
platformio-rpi:
name: PIO RPI
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
board:
- rpipicow
- rpipico2w
steps:
- name: Checkout
uses: actions/checkout@v4
- 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 PIO
run: |
python -m pip install --upgrade pip
pip install --upgrade platformio
- name: Build Examples
run: |
for i in `ls examples`; do
echo "============================================================="
echo "Building examples/$i..."
echo "============================================================="
PLATFORMIO_SRC_DIR=examples/$i PIO_BOARD=${{ matrix.board }} pio run -e ci-raspberrypi
done

View File

@@ -0,0 +1,16 @@
name: Report Size Deltas
on:
schedule:
- cron: '*/5 * * * *'
jobs:
report:
runs-on: ubuntu-latest
steps:
- name: Comment size deltas reports to PRs
uses: arduino/report-size-deltas@v1
with:
# The name of the workflow artifact created by the "Compile Examples" workflow
sketches-reports-source: sketches-reports

View File

@@ -0,0 +1,20 @@
name: 'Close stale issues and PR'
on:
schedule:
- cron: '30 6 * * *'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.'
stale-pr-message: 'This PR is marked as stale because it has been open 45 days with no activity. You can remove stale label or comment if this PR is still valid.'
close-issue-message: 'This issue was closed because it has been stalled for 5 days with no activity.'
exempt-issue-labels: bug,triage,feature_request
exempt-pr-labels: wip,feature_request
exempt-all-assignees: true
days-before-stale: 30
days-before-close: 7
days-before-pr-close: -1

View File

@@ -0,0 +1,35 @@
.DS_Store
.vscode
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app

View File

@@ -0,0 +1 @@
{"type": "library", "name": "RPAsyncTCP", "version": "1.3.2", "spec": {"owner": "ayushsharma82", "id": 18425, "name": "RPAsyncTCP", "requirements": null, "uri": null}}

View File

@@ -0,0 +1,60 @@
## Contributing to RPAsyncTCP
### Reporting Bugs
Please report bugs in [RPAsyncTCP Issues](https://github.com/ayushsharma82/RPAsyncTCP/issues) if you find them.
However, before reporting a bug please check through the following:
* [Existing Open Issues](https://github.com/ayushsharma82/RPAsyncTCP/issues) - someone might have already encountered this.
If you don't find anything, please [open a new issue](https://github.com/ayushsharma82/RPAsyncTCP/issues/new).
---
### How to submit a bug report
Please ensure to specify the following:
* Arduino IDE version (e.g. 1.8.19) or Platform.io version
* `RP2040` Core Version (e.g. RP2040 core v2.7.1)
* `RP2040` Board type (e.g. RASPBERRY_PI_PICO_W)
* Contextual information (e.g. what you were trying to achieve)
* Simplest possible steps to reproduce
* Anything that might be relevant in your opinion, such as:
* Operating system (Windows, Ubuntu, etc.) and the output of `uname -a`
* Network configuration
Please be educated, civilized and constructive as you've always been. Disrespective posts against [GitHub Code of Conduct](https://docs.github.com/en/site-policy/github-terms/github-event-code-of-conduct) will be ignored and deleted.
---
### Example
```
Arduino IDE version: 1.8.19
RP2040 core v2.7.1
RASPBERRY_PI_PICO_W Module
OS: Ubuntu 20.04 LTS
Linux xy-Inspiron-3593 5.15.0-58-generic #64~20.04.1-Ubuntu SMP Fri Jan 6 16:42:31 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
Context:
I encountered a crash while using this library
Steps to reproduce:
1. ...
2. ...
3. ...
4. ...
```
### Sending Feature Requests
Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful.
There are usually some outstanding feature requests in the [existing issues list](https://github.com/ayushsharma82/RPAsyncTCP/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them.
### Sending Pull Requests
Pull Requests with changes and fixes are also welcome!

View File

@@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

View File

@@ -0,0 +1,105 @@
![RPAsyncTCP](https://raw.githubusercontent.com/ayushsharma82/RPAsyncTCP/master/docs/splash-dark.png#gh-dark-mode-only)
![RPAsyncTCP](https://raw.githubusercontent.com/ayushsharma82/RPAsyncTCP/master/docs/splash-light.png#gh-light-mode-only)
[![GitHub release](https://img.shields.io/github/release/ayushsharma82/RPAsyncTCP.svg)](https://github.com/ayushsharma82/RPAsyncTCP/releases)
[![GitHub issues](https://img.shields.io/github/issues/ayushsharma82/RPAsyncTCP.svg)](http://github.com/ayushsharma82/RPAsyncTCP/issues)
[![arduino-library-badge](https://www.ardu-badge.com/badge/RPAsyncTCP.svg)](https://www.ardu-badge.com/RPAsyncTCP)
> [!TIP]
> This library is a fork of **AsyncTCP_RP2040W** and is a drop-in replacement for it. If you were previously using `AsyncTCP_RP2040W`, you will need to update your `include` directives to `#include <RPAsyncTCP.h>`.
## Table of Contents
- [What is RPAsyncTCP?](#what-is-rpasynctcp)
- [Features](#i-features)
- [Supported Boards](#ii-supported-boards)
- [Installation](#installation)
- [Using Arduino Library Manager](#i-using-arduino-library-manager)
- [Using PlatformIO](#ii-using-platformio)
- [Manual Installation](#iii-manual-installation)
- [Examples](#examples)
- [Debugging](#debugging)
- [License](#license)
- [Authors & Maintainers](#authors--maintainers)
<br/>
## What is RPAsyncTCP?
RPAsyncTCP brings the **asynchronous** networking power of ESPAsyncTCP to **RP2040+W and RP2350+W MCUs**, serving as the foundation for libraries like `ESPAsyncWebServer` which allows for advanced routing and better performance compared to syncronous `WebServer` library.
### I. Features
- Handles multiple connections simultaneously
- Callbacks are triggered when requests are ready
- Faster response handling and improved efficiency
- Support for WebSockets, EventSource (Server-Sent Events), and URL Rewriting
- Static file serving with cache and indexing support
- Simple template processing
### II. Supported Boards
RPAsyncTCP supports [earlephilhower/arduino-pico](https://github.com/earlephilhower/arduino-pico) Arduino Board.
- **RP2040** + CYW43439 WiFi (Example: RaspberryPi Pico W)
- **RP2350** + CYW43439 WiFi (Example: RaspberryPi Pico 2W)
<br/>
## Installation
### I. Using Arduino Library Manager
Search for **RPAsyncTCP** in the Arduino Library Manager and install the latest version.
### II. Using PlatformIO
Search for **RPAsyncTCP** in the PlatformIO Library Manager and install in your project.
### III. Manual Installation
If you would like to install manually, you can follow these steps:
1. Download the latest release from [GitHub](https://github.com/ayushsharma82/RPAsyncTCP)
2. For Arduino IDE: Extract and place the folder in `~/Arduino/libraries/`
3. For PlatformIO, you can place the folder in your `libs` folder of your project.
<br/>
## Examples
Check out the example projects:
- [Client](https://github.com/ayushsharma82/RPAsyncTCP/tree/main/examples/Client/Client.ino)
- [Server](https://github.com/ayushsharma82/RPAsyncTCP/tree/main/examples/Server/Server.ino)
<br/>
## Debugging
Debugging is enabled by default on Serial. You can adjust the log level by modifying `_RPAsyncTCP_LOGLEVEL_` in the library files:
```cpp
// 0: DISABLED: no logging
// 1: ERROR: errors
#define _RPAsyncTCP_LOGLEVEL_ 1
```
<br/>
## License
This library is licensed under the [LGPL-3.0 License](LICENSE).
<br/>
## Authors & Maintainers
Thanks to [Khoi Hoang](https://github.com/khoih-prog) for the original fork to add support for RP2040+W. You can support him for his original work [here](https://www.buymeacoffee.com/khoihprog6).
- **Previous Authors:** Hristo Gochkov (2016), Khoi Hoang (2022)
- **Current Maintainer:** Ayush Sharma (2025)

View File

@@ -0,0 +1,42 @@
# RPAsyncTCP Library
[![arduino-library-badge](https://www.ardu-badge.com/badge/RPAsyncTCP.svg?)](https://www.ardu-badge.com/RPAsyncTCP)
[![GitHub release](https://img.shields.io/github/release/ayushsharma82/RPAsyncTCP.svg)](https://github.com/ayushsharma82/RPAsyncTCP/releases)
[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](#Contributing)
[![GitHub issues](https://img.shields.io/github/issues/ayushsharma82/RPAsyncTCP.svg)](http://github.com/ayushsharma82/RPAsyncTCP/issues)
<a href="https://www.buymeacoffee.com/khoihprog6" title="Donate to my libraries using BuyMeACoffee"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Donate to my libraries using BuyMeACoffee" style="height: 50px !important;width: 181px !important;" ></a>
<a href="https://www.buymeacoffee.com/khoihprog6" title="Donate to my libraries using BuyMeACoffee"><img src="https://img.shields.io/badge/buy%20me%20a%20coffee-donate-orange.svg?logo=buy-me-a-coffee&logoColor=FFDD00" style="height: 20px !important;width: 200px !important;" ></a>
---
---
## Table of Contents
* [Changelog](#changelog)
* [Releases v1.2.0](#Releases-v120)
* [Releases v1.1.0](#Releases-v110)
* [Initial Releases v1.0.0](#Initial-Releases-v100)
---
---
## Changelog
### Releases v1.2.0
1. Add complex auto-reconnect `AsyncTCP_Client` and `AsyncTCP_Server` examples
2. Improve `README.md` so that links can be used in other sites, such as `PIO`
### Releases v1.1.0
1. Fix issue with slow browsers or network
2. Remove hard-code if possible
3. Improve debug messages by adding functions to display `error/state messages` instead of `cryptic error/state number`
4. Clean up
### Initial Releases v1.0.0
1. Initial coding to support **RASPBERRY_PI_PICO_W with CYW43439 WiFi**, using [**arduino-pico core v2.4.0+**](https://github.com/earlephilhower/arduino-pico)

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

@@ -0,0 +1,130 @@
#include <RPAsyncTCP.h>
// Network Configuration
const char* SSID = "your_ssid";
const char* PASSWORD = "12345678";
const IPAddress SERVER_IP(192, 168, 2, 128);
const uint16_t TCP_PORT = 5698;
// Timing Configuration
const unsigned long RECONNECT_CHECK_INTERVAL = 1000; // 1 second
const unsigned long HEARTBEAT_INTERVAL = 10000; // 10 seconds
// Client Configuration
AsyncClient* tcpClient = nullptr;
bool isConnected = false;
const uint8_t REPLY_BUFFER_SIZE = 64;
// Status Tracking
unsigned long lastHeartbeat = 0;
unsigned long lastReconnectAttempt = 0;
void printNetworkStatus() {
Serial.print("Connected to: ");
Serial.println(WiFi.SSID());
Serial.print("Local IP: ");
Serial.println(WiFi.localIP());
}
bool sendHeartbeat(AsyncClient* client) {
if (!client || !client->canSend()) {
Serial.println("Cannot send heartbeat");
return false;
}
char message[REPLY_BUFFER_SIZE];
snprintf(message, sizeof(message), "Client heartbeat from %s", WiFi.localIP().toString().c_str());
size_t added = client->add(message, strlen(message));
if (added == 0) {
Serial.println("Failed to add heartbeat to buffer");
return false;
}
if (!client->send()) {
Serial.println("Failed to send heartbeat");
return false;
}
return true;
}
void handleServerData(void* arg, AsyncClient* client, void* data, size_t len) {
Serial.printf("\nReceived %d bytes from %s\n", len, client->remoteIP().toString().c_str());
Serial.write(static_cast<uint8_t*>(data), len);
// Immediate reply to server
char ackMsg[] = "ACK";
client->add(ackMsg, strlen(ackMsg));
client->send();
}
void handleConnect(void* arg, AsyncClient* client) {
Serial.printf("Connected to server %s:%d\n", SERVER_IP.toString().c_str(), TCP_PORT);
isConnected = true;
sendHeartbeat(client); // Send initial heartbeat
}
void handleDisconnect(void* arg, AsyncClient* client) {
Serial.println("Disconnected from server");
isConnected = false;
}
bool connectToServer() {
if (tcpClient) {
delete tcpClient;
tcpClient = nullptr;
}
tcpClient = new AsyncClient();
if (!tcpClient) {
Serial.println("Failed to create TCP client");
return false;
}
// Setup event handlers
tcpClient->onData(handleServerData);
tcpClient->onConnect(handleConnect);
tcpClient->onDisconnect(handleDisconnect);
return tcpClient->connect(SERVER_IP, TCP_PORT);
}
void setup() {
Serial.begin(115200);
while (!Serial && millis() < 5000); // Wait for serial port
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("WiFi module not found!");
while (true); // Halt if no WiFi
}
Serial.print("Connecting to: ");
Serial.println(SSID);
while (WiFi.begin(SSID, PASSWORD) != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
printNetworkStatus();
connectToServer();
}
void loop() {
unsigned long currentTime = millis();
// Heartbeat logic: Send every HEARTBEAT_INTERVAL
if (isConnected && (currentTime - lastHeartbeat >= HEARTBEAT_INTERVAL)) {
if (sendHeartbeat(tcpClient)) {
lastHeartbeat = currentTime;
}
}
// Reconnection logic
if (!isConnected && (currentTime - lastReconnectAttempt >= RECONNECT_CHECK_INTERVAL)) {
Serial.println("Attempting to reconnect...");
connectToServer();
lastReconnectAttempt = currentTime;
}
}

View File

@@ -0,0 +1,117 @@
#include <RPAsyncTCP.h>
#include <vector>
#include <algorithm>
// Network Configuration
const char* SSID = "your_ssid";
const char* PASSWORD = "12345678";
const uint16_t TCP_PORT = 5698;
// Server Configuration
AsyncServer* tcpServer = nullptr;
std::vector<AsyncClient*> connectedClients;
IPAddress serverIP;
// Server Response Configuration
const uint16_t RESPONSE_BUFFER_SIZE = 128;
void printNetworkStatus() {
if (serverIP == INADDR_NONE) return; // Ensure IP is valid
Serial.print("Connected to: ");
Serial.println(WiFi.SSID());
serverIP = WiFi.localIP();
Serial.print("Server IP: ");
Serial.println(serverIP);
}
void handleClientDisconnect(AsyncClient* client) {
Serial.printf("Client %s disconnected\n", client->remoteIP().toString().c_str());
// Remove and delete client
connectedClients.erase(std::remove(connectedClients.begin(), connectedClients.end(), client), connectedClients.end());
delete client;
}
void sendWelcomeMessage(AsyncClient* client) {
if (!client->canSend() || client->space() < RESPONSE_BUFFER_SIZE) return;
char response[RESPONSE_BUFFER_SIZE];
snprintf(response, sizeof(response), "Connected to RPAsyncTCP Server @ %s", serverIP.toString().c_str());
size_t len = strlen(response);
size_t added = client->add(response, len, ASYNC_WRITE_FLAG_COPY);
if (added != len) {
Serial.println("Failed to add all data to buffer");
return;
}
if (!client->send()) {
Serial.println("Failed to send data");
}
}
// Client Event Handlers
void handleClientData(void* arg, AsyncClient* client, void* data, size_t length) {
Serial.printf("\nReceived %zu bytes from %s\n", length, client->remoteIP().toString().c_str());
Serial.write(static_cast<uint8_t*>(data), length);
sendWelcomeMessage(client);
}
void handleClientError(void* arg, AsyncClient* client, err_t error) {
Serial.printf("Client %s error: %s\n", client->remoteIP().toString().c_str(), client->errorToString(error));
handleClientDisconnect(client);
}
void handleClientTimeout(void* arg, AsyncClient* client, uint32_t time) {
Serial.printf("Client %s timeout after %ums\n",
client->remoteIP().toString().c_str(), time);
handleClientDisconnect(client);
}
// Server Event Handler
void handleNewConnection(void* arg, AsyncClient* client) {
Serial.printf("New client connected: %s\n", client->remoteIP().toString().c_str());
// Setup client callbacks
client->onData(handleClientData, nullptr);
client->onError(handleClientError, nullptr);
client->onDisconnect([](void*, AsyncClient* c) { handleClientDisconnect(c); }, nullptr);
client->onTimeout(handleClientTimeout, nullptr);
connectedClients.push_back(client);
sendWelcomeMessage(client);
}
void initializeWiFi() {
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("WiFi module not found!");
while(true);
}
Serial.print("Connecting to: ");
Serial.println(SSID);
while (WiFi.begin(SSID, PASSWORD) != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
}
void setup() {
Serial.begin(115200);
while (!Serial && millis() < 5000);
initializeWiFi();
printNetworkStatus();
tcpServer = new AsyncServer(TCP_PORT);
tcpServer->onClient(handleNewConnection, tcpServer);
tcpServer->begin();
Serial.printf("Server running at %s:%d\n", serverIP.toString().c_str(), TCP_PORT);
}
void loop() {
delay(1); // Yield to background tasks
}

View File

@@ -0,0 +1,36 @@
{
"name": "RPAsyncTCP",
"version": "1.3.2",
"keywords": "http, communication, async, websocket, webserver, async-webserver, async-tcp, async-udp, async-websocket, async-http, ssl, tls, rp2040, rp2350, rp2350w, rp2040w, raspberry-pi-pico-w, cyw43439, wifi",
"description": "Asynchronous TCP Library for RP2040+W, RP2350+W (arduino-pico core)",
"authors": [
{
"name": "Hristo Gochkov",
"url": "https://github.com/me-no-dev"
},
{
"name": "Khoi Hoang",
"url": "https://github.com/khoih-prog",
"email": "khoih.prog@gmail.com"
},
{
"name": "Ayush Sharma",
"url": "https://github.com/ayushsharma82",
"email": "asrocks5@gmail.com",
"maintainer": true
}
],
"repository": {
"type": "git",
"url": "https://github.com/ayushsharma82/RPAsyncTCP"
},
"homepage": "https://github.com/ayushsharma82/RPAsyncTCP",
"export": {
"exclude": ["linux", "extras", "tests"]
},
"license": "LGPL-3.0",
"frameworks": "*",
"platforms": "raspberrypi",
"examples": "examples/*/*/*.ino",
"headers": "RPAsyncTCP.h"
}

View File

@@ -0,0 +1,12 @@
name=RPAsyncTCP
version=1.3.2
author=Hristo Gochkov,Khoi Hoang,Ayush Sharma
maintainer=Ayush Sharma <asrocks5@gmail.com>
sentence=Asynchronous TCP Library for RP2040+W, RP2350+W (arduino-pico core)
paragraph=Asynchronous TCP Library for RP2040+W, RP2350+W (arduino-pico core), intended to be used with ESPAsyncWebServer library.
category=Communication
url=https://github.com/ayushsharma82/RPAsyncTCP
architectures=rp2040,rp2350
repository=https://github.com/ayushsharma82/RPAsyncTCP
license=LGPL-3.0
includes=RPAsyncTCP.h

View File

@@ -0,0 +1,59 @@
[platformio]
default_envs = rp2040, rp2350
lib_dir = .
; src_dir = examples/Client
src_dir = examples/Server
[env:rp2040]
framework = arduino
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
board = rpipicow
board_build.core = earlephilhower
build_flags =
-Og
-Wall -Wextra
-Wno-unused-parameter
; -D CONFIG_ARDUHAL_LOG_COLORS
-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
monitor_speed = 115200
monitor_filters = log2file
lib_compat_mode = strict
lib_ldf_mode = chain
lib_deps =
ayushsharma82/RPAsyncTCP@^1.3.1
lib_ignore =
lwIP_ESPHost
[env:rp2350]
framework = arduino
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
board = rpipico2w
board_build.core = earlephilhower
build_flags =
-Og
-Wall -Wextra
-Wno-unused-parameter
; -D CONFIG_ARDUHAL_LOG_COLORS
-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
monitor_speed = 115200
monitor_filters = log2file
lib_compat_mode = strict
lib_ldf_mode = chain
lib_deps =
ayushsharma82/RPAsyncTCP@^1.3.1
lib_ignore =
lwIP_ESPHost
; CI
[env:ci-raspberrypi]
framework = arduino
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
board = ${sysenv.PIO_BOARD}
board_build.core = earlephilhower
lib_deps =
ayushsharma82/RPAsyncTCP@^1.3.1
lib_ignore =
lwIP_ESPHost
build_flags = ${env.build_flags}
-Wno-missing-field-initializers

View File

@@ -0,0 +1,355 @@
/*
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
*/
#if !defined(_RPAsyncTCP_LOGLEVEL_)
#define _RPAsyncTCP_LOGLEVEL_ 1
#endif
#include "AsyncPrinter.h"
#include "debug.h"
/////////////////////////////////////////////////////////
AsyncPrinter::AsyncPrinter()
: _client(NULL)
, _data_cb(NULL)
, _data_arg(NULL)
, _close_cb(NULL)
, _close_arg(NULL)
, _tx_buffer(NULL)
, _tx_buffer_size(TCP_MSS)
, next(NULL)
{}
/////////////////////////////////////////////////////////
AsyncPrinter::AsyncPrinter(AsyncClient *client, size_t txBufLen)
: _client(client)
, _data_cb(NULL)
, _data_arg(NULL)
, _close_cb(NULL)
, _close_arg(NULL)
, _tx_buffer(NULL)
, _tx_buffer_size(txBufLen)
, next(NULL)
{
_attachCallbacks();
_tx_buffer = new (std::nothrow) cbuf(_tx_buffer_size);
if (_tx_buffer == NULL)
{
panic(); //What should we do?
}
}
/////////////////////////////////////////////////////////
AsyncPrinter::~AsyncPrinter()
{
_on_close();
}
/////////////////////////////////////////////////////////
void AsyncPrinter::onData(ApDataHandler cb, void *arg)
{
_data_cb = cb;
_data_arg = arg;
}
/////////////////////////////////////////////////////////
void AsyncPrinter::onClose(ApCloseHandler cb, void *arg)
{
_close_cb = cb;
_close_arg = arg;
}
/////////////////////////////////////////////////////////
int AsyncPrinter::connect(IPAddress ip, uint16_t port)
{
if (_client != NULL && connected())
return 0;
_client = new (std::nothrow) AsyncClient();
if (_client == NULL)
{
panic();
}
_client->onConnect([](void *obj, AsyncClient * c)
{
((AsyncPrinter*)(obj))->_onConnect(c);
}, this);
if (_client->connect(ip, port))
{
while (_client && _client->state() < 4)
delay(1);
return connected();
}
return 0;
}
/////////////////////////////////////////////////////////
int AsyncPrinter::connect(const char *host, uint16_t port)
{
if (_client != NULL && connected())
return 0;
_client = new (std::nothrow) AsyncClient();
if (_client == NULL)
{
panic();
}
_client->onConnect([](void *obj, AsyncClient * c)
{
((AsyncPrinter*)(obj))->_onConnect(c);
}, this);
if (_client->connect(host, port))
{
while (_client && _client->state() < 4)
delay(1);
return connected();
}
return 0;
}
/////////////////////////////////////////////////////////
void AsyncPrinter::_onConnect(AsyncClient *c)
{
RPAsyncTCP_UNUSED(c);
if (_tx_buffer != NULL)
{
cbuf *b = _tx_buffer;
_tx_buffer = NULL;
delete b;
}
_tx_buffer = new (std::nothrow) cbuf(_tx_buffer_size);
if (_tx_buffer)
{
panic();
}
_attachCallbacks();
}
/////////////////////////////////////////////////////////
AsyncPrinter::operator bool()
{
return connected();
}
/////////////////////////////////////////////////////////
AsyncPrinter & AsyncPrinter::operator=(const AsyncPrinter &other)
{
if (_client != NULL)
{
_client->close(true);
_client = NULL;
}
_tx_buffer_size = other._tx_buffer_size;
if (_tx_buffer != NULL)
{
cbuf *b = _tx_buffer;
_tx_buffer = NULL;
delete b;
}
_tx_buffer = new (std::nothrow) cbuf(other._tx_buffer_size);
if (_tx_buffer == NULL)
{
panic();
}
_client = other._client;
_attachCallbacks();
return *this;
}
/////////////////////////////////////////////////////////
size_t AsyncPrinter::write(uint8_t data)
{
return write(&data, 1);
}
/////////////////////////////////////////////////////////
size_t AsyncPrinter::write(const uint8_t *data, size_t len)
{
if (_tx_buffer == NULL || !connected())
return 0;
size_t toWrite = 0;
size_t toSend = len;
while (_tx_buffer->room() < toSend)
{
toWrite = _tx_buffer->room();
_tx_buffer->write((const char*)data, toWrite);
while (connected() && !_client->canSend())
delay(0);
if (!connected())
return 0; // or len - toSend;
_sendBuffer();
toSend -= toWrite;
}
_tx_buffer->write((const char*)(data + (len - toSend)), toSend);
while (connected() && !_client->canSend())
delay(0);
if (!connected())
return 0; // or len - toSend;
_sendBuffer();
return len;
}
/////////////////////////////////////////////////////////
bool AsyncPrinter::connected()
{
return (_client != NULL && _client->connected());
}
/////////////////////////////////////////////////////////
void AsyncPrinter::close()
{
if (_client != NULL)
_client->close(true);
}
/////////////////////////////////////////////////////////
size_t AsyncPrinter::_sendBuffer()
{
size_t available = _tx_buffer->available();
if (!connected() || !_client->canSend() || available == 0)
return 0;
size_t sendable = _client->space();
if (sendable < available)
available = sendable;
char *out = new (std::nothrow) char[available];
if (out == NULL)
{
panic(); // Connection should be aborted instead
}
_tx_buffer->read(out, available);
size_t sent = _client->write(out, available);
delete out;
return sent;
}
/////////////////////////////////////////////////////////
void AsyncPrinter::_onData(void *data, size_t len)
{
if (_data_cb)
_data_cb(_data_arg, this, (uint8_t*)data, len);
}
/////////////////////////////////////////////////////////
void AsyncPrinter::_on_close()
{
if (_client != NULL)
{
_client = NULL;
}
if (_tx_buffer != NULL)
{
cbuf *b = _tx_buffer;
_tx_buffer = NULL;
delete b;
}
if (_close_cb)
_close_cb(_close_arg, this);
}
/////////////////////////////////////////////////////////
void AsyncPrinter::_attachCallbacks()
{
_client->onPoll([](void *obj, AsyncClient * c)
{
RPAsyncTCP_UNUSED(c);
((AsyncPrinter*)(obj))->_sendBuffer();
}, this);
_client->onAck([](void *obj, AsyncClient * c, size_t len, uint32_t time)
{
RPAsyncTCP_UNUSED(c);
RPAsyncTCP_UNUSED(len);
RPAsyncTCP_UNUSED(time);
((AsyncPrinter*)(obj))->_sendBuffer();
}, this);
_client->onDisconnect([](void *obj, AsyncClient * c)
{
((AsyncPrinter*)(obj))->_on_close(); delete c;
}, this);
_client->onData([](void *obj, AsyncClient * c, void *data, size_t len)
{
RPAsyncTCP_UNUSED(c);
((AsyncPrinter*)(obj))->_onData(data, len);
}, this);
}
/////////////////////////////////////////////////////////

View File

@@ -0,0 +1,77 @@
/*
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
*/
#ifndef ASYNCPRINTER_H_
#define ASYNCPRINTER_H_
#include "Arduino.h"
#include "RPAsyncTCP.h"
#include "cbuf.h"
class AsyncPrinter;
typedef std::function<void(void*, AsyncPrinter*, uint8_t*, size_t)> ApDataHandler;
typedef std::function<void(void*, AsyncPrinter*)> ApCloseHandler;
/////////////////////////////////////////////////////////
class AsyncPrinter: public Print
{
private:
AsyncClient *_client;
ApDataHandler _data_cb;
void *_data_arg;
ApCloseHandler _close_cb;
void *_close_arg;
cbuf *_tx_buffer;
size_t _tx_buffer_size;
void _onConnect(AsyncClient *c);
public:
AsyncPrinter *next;
AsyncPrinter();
AsyncPrinter(AsyncClient *client, size_t txBufLen = TCP_MSS);
virtual ~AsyncPrinter();
int connect(IPAddress ip, uint16_t port);
int connect(const char *host, uint16_t port);
void onData(ApDataHandler cb, void *arg);
void onClose(ApCloseHandler cb, void *arg);
operator bool();
AsyncPrinter & operator=(const AsyncPrinter &other);
size_t write(uint8_t data);
size_t write(const uint8_t *data, size_t len);
bool connected();
void close();
size_t _sendBuffer();
void _onData(void *data, size_t len);
void _on_close();
void _attachCallbacks();
};
#endif /* ASYNCPRINTER_H_ */

View File

@@ -0,0 +1,127 @@
#ifndef _DEBUG_PRINT_MACROS_H
#define _DEBUG_PRINT_MACROS_H
// Some customizable print macros to suite the debug needs de jour.
// Debug macros
// #include <pgmspace.h>
// https://stackoverflow.com/questions/8487986/file-macro-shows-full-path
// This value is resolved at compile time.
#define _FILENAME_ strrchr("/" __FILE__, '/')
/////////////////////////////////////////////////////////
#if defined(DEBUG_ESP_PORT) && !defined(DEBUG_TIME_STAMP_FMT)
#define DEBUG_TIME_STAMP_FMT "%06u.%03u "
struct _DEBUG_TIME_STAMP
{
unsigned dec;
unsigned whole;
};
inline struct _DEBUG_TIME_STAMP debugTimeStamp()
{
struct _DEBUG_TIME_STAMP st;
unsigned now = millis() % 1000000000;
st.dec = now % 1000;
st.whole = now / 1000;
return st;
}
#endif
/////////////////////////////////////////////////////////
#if defined(DEBUG_ESP_PORT) && !defined(DEBUG_GENERIC)
#define DEBUG_GENERIC( module, format, ... ) \
do { \
struct _DEBUG_TIME_STAMP st = debugTimeStamp(); \
DEBUG_ESP_PORT.printf( DEBUG_TIME_STAMP_FMT module " " format, st.whole, st.dec, ##__VA_ARGS__ ); \
} while(false)
#endif
/////////////////////////////////////////////////////////
#if defined(DEBUG_ESP_PORT) && !defined(DEBUG_GENERIC_P)
#define DEBUG_GENERIC_P( module, format, ... ) \
do { \
struct _DEBUG_TIME_STAMP st = debugTimeStamp(); \
DEBUG_ESP_PORT.printf_P(PSTR( DEBUG_TIME_STAMP_FMT module " " format ), st.whole, st.dec, ##__VA_ARGS__ ); \
} while(false)
#endif
/////////////////////////////////////////////////////////
#if defined(DEBUG_GENERIC) && !defined(ASSERT_GENERIC)
#define ASSERT_GENERIC( a, module ) \
do { \
if ( !(a) ) { \
DEBUG_GENERIC( module, "%s:%s:%u: ASSERT("#a") failed!\n", __FILE__, __func__, __LINE__); \
DEBUG_ESP_PORT.flush(); \
} \
} while(false)
#endif
/////////////////////////////////////////////////////////
#if defined(DEBUG_GENERIC_P) && !defined(ASSERT_GENERIC_P)
#define ASSERT_GENERIC_P( a, module ) \
do { \
if ( !(a) ) { \
DEBUG_GENERIC_P( module, "%s:%s:%u: ASSERT("#a") failed!\n", __FILE__, __func__, __LINE__); \
DEBUG_ESP_PORT.flush(); \
} \
} while(false)
#endif
/////////////////////////////////////////////////////////
#ifndef DEBUG_GENERIC
#define DEBUG_GENERIC(...) do { (void)0;} while(false)
#endif
/////////////////////////////////////////////////////////
#ifndef DEBUG_GENERIC_P
#define DEBUG_GENERIC_P(...) do { (void)0;} while(false)
#endif
/////////////////////////////////////////////////////////
#ifndef ASSERT_GENERIC
#define ASSERT_GENERIC(...) do { (void)0;} while(false)
#endif
/////////////////////////////////////////////////////////
#ifndef ASSERT_GENERIC_P
#define ASSERT_GENERIC_P(...) do { (void)0;} while(false)
#endif
/////////////////////////////////////////////////////////
#ifndef DEBUG_ESP_PRINTF
#define DEBUG_ESP_PRINTF( format, ...) DEBUG_GENERIC_P("[%s]", format, &_FILENAME_[1], ##__VA_ARGS__)
#endif
/////////////////////////////////////////////////////////
#if defined(DEBUG_ESP_ASYNC_TCP) && !defined(ASYNC_TCP_DEBUG)
#define ASYNC_TCP_DEBUG( format, ...) DEBUG_GENERIC_P("[ASYNC_TCP]", format, ##__VA_ARGS__)
#endif
/////////////////////////////////////////////////////////
#ifndef ASYNC_TCP_ASSERT
#define ASYNC_TCP_ASSERT( a ) ASSERT_GENERIC_P( (a), "[ASYNC_TCP]")
#endif
/////////////////////////////////////////////////////////
#if defined(DEBUG_ESP_TCP_SSL) && !defined(TCP_SSL_DEBUG)
#define TCP_SSL_DEBUG( format, ...) DEBUG_GENERIC_P("[TCP_SSL]", format, ##__VA_ARGS__)
#endif
/////////////////////////////////////////////////////////
#endif //_DEBUG_PRINT_MACROS_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,417 @@
#ifndef RPAsyncTCP_H_
#define RPAsyncTCP_H_
#include "Arduino.h"
#include <WiFi.h>
#if ASYNC_TCP_SSL_ENABLED
#undef ASYNC_TCP_SSL_ENABLED
#define ASYNC_TCP_SSL_ENABLED false
#warning ASYNC_TCP_SSL_ENABLED is not ready yet. Disable it
#endif
#define DEBUG_ESP_ASYNC_TCP true
#include "RPAsyncTCP_Debug.h"
#include <async_config.h>
#include "IPAddress.h"
#include <functional>
#include <memory>
extern "C"
{
#include "lwip/ip_addr.h"
#include "lwip/err.h"
#include "lwip/tcp.h"
#include "lwip/pbuf.h"
};
#ifndef RPAsyncTCP_UNUSED
#define RPAsyncTCP_UNUSED(x) (void)(x)
#endif
/////////////////////////////////////////////////
class AsyncClient;
class AsyncServer;
class ACErrorTracker;
#define ASYNC_MAX_ACK_TIME 5000
#define ASYNC_WRITE_FLAG_COPY 0x01 //to allocate new buffer to hold the data while sending
#define ASYNC_WRITE_FLAG_MORE 0x02 //not send PSH flag. More data to be sent before the application should react.
/////////////////////////////////////////////////
struct tcp_pcb;
#if ASYNC_TCP_SSL_ENABLED
struct SSL_;
typedef struct SSL_ SSL;
struct SSL_CTX_;
typedef struct SSL_CTX_ SSL_CTX;
#endif
typedef std::function<void(void*, AsyncClient*)> AcConnectHandler;
typedef std::function<void(void*, AsyncClient*, size_t len, uint32_t time)> AcAckHandler;
typedef std::function<void(void*, AsyncClient*, err_t error)> AcErrorHandler;
typedef std::function<void(void*, AsyncClient*, void *data, size_t len)> AcDataHandler;
typedef std::function<void(void*, AsyncClient*, struct pbuf *pb)> AcPacketHandler;
typedef std::function<void(void*, AsyncClient*, uint32_t time)> AcTimeoutHandler;
typedef std::function<void(void*, size_t event)> AsNotifyHandler;
enum error_events
{
EE_OK = 0,
EE_ABORTED, // Callback or foreground aborted connections
EE_ERROR_CB, // Stack initiated aborts via error Callbacks.
EE_CONNECTED_CB,
EE_RECV_CB,
EE_ACCEPT_CB,
EE_MAX
};
/////////////////////////////////////////////////
// DEBUG_MORE is for gathering more information on which CBs close events are
// occuring and count.
// #define DEBUG_MORE 1
class ACErrorTracker
{
private:
AsyncClient *_client;
err_t _close_error;
int _errored;
#if DEBUG_ESP_ASYNC_TCP
size_t _connectionId;
#endif
#ifdef DEBUG_MORE
AsNotifyHandler _error_event_cb;
void* _error_event_cb_arg;
#endif
protected:
friend class AsyncClient;
friend class AsyncServer;
#ifdef DEBUG_MORE
void onErrorEvent(AsNotifyHandler cb, void *arg);
#endif
#if DEBUG_ESP_ASYNC_TCP
void setConnectionId(size_t id)
{
_connectionId = id;
}
size_t getConnectionId()
{
return _connectionId;
}
#endif
void setCloseError(err_t e);
void setErrored(size_t errorEvent);
err_t getCallbackCloseError();
void clearClient()
{
if (_client) _client = NULL;
}
public:
err_t getCloseError() const
{
return _close_error;
}
bool hasClient() const
{
return (_client != NULL);
}
ACErrorTracker(AsyncClient *c);
~ACErrorTracker() {}
};
/////////////////////////////////////////////////
class AsyncClient
{
protected:
friend class AsyncTCPbuffer;
friend class AsyncServer;
tcp_pcb* _pcb;
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 _pcb_busy;
#if ASYNC_TCP_SSL_ENABLED
bool _pcb_secure;
bool _handshake_done;
#endif
uint32_t _pcb_sent_at;
bool _close_pcb;
bool _ack_pcb;
uint32_t _tx_unacked_len;
uint32_t _tx_acked_len;
uint32_t _tx_unsent_len;
uint32_t _rx_ack_len;
uint32_t _rx_last_packet;
uint32_t _rx_since_timeout;
uint32_t _ack_timeout;
uint16_t _connect_port;
u8_t _recv_pbuf_flags;
std::shared_ptr<ACErrorTracker> _errorTracker;
void _close();
void _connected(std::shared_ptr<ACErrorTracker>& closeAbort, void* pcb, err_t err);
void _error(err_t err);
#if ASYNC_TCP_SSL_ENABLED
void _ssl_error(int8_t err);
#endif
void _poll(std::shared_ptr<ACErrorTracker>& closeAbort, tcp_pcb* pcb);
void _sent(std::shared_ptr<ACErrorTracker>& closeAbort, tcp_pcb* pcb, uint16_t len);
#if LWIP_VERSION_MAJOR == 1
void _dns_found(struct ip_addr *ipaddr);
#else
void _dns_found(ip_addr_t *p);
#endif
static err_t _s_poll(void *arg, struct tcp_pcb *tpcb);
static err_t _s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, err_t err);
static void _s_error(void *arg, err_t err);
static err_t _s_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len);
static err_t _s_connected(void* arg, void* tpcb, err_t err);
#if LWIP_VERSION_MAJOR == 1
static void _s_dns_found(const char *name, struct ip_addr *ipaddr, void *arg);
#else
static void _s_dns_found(const char *name, ip_addr_t *p, void *arg);
#endif
#if ASYNC_TCP_SSL_ENABLED
static void _s_data(void *arg, struct tcp_pcb *tcp, uint8_t * data, size_t len);
static void _s_handshake(void *arg, struct tcp_pcb *tcp, SSL *ssl);
static void _s_ssl_error(void *arg, struct tcp_pcb *tcp, int8_t err);
#endif
std::shared_ptr<ACErrorTracker> getACErrorTracker() const
{
return _errorTracker;
};
void setCloseError(err_t e) const
{
_errorTracker->setCloseError(e);
}
public:
AsyncClient* prev;
AsyncClient* next;
#if ASYNC_TCP_SSL_ENABLED
AsyncClient(tcp_pcb* pcb = 0, SSL_CTX * ssl_ctx = NULL);
#else
AsyncClient(tcp_pcb* pcb = 0);
#endif
~AsyncClient();
AsyncClient & operator=(const AsyncClient &other);
AsyncClient & operator+=(const AsyncClient &other);
bool operator==(const AsyncClient &other) const;
bool operator!=(const AsyncClient &other) const
{
return !(*this == other);
}
#if ASYNC_TCP_SSL_ENABLED
bool connect(IPAddress ip, uint16_t port, bool secure = false);
bool connect(const char* host, uint16_t port, bool secure = false);
#else
bool connect(IPAddress ip, uint16_t port);
bool connect(const char* host, uint16_t port);
#endif
void close(bool now = false);
void stop();
void abort();
bool free();
bool canSend() const;//ack is not pending
size_t space() const;
size_t add(const char* data, size_t size, uint8_t apiflags = 0); //add for sending
bool send();//send all data added with the method above
size_t ack(size_t len); //ack data that you have not acked using the method below
void ackLater()
{
_ack_pcb = false; //will not ack the current packet. Call from onData
}
bool isRecvPush()
{
return !!(_recv_pbuf_flags & PBUF_FLAG_PUSH);
}
#if DEBUG_ESP_ASYNC_TCP
size_t getConnectionId() const
{
return _errorTracker->getConnectionId();
}
#endif
#if ASYNC_TCP_SSL_ENABLED
SSL *getSSL();
#endif
size_t write(const char* data);
size_t write(const char* data, size_t size, uint8_t apiflags = 0); //only when canSend() == true
uint8_t state() const;
bool connecting() const;
bool connected() const;
bool disconnecting() const;
bool disconnected() const;
bool freeable() const;//disconnected or disconnecting
uint16_t getMss() const;
uint32_t getRxTimeout() const;
void setRxTimeout(uint32_t timeout);//no RX data timeout for the connection in seconds
uint32_t getAckTimeout() const;
void setAckTimeout(uint32_t timeout);//no ACK timeout for the last sent packet in milliseconds
void setNoDelay(bool nodelay);
bool getNoDelay() const;
uint32_t getRemoteAddress() const;
uint16_t getRemotePort() const;
uint32_t getLocalAddress() const;
uint16_t getLocalPort() const;
IPAddress remoteIP() const;
uint16_t remotePort() const;
IPAddress localIP() const;
uint16_t localPort() const;
void onConnect(AcConnectHandler cb, void* arg = 0); //on successful connect
void onDisconnect(AcConnectHandler cb, void* arg = 0); //disconnected
void onAck(AcAckHandler cb, void* arg = 0); //ack received
void onError(AcErrorHandler cb, void* arg = 0); //unsuccessful connect or error
void onData(AcDataHandler cb, void* arg = 0); //data received (called if onPacket is not used)
void onPacket(AcPacketHandler cb, void* arg = 0); //data received
void onTimeout(AcTimeoutHandler cb, void* arg = 0); //ack timeout
void onPoll(AcConnectHandler cb, void* arg = 0); //every 125ms when connected
void ackPacket(struct pbuf * pb);
const char * errorToString(err_t error) const;
const char * stateToString() const;
void _recv(std::shared_ptr<ACErrorTracker>& closeAbort, tcp_pcb* pcb, pbuf* pb, err_t err);
err_t getCloseError() const
{
return _errorTracker->getCloseError();
}
};
/////////////////////////////////////////////////
#if ASYNC_TCP_SSL_ENABLED
typedef std::function<int(void* arg, const char *filename, uint8_t **buf)> AcSSlFileHandler;
struct pending_pcb;
#endif
/////////////////////////////////////////////////
class AsyncServer
{
protected:
uint16_t _port;
IPAddress _addr;
bool _noDelay;
tcp_pcb* _pcb;
AcConnectHandler _connect_cb;
void* _connect_cb_arg;
#if ASYNC_TCP_SSL_ENABLED
struct pending_pcb * _pending;
SSL_CTX * _ssl_ctx;
AcSSlFileHandler _file_cb;
void* _file_cb_arg;
#endif
#ifdef DEBUG_MORE
int _event_count[EE_MAX];
#endif
public:
AsyncServer(IPAddress addr, uint16_t port);
AsyncServer(uint16_t port);
~AsyncServer();
void onClient(AcConnectHandler cb, void* arg);
#if ASYNC_TCP_SSL_ENABLED
void onSslFileRequest(AcSSlFileHandler cb, void* arg);
void beginSecure(const char *cert, const char *private_key_file, const char *password);
#endif
void begin();
void end();
void setNoDelay(bool nodelay);
bool getNoDelay() const;
uint8_t status() const;
#ifdef DEBUG_MORE
int getEventCount(size_t ee) const
{
return _event_count[ee];
}
#endif
protected:
err_t _accept(tcp_pcb* newpcb, err_t err);
static err_t _s_accept(void *arg, tcp_pcb* newpcb, err_t err);
#ifdef DEBUG_MORE
int incEventCount(size_t ee)
{
return ++_event_count[ee];
}
#endif
#if ASYNC_TCP_SSL_ENABLED
int _cert(const char *filename, uint8_t **buf);
err_t _poll(tcp_pcb* pcb);
err_t _recv(tcp_pcb *pcb, struct pbuf *pb, err_t err);
static int _s_cert(void *arg, const char *filename, uint8_t **buf);
static err_t _s_poll(void *arg, struct tcp_pcb *tpcb);
static err_t _s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, err_t err);
#endif
};
/////////////////////////////////////////////////
#endif /* RPAsyncTCP_H_ */

View File

@@ -0,0 +1,65 @@
#pragma once
#ifndef RPAsyncTCP_DEBUG_H
#define RPAsyncTCP_DEBUG_H
#ifdef RPAsyncTCP_DEBUG_PORT
#define DBG_PORT_ATCP RPAsyncTCP_DEBUG_PORT
#else
#define DBG_PORT_ATCP Serial
#endif
// Change _RPAsyncTCP_LOGLEVEL_ to set tracing and logging verbosity
// 0: DISABLED: no logging
// 1: ERROR: errors
// 2: WARN: errors and warnings
// 3: INFO: errors, warnings and informational (default)
// 4: DEBUG: errors, warnings, informational and debug
#ifndef _RPAsyncTCP_LOGLEVEL_
#define _RPAsyncTCP_LOGLEVEL_ 1
#endif
/////////////////////////////////////////////////////////
#define ATCP_PRINT_MARK ATCP_PRINT("[ATCP] ")
#define ATCP_PRINT_SP DBG_PORT_ATCP.print(" ")
#define ATCP_PRINT DBG_PORT_ATCP.print
#define ATCP_PRINTLN DBG_PORT_ATCP.println
/////////////////////////////////////////////////////////
#define ATCP_LOGERROR(x) if(_RPAsyncTCP_LOGLEVEL_>0) { ATCP_PRINT_MARK; ATCP_PRINTLN(x); }
#define ATCP_LOGERROR0(x) if(_RPAsyncTCP_LOGLEVEL_>0) { ATCP_PRINT(x); }
#define ATCP_LOGERROR1(x,y) if(_RPAsyncTCP_LOGLEVEL_>0) { ATCP_PRINT_MARK; ATCP_PRINT(x); ATCP_PRINT_SP; ATCP_PRINTLN(y); }
#define ATCP_LOGERROR2(x,y,z) if(_RPAsyncTCP_LOGLEVEL_>0) { ATCP_PRINT_MARK; ATCP_PRINT(x); ATCP_PRINT_SP; ATCP_PRINT(y); ATCP_PRINT_SP; ATCP_PRINTLN(z); }
#define ATCP_LOGERROR3(x,y,z,w) if(_RPAsyncTCP_LOGLEVEL_>0) { ATCP_PRINT_MARK; ATCP_PRINT(x); ATCP_PRINT_SP; ATCP_PRINT(y); ATCP_PRINT_SP; ATCP_PRINT(z); ATCP_PRINT_SP; ATCP_PRINTLN(w); }
/////////////////////////////////////////////////////////
#define ATCP_LOGWARN(x) if(_RPAsyncTCP_LOGLEVEL_>1) { ATCP_PRINT_MARK; ATCP_PRINTLN(x); }
#define ATCP_LOGWARN0(x) if(_RPAsyncTCP_LOGLEVEL_>1) { ATCP_PRINT(x); }
#define ATCP_LOGWARN1(x,y) if(_RPAsyncTCP_LOGLEVEL_>1) { ATCP_PRINT_MARK; ATCP_PRINT(x); ATCP_PRINT_SP; ATCP_PRINTLN(y); }
#define ATCP_LOGWARN2(x,y,z) if(_RPAsyncTCP_LOGLEVEL_>1) { ATCP_PRINT_MARK; ATCP_PRINT(x); ATCP_PRINT_SP; ATCP_PRINT(y); ATCP_PRINT_SP; ATCP_PRINTLN(z); }
#define ATCP_LOGWARN3(x,y,z,w) if(_RPAsyncTCP_LOGLEVEL_>1) { ATCP_PRINT_MARK; ATCP_PRINT(x); ATCP_PRINT_SP; ATCP_PRINT(y); ATCP_PRINT_SP; ATCP_PRINT(z); ATCP_PRINT_SP; ATCP_PRINTLN(w); }
/////////////////////////////////////////////////////////
#define ATCP_LOGINFO(x) if(_RPAsyncTCP_LOGLEVEL_>2) { ATCP_PRINT_MARK; ATCP_PRINTLN(x); }
#define ATCP_LOGINFO0(x) if(_RPAsyncTCP_LOGLEVEL_>2) { ATCP_PRINT(x); }
#define ATCP_LOGINFO1(x,y) if(_RPAsyncTCP_LOGLEVEL_>2) { ATCP_PRINT_MARK; ATCP_PRINT(x); ATCP_PRINT_SP; ATCP_PRINTLN(y); }
#define ATCP_LOGINFO2(x,y,z) if(_RPAsyncTCP_LOGLEVEL_>2) { ATCP_PRINT_MARK; ATCP_PRINT(x); ATCP_PRINT_SP; ATCP_PRINT(y); ATCP_PRINT_SP; ATCP_PRINTLN(z); }
#define ATCP_LOGINFO3(x,y,z,w) if(_RPAsyncTCP_LOGLEVEL_>2) { ATCP_PRINT_MARK; ATCP_PRINT(x); ATCP_PRINT_SP; ATCP_PRINT(y); ATCP_PRINT_SP; ATCP_PRINT(z); ATCP_PRINT_SP; ATCP_PRINTLN(w); }
/////////////////////////////////////////////////////////
#define ATCP_LOGDEBUG(x) if(_RPAsyncTCP_LOGLEVEL_>3) { ATCP_PRINT_MARK; ATCP_PRINTLN(x); }
#define ATCP_LOGDEBUG0(x) if(_RPAsyncTCP_LOGLEVEL_>3) { ATCP_PRINT(x); }
#define ATCP_LOGDEBUG1(x,y) if(_RPAsyncTCP_LOGLEVEL_>3) { ATCP_PRINT_MARK; ATCP_PRINT(x); ATCP_PRINT_SP; ATCP_PRINTLN(y); }
#define ATCP_LOGDEBUG2(x,y,z) if(_RPAsyncTCP_LOGLEVEL_>3) { ATCP_PRINT_MARK; ATCP_PRINT(x); ATCP_PRINT_SP; ATCP_PRINT(y); ATCP_PRINT_SP; ATCP_PRINTLN(z); }
#define ATCP_LOGDEBUG3(x,y,z,w) if(_RPAsyncTCP_LOGLEVEL_>3) { ATCP_PRINT_MARK; ATCP_PRINT(x); ATCP_PRINT_SP; ATCP_PRINT(y); ATCP_PRINT_SP; ATCP_PRINT(z); ATCP_PRINT_SP; ATCP_PRINTLN(w); }
/////////////////////////////////////////////////////////
#endif //RPAsyncTCP_DEBUG_H

View File

@@ -0,0 +1,721 @@
#include <Arduino.h>
#include "debug.h"
#if !defined(_RPAsyncTCP_LOGLEVEL_)
#define _RPAsyncTCP_LOGLEVEL_ 1
#endif
#include "RPAsyncTCP_buffer.h"
/////////////////////////////////////////////////////////
AsyncTCPbuffer::AsyncTCPbuffer(AsyncClient* client)
{
if (client == NULL)
{
ATCP_LOGDEBUG("client is null!!!");
panic();
}
_client = client;
_TXbufferWrite = new (std::nothrow) cbuf(TCP_MSS);
_TXbufferRead = _TXbufferWrite;
_RXbuffer = new (std::nothrow) cbuf(100);
_RXmode = ATB_RX_MODE_FREE;
_rxSize = 0;
_rxTerminator = 0x00;
_rxReadBytesPtr = NULL;
_rxReadStringPtr = NULL;
_cbDisconnect = NULL;
_cbRX = NULL;
_cbDone = NULL;
_attachCallbacks();
}
/////////////////////////////////////////////////////////
AsyncTCPbuffer::~AsyncTCPbuffer()
{
if (_client)
{
_client->close();
}
if (_RXbuffer)
{
delete _RXbuffer;
_RXbuffer = NULL;
}
if (_TXbufferWrite)
{
// will be deleted in _TXbufferRead chain
_TXbufferWrite = NULL;
}
if (_TXbufferRead)
{
cbuf * next = _TXbufferRead->next;
delete _TXbufferRead;
while (next != NULL)
{
_TXbufferRead = next;
next = _TXbufferRead->next;
delete _TXbufferRead;
}
_TXbufferRead = NULL;
}
}
/////////////////////////////////////////////////////////
size_t AsyncTCPbuffer::write(String & data)
{
return write(data.c_str(), data.length());
}
/////////////////////////////////////////////////////////
size_t AsyncTCPbuffer::write(uint8_t data)
{
return write(&data, 1);
}
/////////////////////////////////////////////////////////
size_t AsyncTCPbuffer::write(const char* data)
{
return write((const uint8_t *) data, strlen(data));
}
/////////////////////////////////////////////////////////
size_t AsyncTCPbuffer::write(const char *data, size_t len)
{
return write((const uint8_t *) data, len);
}
/////////////////////////////////////////////////////////
/**
write data in to buffer and try to send the data
@param data
@param len
@return
*/
size_t AsyncTCPbuffer::write(const uint8_t *data, size_t len)
{
if (_TXbufferWrite == NULL || _client == NULL || !_client->connected() || data == NULL || len == 0)
{
return 0;
}
size_t bytesLeft = len;
while (bytesLeft)
{
size_t w = _TXbufferWrite->write((const char*) data, bytesLeft);
bytesLeft -= w;
data += w;
_sendBuffer();
// add new buffer since we have more data
if (_TXbufferWrite->full() && bytesLeft > 0)
{
/*
// to less ram!!!
if(ESP.getFreeHeap() < 4096) {
ATCP_LOGDEBUG("run out of Heap can not send all Data!");
return (len - bytesLeft);
}
*/
cbuf * next = new (std::nothrow) cbuf(TCP_MSS);
if (next == NULL)
{
ATCP_LOGDEBUG("run out of Heap!");
panic();
}
else
{
ATCP_LOGDEBUG("new cbuf");
}
// add new buffer to chain (current cbuf)
_TXbufferWrite->next = next;
// move ptr for next data
_TXbufferWrite = next;
}
}
return len;
}
/////////////////////////////////////////////////////////
/**
wait until all data has send out
*/
void AsyncTCPbuffer::flush()
{
while (!_TXbufferWrite->empty())
{
while (connected() && !_client->canSend())
{
delay(0);
}
if (!connected())
return;
_sendBuffer();
}
}
/////////////////////////////////////////////////////////
void AsyncTCPbuffer::noCallback()
{
_RXmode = ATB_RX_MODE_NONE;
}
/////////////////////////////////////////////////////////
void AsyncTCPbuffer::readStringUntil(char terminator, String * str, AsyncTCPbufferDoneCb done)
{
if (_client == NULL)
{
return;
}
ATCP_LOGDEBUG1("readStringUntil terminator:", terminator);
_RXmode = ATB_RX_MODE_NONE;
_cbDone = done;
_rxReadStringPtr = str;
_rxTerminator = terminator;
_rxSize = 0;
_RXmode = ATB_RX_MODE_TERMINATOR_STRING;
}
/////////////////////////////////////////////////////////
/*
void AsyncTCPbuffer::readBytesUntil(char terminator, char *buffer, size_t length, AsyncTCPbufferDoneCb done)
{
_RXmode = ATB_RX_MODE_NONE;
_cbDone = done;
_rxReadBytesPtr = (uint8_t *) buffer;
_rxTerminator = terminator;
_rxSize = length;
_RXmode = ATB_RX_MODE_TERMINATOR;
_handleRxBuffer(NULL, 0);
}
void AsyncTCPbuffer::readBytesUntil(char terminator, uint8_t *buffer, size_t length, AsyncTCPbufferDoneCb done)
{
readBytesUntil(terminator, (char *) buffer, length, done);
}
*/
/////////////////////////////////////////////////////////
void AsyncTCPbuffer::readBytes(char *buffer, size_t length, AsyncTCPbufferDoneCb done)
{
if (_client == NULL)
{
return;
}
ATCP_LOGDEBUG1("readBytes length:", length);
_RXmode = ATB_RX_MODE_NONE;
_cbDone = done;
_rxReadBytesPtr = (uint8_t *) buffer;
_rxSize = length;
_RXmode = ATB_RX_MODE_READ_BYTES;
}
/////////////////////////////////////////////////////////
void AsyncTCPbuffer::readBytes(uint8_t *buffer, size_t length, AsyncTCPbufferDoneCb done)
{
readBytes((char *) buffer, length, done);
}
/////////////////////////////////////////////////////////
void AsyncTCPbuffer::onData(AsyncTCPbufferDataCb cb)
{
if (_client == NULL)
{
return;
}
ATCP_LOGDEBUG("onData");
_RXmode = ATB_RX_MODE_NONE;
_cbDone = NULL;
_cbRX = cb;
_RXmode = ATB_RX_MODE_FREE;
}
/////////////////////////////////////////////////////////
void AsyncTCPbuffer::onDisconnect(AsyncTCPbufferDisconnectCb cb)
{
_cbDisconnect = cb;
}
/////////////////////////////////////////////////////////
IPAddress AsyncTCPbuffer::remoteIP()
{
if (!_client)
{
return IPAddress(0, 0, 0, 0);
}
return _client->remoteIP();
}
/////////////////////////////////////////////////////////
uint16_t AsyncTCPbuffer::remotePort()
{
if (!_client)
{
return 0;
}
return _client->remotePort();
}
/////////////////////////////////////////////////////////
bool AsyncTCPbuffer::connected()
{
if (!_client)
{
return false;
}
return _client->connected();
}
/////////////////////////////////////////////////////////
void AsyncTCPbuffer::stop()
{
if (!_client)
{
return;
}
_client->stop();
_client = NULL;
if (_cbDone)
{
switch (_RXmode)
{
case ATB_RX_MODE_READ_BYTES:
case ATB_RX_MODE_TERMINATOR:
case ATB_RX_MODE_TERMINATOR_STRING:
_RXmode = ATB_RX_MODE_NONE;
_cbDone(false, NULL);
break;
default:
break;
}
}
_RXmode = ATB_RX_MODE_NONE;
}
/////////////////////////////////////////////////////////
void AsyncTCPbuffer::close()
{
stop();
}
/////////////////////////////////////////////////////////
/**
attachCallbacks to AsyncClient class
*/
void AsyncTCPbuffer::_attachCallbacks()
{
if (!_client)
{
return;
}
ATCP_LOGDEBUG("attachCallbacks");
_client->onPoll([](void *obj, AsyncClient * c)
{
RPAsyncTCP_UNUSED(c);
AsyncTCPbuffer* b = ((AsyncTCPbuffer*)(obj));
if ((b->_TXbufferRead != NULL) && !b->_TXbufferRead->empty())
{
b->_sendBuffer();
}
// if(!b->_RXbuffer->empty()) {
// b->_handleRxBuffer(NULL, 0);
// }
}, this);
_client->onAck([](void *obj, AsyncClient * c, size_t len, uint32_t time)
{
RPAsyncTCP_UNUSED(c);
RPAsyncTCP_UNUSED(len);
RPAsyncTCP_UNUSED(time);
ATCP_LOGDEBUG("onAck");
((AsyncTCPbuffer*)(obj))->_sendBuffer();
}, this);
_client->onDisconnect([](void *obj, AsyncClient * c)
{
ATCP_LOGDEBUG("onDisconnect");
AsyncTCPbuffer* b = ((AsyncTCPbuffer*)(obj));
b->_client = NULL;
bool del = true;
if (b->_cbDisconnect)
{
del = b->_cbDisconnect(b);
}
delete c;
if (del)
{
delete b;
}
}, this);
_client->onData([](void *obj, AsyncClient * c, void *buf, size_t len)
{
RPAsyncTCP_UNUSED(c);
AsyncTCPbuffer* b = ((AsyncTCPbuffer*)(obj));
b->_rxData((uint8_t *)buf, len);
}, this);
_client->onTimeout([](void *obj, AsyncClient * c, uint32_t time)
{
RPAsyncTCP_UNUSED(obj);
RPAsyncTCP_UNUSED(time);
ATCP_LOGDEBUG("onTimeout");
c->close();
}, this);
ATCP_LOGDEBUG("attachCallbacks Done.");
}
/////////////////////////////////////////////////////////
/**
send TX buffer if possible
*/
void AsyncTCPbuffer::_sendBuffer()
{
//ATCP_LOGDEBUG("_sendBuffer...");
size_t available = _TXbufferRead->available();
if (available == 0 || _client == NULL || !_client->connected() || !_client->canSend())
{
return;
}
while (connected() && (_client->space() > 0) && (_TXbufferRead->available() > 0) && _client->canSend())
{
available = _TXbufferRead->available();
if (available > _client->space())
{
available = _client->space();
}
char *out = new (std::nothrow) char[available];
if (out == NULL)
{
ATCP_LOGDEBUG("to less heap, try later.");
return;
}
// read data from buffer
_TXbufferRead->peek(out, available);
// send data
size_t send = _client->write((const char*) out, available);
if (send != available)
{
ATCP_LOGDEBUG3("_sendBuffer write failed send:", send, ", available:", available);
if (!connected())
{
ATCP_LOGDEBUG("incomplete transfer, connection lost.");
}
}
// remove really send data from buffer
_TXbufferRead->remove(send);
// if buffer is empty and there is a other buffer in chain delete the empty one
if (_TXbufferRead->available() == 0 && _TXbufferRead->next != NULL)
{
cbuf * old = _TXbufferRead;
_TXbufferRead = _TXbufferRead->next;
delete old;
ATCP_LOGDEBUG("delete cbuf");
}
delete[] out;
}
}
/////////////////////////////////////////////////////////
/**
called on incoming data
@param buf
@param len
*/
void AsyncTCPbuffer::_rxData(uint8_t *buf, size_t len)
{
if (!_client || !_client->connected())
{
ATCP_LOGDEBUG("not connected!");
return;
}
if (!_RXbuffer)
{
ATCP_LOGDEBUG("_rxData no _RXbuffer!");
return;
}
ATCP_LOGDEBUG3("_rxData len:", len, ", RXmode:", _RXmode);
size_t handled = 0;
if (_RXmode != ATB_RX_MODE_NONE)
{
handled = _handleRxBuffer((uint8_t *) buf, len);
buf += handled;
len -= handled;
// handle as much as possible before using the buffer
if (_RXbuffer->empty())
{
while (_RXmode != ATB_RX_MODE_NONE && handled != 0 && len > 0)
{
handled = _handleRxBuffer(buf, len);
buf += handled;
len -= handled;
}
}
}
if (len > 0)
{
if (_RXbuffer->room() < len)
{
// to less space
ATCP_LOGDEBUG("_rxData buffer full try resize");
_RXbuffer->resizeAdd((len + _RXbuffer->room()));
if (_RXbuffer->room() < len)
{
ATCP_LOGDEBUG1("_rxData buffer to full can only handle:", _RXbuffer->room());
}
}
_RXbuffer->write((const char *) (buf), len);
}
if (!_RXbuffer->empty() && _RXmode != ATB_RX_MODE_NONE)
{
// handle as much as possible data in buffer
handled = _handleRxBuffer(NULL, 0);
while (_RXmode != ATB_RX_MODE_NONE && handled != 0)
{
handled = _handleRxBuffer(NULL, 0);
}
}
// clean up ram
if (_RXbuffer->empty() && _RXbuffer->room() != 100)
{
_RXbuffer->resize(100);
}
}
/////////////////////////////////////////////////////////
size_t AsyncTCPbuffer::_handleRxBuffer(uint8_t *buf, size_t len)
{
if (!_client || !_client->connected() || _RXbuffer == NULL)
{
return 0;
}
ATCP_LOGDEBUG3("_handleRxBuffer len:", len, ", RXmode:", _RXmode);
size_t BufferAvailable = _RXbuffer->available();
size_t r = 0;
if (_RXmode == ATB_RX_MODE_NONE)
{
return 0;
}
else if (_RXmode == ATB_RX_MODE_FREE)
{
if (_cbRX == NULL)
{
return 0;
}
if (BufferAvailable > 0)
{
uint8_t * b = new (std::nothrow) uint8_t[BufferAvailable];
if (b == NULL)
{
panic(); //TODO: What action should this be ?
}
_RXbuffer->peek((char *) b, BufferAvailable);
r = _cbRX(b, BufferAvailable);
_RXbuffer->remove(r);
}
if (r == BufferAvailable && buf && (len > 0))
{
return _cbRX(buf, len);
}
else
{
return 0;
}
}
else if (_RXmode == ATB_RX_MODE_READ_BYTES)
{
if (_rxReadBytesPtr == NULL || _cbDone == NULL)
{
return 0;
}
size_t newReadCount = 0;
if (BufferAvailable)
{
r = _RXbuffer->read((char *) _rxReadBytesPtr, _rxSize);
_rxSize -= r;
_rxReadBytesPtr += r;
}
if (_RXbuffer->empty() && (len > 0) && buf)
{
r = len;
if (r > _rxSize)
{
r = _rxSize;
}
memcpy(_rxReadBytesPtr, buf, r);
_rxReadBytesPtr += r;
_rxSize -= r;
newReadCount += r;
}
if (_rxSize == 0)
{
_RXmode = ATB_RX_MODE_NONE;
_cbDone(true, NULL);
}
// add left over bytes to Buffer
return newReadCount;
}
else if (_RXmode == ATB_RX_MODE_TERMINATOR)
{
// TODO implement read terminator non string
}
else if (_RXmode == ATB_RX_MODE_TERMINATOR_STRING)
{
if (_rxReadStringPtr == NULL || _cbDone == NULL)
{
return 0;
}
// handle Buffer
if (BufferAvailable > 0)
{
while (!_RXbuffer->empty())
{
char c = _RXbuffer->read();
if (c == _rxTerminator || c == 0x00)
{
_RXmode = ATB_RX_MODE_NONE;
_cbDone(true, _rxReadStringPtr);
return 0;
}
else
{
(*_rxReadStringPtr) += c;
}
}
}
if (_RXbuffer->empty() && (len > 0) && buf)
{
size_t newReadCount = 0;
while (newReadCount < len)
{
char c = (char) * buf;
buf++;
newReadCount++;
if (c == _rxTerminator || c == 0x00)
{
_RXmode = ATB_RX_MODE_NONE;
_cbDone(true, _rxReadStringPtr);
return newReadCount;
}
else
{
(*_rxReadStringPtr) += c;
}
}
return newReadCount;
}
}
return 0;
}

View File

@@ -0,0 +1,93 @@
#ifndef RPAsyncTCP_BUFFER_H_
#define RPAsyncTCP_BUFFER_H_
#ifndef DEBUG_ASYNC_TCP
#define DEBUG_ASYNC_TCP(...)
#endif
#include <Arduino.h>
#include <cbuf.h>
#include "RPAsyncTCP.h"
typedef enum
{
ATB_RX_MODE_NONE,
ATB_RX_MODE_FREE,
ATB_RX_MODE_READ_BYTES,
ATB_RX_MODE_TERMINATOR,
ATB_RX_MODE_TERMINATOR_STRING
} atbRxMode_t;
/////////////////////////////////////////////////////////
class AsyncTCPbuffer: public Print
{
public:
typedef std::function<size_t(uint8_t * payload, size_t length)> AsyncTCPbufferDataCb;
typedef std::function<void(bool ok, void * ret)> AsyncTCPbufferDoneCb;
typedef std::function<bool(AsyncTCPbuffer * obj)> AsyncTCPbufferDisconnectCb;
AsyncTCPbuffer(AsyncClient* c);
virtual ~AsyncTCPbuffer();
size_t write(String & data);
size_t write(uint8_t data);
size_t write(const char* data);
size_t write(const char *data, size_t len);
size_t write(const uint8_t *data, size_t len);
void flush();
void noCallback();
void readStringUntil(char terminator, String * str, AsyncTCPbufferDoneCb done);
// TODO implement read terminator non string
//void readBytesUntil(char terminator, char *buffer, size_t length, AsyncTCPbufferDoneCb done);
//void readBytesUntil(char terminator, uint8_t *buffer, size_t length, AsyncTCPbufferDoneCb done);
void readBytes(char *buffer, size_t length, AsyncTCPbufferDoneCb done);
void readBytes(uint8_t *buffer, size_t length, AsyncTCPbufferDoneCb done);
// TODO implement
// void setTimeout(size_t timeout);
void onData(AsyncTCPbufferDataCb cb);
void onDisconnect(AsyncTCPbufferDisconnectCb cb);
IPAddress remoteIP();
uint16_t remotePort();
IPAddress localIP();
uint16_t localPort();
bool connected();
void stop();
void close();
protected:
AsyncClient* _client;
cbuf * _TXbufferRead;
cbuf * _TXbufferWrite;
cbuf * _RXbuffer;
atbRxMode_t _RXmode;
size_t _rxSize;
char _rxTerminator;
uint8_t * _rxReadBytesPtr;
String * _rxReadStringPtr;
AsyncTCPbufferDataCb _cbRX;
AsyncTCPbufferDoneCb _cbDone;
AsyncTCPbufferDisconnectCb _cbDisconnect;
void _attachCallbacks();
void _sendBuffer();
void _on_close();
void _rxData(uint8_t *buf, size_t len);
size_t _handleRxBuffer(uint8_t *buf, size_t len);
};
#endif /* RPAsyncTCP_BUFFER_H_ */

View File

@@ -0,0 +1,615 @@
#if !defined(_RPAsyncTCP_LOGLEVEL_)
#define _RPAsyncTCP_LOGLEVEL_ 1
#endif
#include "Arduino.h"
#include "SyncClient.h"
#include "RPAsyncTCP.h"
#include "cbuf.h"
#define DEBUG_ESP_SYNC_CLIENT
/////////////////////////////////////////////////////////
#if defined(DEBUG_ESP_SYNC_CLIENT) && !defined(SYNC_CLIENT_DEBUG)
#define SYNC_CLIENT_DEBUG( format, ...) DEBUG_GENERIC_P("[SYNC_CLIENT]", format, ##__VA_ARGS__)
#endif
/////////////////////////////////////////////////////////
#ifndef SYNC_CLIENT_DEBUG
#define SYNC_CLIENT_DEBUG(...) do { (void)0;} while(false)
#endif
/////////////////////////////////////////////////////////
/*
Without LWIP_NETIF_TX_SINGLE_PBUF, all tcp_writes default to "no copy".
Referenced data must be preserved and free-ed from the specified tcp_sent()
callback. Alternative, tcp_writes need to use the TCP_WRITE_FLAG_COPY
attribute.
*/
static_assert(LWIP_NETIF_TX_SINGLE_PBUF, "Required, tcp_write() must always copy.");
/////////////////////////////////////////////////////////
SyncClient::SyncClient(size_t txBufLen)
: _client(NULL)
, _tx_buffer(NULL)
, _tx_buffer_size(txBufLen)
, _rx_buffer(NULL)
, _ref(NULL)
{
ref();
}
/////////////////////////////////////////////////////////
SyncClient::SyncClient(AsyncClient *client, size_t txBufLen)
: _client(client)
, _tx_buffer(new (std::nothrow) cbuf(txBufLen))
, _tx_buffer_size(txBufLen)
, _rx_buffer(NULL)
, _ref(NULL)
{
if (ref() > 0 && _client != NULL)
_attachCallbacks();
}
/////////////////////////////////////////////////////////
SyncClient::~SyncClient()
{
if (0 == unref())
_release();
}
/////////////////////////////////////////////////////////
void SyncClient::_release()
{
if (_client != NULL)
{
_client->onData(NULL, NULL);
_client->onAck(NULL, NULL);
_client->onPoll(NULL, NULL);
_client->abort();
_client = NULL;
}
if (_tx_buffer != NULL)
{
cbuf *b = _tx_buffer;
_tx_buffer = NULL;
delete b;
}
while (_rx_buffer != NULL)
{
cbuf *b = _rx_buffer;
_rx_buffer = _rx_buffer->next;
delete b;
}
}
/////////////////////////////////////////////////////////
int SyncClient::ref()
{
if (_ref == NULL)
{
_ref = new (std::nothrow) int;
if (_ref != NULL)
*_ref = 0;
else
return -1;
}
return (++*_ref);
}
/////////////////////////////////////////////////////////
int SyncClient::unref()
{
int count = -1;
if (_ref != NULL)
{
count = --*_ref;
if (0 == count)
{
delete _ref;
_ref = NULL;
}
}
return count;
}
/////////////////////////////////////////////////////////
#if ASYNC_TCP_SSL_ENABLED
int SyncClient::_connect(const IPAddress& ip, uint16_t port, bool secure)
#else
int SyncClient::_connect(const IPAddress& ip, uint16_t port)
#endif
{
if (connected())
return 0;
if (_client != NULL)
delete _client;
_client = new (std::nothrow) AsyncClient();
if (_client == NULL)
return 0;
_client->onConnect([](void *obj, AsyncClient * c)
{
((SyncClient*)(obj))->_onConnect(c);
}, this);
_attachCallbacks_Disconnect();
#if ASYNC_TCP_SSL_ENABLED
if (_client->connect(ip, port, secure))
#else
if (_client->connect(ip, port))
#endif
{
while (_client != NULL && !_client->connected() && !_client->disconnecting())
delay(1);
return connected();
}
return 0;
}
/////////////////////////////////////////////////////////
#if ASYNC_TCP_SSL_ENABLED
int SyncClient::connect(const char *host, uint16_t port, bool secure)
#else
int SyncClient::connect(const char *host, uint16_t port)
#endif
{
if (connected())
return 0;
if (_client != NULL)
delete _client;
_client = new (std::nothrow) AsyncClient();
if (_client == NULL)
return 0;
_client->onConnect([](void *obj, AsyncClient * c)
{
((SyncClient*)(obj))->_onConnect(c);
}, this);
_attachCallbacks_Disconnect();
#if ASYNC_TCP_SSL_ENABLED
if (_client->connect(host, port, secure))
#else
if (_client->connect(host, port))
#endif
{
while (_client != NULL && !_client->connected() && !_client->disconnecting())
delay(1);
return connected();
}
return 0;
}
/////////////////////////////////////////////////////////
//#define SYNCCLIENT_NEW_OPERATOR_EQUAL
#ifdef SYNCCLIENT_NEW_OPERATOR_EQUAL
/*
New behavior for operator=
Allow for the object to be placed on a queue and transfered to a new container
with buffers still in tact. Avoiding receive data drops. Transfers rx and tx
buffers. Supports return by value.
Note, this is optional, the old behavior is the default.
*/
SyncClient & SyncClient::operator=(const SyncClient &other)
{
int *rhsref = other._ref;
++*rhsref; // Just in case the left and right side are the same object with different containers
if (0 == unref())
_release();
_ref = other._ref;
ref();
--*rhsref;
// Why do I not test _tx_buffer for != NULL and free?
// I allow for the lh target container, to be a copy of an active
// connection. Thus we are just reusing the container.
// The above unref() handles releaseing the previous client of the container.
_tx_buffer_size = other._tx_buffer_size;
_tx_buffer = other._tx_buffer;
_client = other._client;
if (_client != NULL && _tx_buffer == NULL)
_tx_buffer = new (std::nothrow) cbuf(_tx_buffer_size);
_rx_buffer = other._rx_buffer;
if (_client)
_attachCallbacks();
return *this;
}
/////////////////////////////////////////////////////////
#else // ! SYNCCLIENT_NEW_OPERATOR_EQUAL
// This is the origianl logic with null checks
SyncClient & SyncClient::operator=(const SyncClient &other)
{
if (_client != NULL)
{
_client->abort();
_client->free();
_client = NULL;
}
_tx_buffer_size = other._tx_buffer_size;
if (_tx_buffer != NULL)
{
cbuf *b = _tx_buffer;
_tx_buffer = NULL;
delete b;
}
while (_rx_buffer != NULL)
{
cbuf *b = _rx_buffer;
_rx_buffer = b->next;
delete b;
}
if (other._client != NULL)
_tx_buffer = new (std::nothrow) cbuf(other._tx_buffer_size);
_client = other._client;
if (_client)
_attachCallbacks();
return *this;
}
#endif
/////////////////////////////////////////////////////////
void SyncClient::setTimeout(uint32_t seconds)
{
if (_client != NULL)
_client->setRxTimeout(seconds);
}
/////////////////////////////////////////////////////////
uint8_t SyncClient::status()
{
if (_client == NULL)
return 0;
return _client->state();
}
/////////////////////////////////////////////////////////
uint8_t SyncClient::connected()
{
return (_client != NULL && _client->connected());
}
/////////////////////////////////////////////////////////
bool SyncClient::stop(unsigned int maxWaitMs)
{
RPAsyncTCP_UNUSED(maxWaitMs);
if (_client != NULL)
_client->close(true);
return true;
}
/////////////////////////////////////////////////////////
size_t SyncClient::_sendBuffer()
{
if (_client == NULL || _tx_buffer == NULL)
return 0;
size_t available = _tx_buffer->available();
if ( !connected() || !_client->canSend() || (available == 0) )
return 0;
size_t sendable = _client->space();
if (sendable < available)
available = sendable;
char *out = new (std::nothrow) char[available];
if (out == NULL)
return 0;
_tx_buffer->read(out, available);
size_t sent = _client->write(out, available);
delete[] out;
return sent;
}
/////////////////////////////////////////////////////////
void SyncClient::_onData(void *data, size_t len)
{
_client->ackLater();
cbuf *b = new (std::nothrow) cbuf(len + 1);
if (b != NULL)
{
b->write((const char *)data, len);
if (_rx_buffer == NULL)
_rx_buffer = b;
else
{
cbuf *p = _rx_buffer;
while (p->next != NULL)
p = p->next;
p->next = b;
}
}
else
{
// We ran out of memory. This fail causes lost receive data.
// The connection should be closed in a manner that conveys something
// bad/abnormal has happened to the connection. Hence, we abort the
// connection to avoid possible data corruption.
// Note, callbacks maybe called.
_client->abort();
}
}
/////////////////////////////////////////////////////////
void SyncClient::_onDisconnect()
{
if (_client != NULL)
{
_client = NULL;
}
if (_tx_buffer != NULL)
{
cbuf *b = _tx_buffer;
_tx_buffer = NULL;
delete b;
}
}
/////////////////////////////////////////////////////////
void SyncClient::_onConnect(AsyncClient *c)
{
_client = c;
if (_tx_buffer != NULL)
{
cbuf *b = _tx_buffer;
_tx_buffer = NULL;
delete b;
}
_tx_buffer = new (std::nothrow) cbuf(_tx_buffer_size);
_attachCallbacks_AfterConnected();
}
/////////////////////////////////////////////////////////
void SyncClient::_attachCallbacks()
{
_attachCallbacks_Disconnect();
_attachCallbacks_AfterConnected();
}
/////////////////////////////////////////////////////////
void SyncClient::_attachCallbacks_AfterConnected()
{
_client->onAck([](void *obj, AsyncClient * c, size_t len, uint32_t time)
{
RPAsyncTCP_UNUSED(c);
RPAsyncTCP_UNUSED(len);
RPAsyncTCP_UNUSED(time);
((SyncClient*)(obj))->_sendBuffer();
}, this);
_client->onData([](void *obj, AsyncClient * c, void *data, size_t len)
{ RPAsyncTCP_UNUSED(c);
((SyncClient*)(obj))->_onData(data, len);
}, this);
_client->onTimeout([](void *obj, AsyncClient * c, uint32_t time)
{
RPAsyncTCP_UNUSED(obj);
RPAsyncTCP_UNUSED(time);
c->close();
}, this);
}
/////////////////////////////////////////////////////////
void SyncClient::_attachCallbacks_Disconnect()
{
_client->onDisconnect([](void *obj, AsyncClient * c)
{
((SyncClient*)(obj))->_onDisconnect();
delete c;
}, this);
}
/////////////////////////////////////////////////////////
size_t SyncClient::write(uint8_t data)
{
return write(&data, 1);
}
/////////////////////////////////////////////////////////
size_t SyncClient::write(const uint8_t *data, size_t len)
{
if (_tx_buffer == NULL || !connected())
{
return 0;
}
size_t toWrite = 0;
size_t toSend = len;
while (_tx_buffer->room() < toSend)
{
toWrite = _tx_buffer->room();
_tx_buffer->write((const char*)data, toWrite);
while (connected() && !_client->canSend())
delay(0);
if (!connected())
return 0;
_sendBuffer();
toSend -= toWrite;
}
_tx_buffer->write((const char*)(data + (len - toSend)), toSend);
if (connected() && _client->canSend())
_sendBuffer();
return len;
}
/////////////////////////////////////////////////////////
int SyncClient::available()
{
if (_rx_buffer == NULL)
return 0;
size_t a = 0;
cbuf *b = _rx_buffer;
while (b != NULL)
{
a += b->available();
b = b->next;
}
return a;
}
/////////////////////////////////////////////////////////
int SyncClient::peek()
{
if (_rx_buffer == NULL)
return -1;
return _rx_buffer->peek();
}
/////////////////////////////////////////////////////////
int SyncClient::read(uint8_t *data, size_t len)
{
if (_rx_buffer == NULL)
return -1;
size_t readSoFar = 0;
while (_rx_buffer != NULL && (len - readSoFar) >= _rx_buffer->available())
{
cbuf *b = _rx_buffer;
_rx_buffer = _rx_buffer->next;
size_t toRead = b->available();
readSoFar += b->read((char*)(data + readSoFar), toRead);
if (connected())
{
_client->ack(b->size() - 1);
}
delete b;
}
if (_rx_buffer != NULL && readSoFar < len)
{
readSoFar += _rx_buffer->read((char*)(data + readSoFar), (len - readSoFar));
}
return readSoFar;
}
/////////////////////////////////////////////////////////
int SyncClient::read()
{
uint8_t res = 0;
if (read(&res, 1) != 1)
return -1;
return res;
}
/////////////////////////////////////////////////////////
bool SyncClient::flush(unsigned int maxWaitMs)
{
RPAsyncTCP_UNUSED(maxWaitMs);
if (_tx_buffer == NULL || !connected())
return false;
if (_tx_buffer->available())
{
while (connected() && !_client->canSend())
delay(0);
if (_client == NULL || _tx_buffer == NULL)
return false;
_sendBuffer();
}
return true;
}
/////////////////////////////////////////////////////////

View File

@@ -0,0 +1,125 @@
#ifndef SYNCCLIENT_H_
#define SYNCCLIENT_H_
#define LWIP_NETIF_TX_SINGLE_PBUF 1
#include "Client.h"
// Needed for Arduino core releases prior to 2.5.0, because of changes
// made to accommodate Arduino core 2.5.0
// CONST was 1st defined in Core 2.5.0 in IPAddress.h
#ifndef CONST
#define CONST
#endif
#include <async_config.h>
class cbuf;
class AsyncClient;
/////////////////////////////////////////////////////////
class SyncClient: public Client
{
private:
AsyncClient *_client;
cbuf *_tx_buffer;
size_t _tx_buffer_size;
cbuf *_rx_buffer;
int *_ref;
size_t _sendBuffer();
void _onData(void *data, size_t len);
void _onConnect(AsyncClient *c);
void _onDisconnect();
void _attachCallbacks();
void _attachCallbacks_Disconnect();
void _attachCallbacks_AfterConnected();
void _release();
public:
SyncClient(size_t txBufLen = TCP_MSS);
SyncClient(AsyncClient *client, size_t txBufLen = TCP_MSS);
virtual ~SyncClient();
int ref();
int unref();
operator bool()
{
return connected();
}
SyncClient & operator=(const SyncClient &other);
#if ASYNC_TCP_SSL_ENABLED
int _connect(const IPAddress& ip, uint16_t port, bool secure);
int connect(CONST IPAddress& ip, uint16_t port, bool secure)
{
return _connect(ip, port, secure);
}
int connect(IPAddress ip, uint16_t port, bool secure)
{
return _connect(reinterpret_cast<const IPAddress&>(ip), port, secure);
}
int connect(const char *host, uint16_t port, bool secure);
int connect(CONST IPAddress& ip, uint16_t port)
{
return _connect(ip, port, false);
}
int connect(IPAddress ip, uint16_t port)
{
return _connect(reinterpret_cast<const IPAddress&>(ip), port, false);
}
int connect(const char *host, uint16_t port)
{
return connect(host, port, false);
}
#else
int _connect(const IPAddress& ip, uint16_t port);
int connect(CONST IPAddress& ip, uint16_t port)
{
return _connect(ip, port);
}
int connect(IPAddress ip, uint16_t port)
{
return _connect(reinterpret_cast<const IPAddress&>(ip), port);
}
int connect(const char *host, uint16_t port);
#endif
void setTimeout(uint32_t seconds);
uint8_t status();
uint8_t connected();
bool stop(unsigned int maxWaitMs);
bool flush(unsigned int maxWaitMs);
void stop()
{
(void)stop(0);
}
void flush()
{
(void)flush(0);
}
size_t write(uint8_t data);
size_t write(const uint8_t *data, size_t len);
int available();
int peek();
int read();
int read(uint8_t *data, size_t len);
};
#endif /* SYNCCLIENT_H_ */

View File

@@ -0,0 +1,12 @@
#ifndef _RP2040W_ASYNC_CONFIG_H_
#define _RP2040W_ASYNC_CONFIG_H_
#ifndef TCP_MSS
// May have been definded as a -DTCP_MSS option on the compile line or not.
// Arduino core 2.3.0 or earlier does not do the -DTCP_MSS option.
// Later versions may set this option with info from board.txt.
// However, Core 2.4.0 and up board.txt does not define TCP_MSS for lwIP v1.4
#define TCP_MSS MBED_CONF_LWIP_TCP_MSS //(1460)
#endif
#endif // _RP2040W_ASYNC_CONFIG_H_

View File

@@ -0,0 +1,109 @@
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2016 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
*
* Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case,
* it is free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef _C_TYPES_H_
#define _C_TYPES_H_
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <sys/cdefs.h>
typedef signed char sint8_t;
typedef signed short sint16_t;
typedef signed long sint32_t;
typedef signed long long sint64_t;
typedef unsigned long long u_int64_t;
typedef float real32_t;
typedef double real64_t;
typedef unsigned char uint8;
typedef unsigned char u8;
typedef signed char sint8;
typedef signed char int8;
typedef signed char s8;
typedef unsigned short uint16;
typedef unsigned short u16;
typedef signed short sint16;
typedef signed short s16;
typedef unsigned int uint32;
typedef unsigned int u_int;
typedef unsigned int u32;
typedef signed int sint32;
typedef signed int s32;
typedef int int32;
typedef signed long long sint64;
typedef unsigned long long uint64;
typedef unsigned long long u64;
typedef float real32;
typedef double real64;
#define __le16 u16
#define LOCAL static
#ifndef NULL
#define NULL (void *)0
#endif /* NULL */
/* probably should not put STATUS here */
typedef enum
{
OK = 0,
FAIL,
PENDING,
BUSY,
CANCEL,
} STATUS;
#define BIT(nr) (1UL << (nr))
#define REG_SET_BIT(_r, _b) (*(volatile uint32_t*)(_r) |= (_b))
#define REG_CLR_BIT(_r, _b) (*(volatile uint32_t*)(_r) &= ~(_b))
#define DMEM_ATTR __attribute__((section(".bss")))
#define SHMEM_ATTR
#ifdef ICACHE_FLASH
#define __ICACHE_STRINGIZE_NX(A) #A
#define __ICACHE_STRINGIZE(A) __ICACHE_STRINGIZE_NX(A)
#define ICACHE_FLASH_ATTR __attribute__((section("\".irom0.text." __FILE__ "." __ICACHE_STRINGIZE(__LINE__) "." __ICACHE_STRINGIZE(__COUNTER__) "\"")))
#define ICACHE_RAM_ATTR __attribute__((section("\".iram.text." __FILE__ "." __ICACHE_STRINGIZE(__LINE__) "." __ICACHE_STRINGIZE(__COUNTER__) "\"")))
#else
#define ICACHE_FLASH_ATTR
#define ICACHE_RAM_ATTR
#endif /* ICACHE_FLASH */
// counterpart https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp8266-compat.h
#define IRAM_ATTR ICACHE_RAM_ATTR
#define STORE_ATTR __attribute__((aligned(4)))
#ifndef __cplusplus
#define BOOL bool
#define TRUE true
#define FALSE false
#endif /* !__cplusplus */
#endif /* _C_TYPES_H_ */

View File

@@ -0,0 +1,221 @@
/*
cbuf.cpp - Circular buffer implementation
Copyright (c) 2014 Ivan Grokhotkov. 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
*/
#include "cbuf.h"
#include "c_types.h"
cbuf::cbuf(size_t size) :
next(NULL), _size(size), _buf(new char[size]), _bufend(_buf + size), _begin(_buf), _end(_begin)
{
}
cbuf::~cbuf()
{
delete[] _buf;
}
size_t cbuf::resizeAdd(size_t addSize)
{
return resize(_size + addSize);
}
size_t cbuf::resize(size_t newSize)
{
size_t bytes_available = available();
// not lose any data
// if data can be lost use remove or flush before resize
if ((newSize <= bytes_available) || (newSize == _size))
{
return _size;
}
char *newbuf = new char[newSize];
char *oldbuf = _buf;
if (!newbuf)
{
return _size;
}
if (_buf)
{
read(newbuf, bytes_available);
memset((newbuf + bytes_available), 0x00, (newSize - bytes_available));
}
_begin = newbuf;
_end = newbuf + bytes_available;
_bufend = newbuf + newSize;
_size = newSize;
_buf = newbuf;
delete[] oldbuf;
return _size;
}
size_t ICACHE_RAM_ATTR cbuf::available() const
{
if (_end >= _begin)
{
return _end - _begin;
}
return _size - (_begin - _end);
}
size_t cbuf::size()
{
return _size;
}
size_t cbuf::room() const
{
if (_end >= _begin)
{
return _size - (_end - _begin) - 1;
}
return _begin - _end - 1;
}
int cbuf::peek()
{
if (empty())
return -1;
return static_cast<int>(*_begin);
}
size_t cbuf::peek(char *dst, size_t size)
{
size_t bytes_available = available();
size_t size_to_read = (size < bytes_available) ? size : bytes_available;
size_t size_read = size_to_read;
char * begin = _begin;
if (_end < _begin && size_to_read > (size_t) (_bufend - _begin))
{
size_t top_size = _bufend - _begin;
memcpy(dst, _begin, top_size);
begin = _buf;
size_to_read -= top_size;
dst += top_size;
}
memcpy(dst, begin, size_to_read);
return size_read;
}
int ICACHE_RAM_ATTR cbuf::read()
{
if (empty())
return -1;
char result = *_begin;
_begin = wrap_if_bufend(_begin + 1);
return static_cast<int>(result);
}
size_t cbuf::read(char* dst, size_t size)
{
size_t bytes_available = available();
size_t size_to_read = (size < bytes_available) ? size : bytes_available;
size_t size_read = size_to_read;
if (_end < _begin && size_to_read > (size_t) (_bufend - _begin))
{
size_t top_size = _bufend - _begin;
memcpy(dst, _begin, top_size);
_begin = _buf;
size_to_read -= top_size;
dst += top_size;
}
memcpy(dst, _begin, size_to_read);
_begin = wrap_if_bufend(_begin + size_to_read);
return size_read;
}
size_t ICACHE_RAM_ATTR cbuf::write(char c)
{
if (full())
return 0;
*_end = c;
_end = wrap_if_bufend(_end + 1);
return 1;
}
size_t cbuf::write(const char* src, size_t size)
{
size_t bytes_available = room();
size_t size_to_write = (size < bytes_available) ? size : bytes_available;
size_t size_written = size_to_write;
if (_end >= _begin && size_to_write > (size_t) (_bufend - _end))
{
size_t top_size = _bufend - _end;
memcpy(_end, src, top_size);
_end = _buf;
size_to_write -= top_size;
src += top_size;
}
memcpy(_end, src, size_to_write);
_end = wrap_if_bufend(_end + size_to_write);
return size_written;
}
void cbuf::flush()
{
_begin = _buf;
_end = _buf;
}
size_t cbuf::remove(size_t size)
{
size_t bytes_available = available();
if (size >= bytes_available)
{
flush();
return 0;
}
size_t size_to_remove = (size < bytes_available) ? size : bytes_available;
if (_end < _begin && size_to_remove > (size_t) (_bufend - _begin))
{
size_t top_size = _bufend - _begin;
_begin = _buf;
size_to_remove -= top_size;
}
_begin = wrap_if_bufend(_begin + size_to_remove);
return available();
}

View File

@@ -0,0 +1,78 @@
/*
cbuf.h - Circular buffer implementation
Copyright (c) 2014 Ivan Grokhotkov. 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
*/
#ifndef __cbuf_h
#define __cbuf_h
#include <stddef.h>
#include <stdint.h>
#include <string.h>
class cbuf
{
public:
cbuf(size_t size);
~cbuf();
size_t resizeAdd(size_t addSize);
size_t resize(size_t newSize);
size_t available() const;
size_t size();
size_t room() const;
inline bool empty() const
{
return _begin == _end;
}
inline bool full() const
{
return wrap_if_bufend(_end + 1) == _begin;
}
int peek();
size_t peek(char *dst, size_t size);
int read();
size_t read(char* dst, size_t size);
size_t write(char c);
size_t write(const char* src, size_t size);
void flush();
size_t remove(size_t size);
cbuf *next;
private:
inline char* wrap_if_bufend(char* ptr) const
{
return (ptr == _bufend) ? _buf : ptr;
}
size_t _size;
char* _buf;
const char* _bufend;
char* _begin;
char* _end;
};
#endif//__cbuf_h

View File

@@ -0,0 +1,4 @@
void panic()
{
}

View File

@@ -0,0 +1,6 @@
#ifndef DEBUG_H_
#define DEBUG_H_
void panic();
#endif

View File

@@ -0,0 +1,802 @@
#if !defined(_RPAsyncTCP_LOGLEVEL_)
#define _RPAsyncTCP_LOGLEVEL_ 1
#endif
#include <async_config.h>
#if ASYNC_TCP_SSL_ENABLED
#include "lwip/opt.h"
#include "lwip/tcp.h"
#include "lwip/inet.h"
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <stdbool.h>
#include <tcp_axtls.h>
uint8_t * default_private_key = NULL;
uint16_t default_private_key_len = 0;
uint8_t * default_certificate = NULL;
uint16_t default_certificate_len = 0;
static uint8_t _tcp_ssl_has_client = 0;
/////////////////////////////////////////////////////////
SSL_CTX * tcp_ssl_new_server_ctx(const char *cert, const char *private_key_file, const char *password)
{
uint32_t options = SSL_CONNECT_IN_PARTS;
SSL_CTX *ssl_ctx;
if (private_key_file)
{
options |= SSL_NO_DEFAULT_KEY;
}
if ((ssl_ctx = ssl_ctx_new(options, SSL_DEFAULT_SVR_SESS)) == NULL)
{
TCP_SSL_DEBUG("tcp_ssl_new_server_ctx: failed to allocate context\n");
return NULL;
}
if (private_key_file)
{
int obj_type = SSL_OBJ_RSA_KEY;
if (strstr(private_key_file, ".p8"))
obj_type = SSL_OBJ_PKCS8;
else if (strstr(private_key_file, ".p12"))
obj_type = SSL_OBJ_PKCS12;
if (ssl_obj_load(ssl_ctx, obj_type, private_key_file, password))
{
TCP_SSL_DEBUG("tcp_ssl_new_server_ctx: load private key '%s' failed\n", private_key_file);
return NULL;
}
}
if (cert)
{
if (ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, cert, NULL))
{
TCP_SSL_DEBUG("tcp_ssl_new_server_ctx: load certificate '%s' failed\n", cert);
return NULL;
}
}
return ssl_ctx;
}
/////////////////////////////////////////////////////////
struct tcp_ssl_pcb
{
struct tcp_pcb *tcp;
int fd;
SSL_CTX* ssl_ctx;
SSL *ssl;
uint8_t type;
int handshake;
void * arg;
tcp_ssl_data_cb_t on_data;
tcp_ssl_handshake_cb_t on_handshake;
tcp_ssl_error_cb_t on_error;
int last_wr;
struct pbuf *tcp_pbuf;
int pbuf_offset;
struct tcp_ssl_pcb * next;
};
typedef struct tcp_ssl_pcb tcp_ssl_t;
static tcp_ssl_t * tcp_ssl_array = NULL;
static int tcp_ssl_next_fd = 0;
/////////////////////////////////////////////////////////
uint8_t tcp_ssl_has_client()
{
return _tcp_ssl_has_client;
}
/////////////////////////////////////////////////////////
tcp_ssl_t * tcp_ssl_new(struct tcp_pcb *tcp)
{
if (tcp_ssl_next_fd < 0)
{
tcp_ssl_next_fd = 0;//overflow
}
tcp_ssl_t * new_item = (tcp_ssl_t*)malloc(sizeof(tcp_ssl_t));
if (!new_item)
{
TCP_SSL_DEBUG("tcp_ssl_new: failed to allocate tcp_ssl\n");
return NULL;
}
new_item->tcp = tcp;
new_item->handshake = SSL_NOT_OK;
new_item->arg = NULL;
new_item->on_data = NULL;
new_item->on_handshake = NULL;
new_item->on_error = NULL;
new_item->tcp_pbuf = NULL;
new_item->pbuf_offset = 0;
new_item->next = NULL;
new_item->ssl_ctx = NULL;
new_item->ssl = NULL;
new_item->type = TCP_SSL_TYPE_CLIENT;
new_item->fd = tcp_ssl_next_fd++;
if (tcp_ssl_array == NULL)
{
tcp_ssl_array = new_item;
}
else
{
tcp_ssl_t * item = tcp_ssl_array;
while (item->next != NULL)
item = item->next;
item->next = new_item;
}
TCP_SSL_DEBUG("tcp_ssl_new: %d\n", new_item->fd);
return new_item;
}
/////////////////////////////////////////////////////////
tcp_ssl_t* tcp_ssl_get(struct tcp_pcb *tcp)
{
if (tcp == NULL)
{
return NULL;
}
tcp_ssl_t * item = tcp_ssl_array;
while (item && item->tcp != tcp)
{
item = item->next;
}
return item;
}
/////////////////////////////////////////////////////////
int tcp_ssl_new_client(struct tcp_pcb *tcp)
{
SSL_CTX* ssl_ctx;
tcp_ssl_t * tcp_ssl;
if (tcp == NULL)
{
return -1;
}
if (tcp_ssl_get(tcp) != NULL)
{
TCP_SSL_DEBUG("tcp_ssl_new_client: tcp_ssl already exists\n");
return -1;
}
ssl_ctx = ssl_ctx_new(SSL_CONNECT_IN_PARTS | SSL_SERVER_VERIFY_LATER, 1);
if (ssl_ctx == NULL)
{
TCP_SSL_DEBUG("tcp_ssl_new_client: failed to allocate ssl context\n");
return -1;
}
tcp_ssl = tcp_ssl_new(tcp);
if (tcp_ssl == NULL)
{
ssl_ctx_free(ssl_ctx);
return -1;
}
tcp_ssl->ssl_ctx = ssl_ctx;
tcp_ssl->ssl = ssl_client_new(ssl_ctx, tcp_ssl->fd, NULL, 0, NULL);
if (tcp_ssl->ssl == NULL)
{
TCP_SSL_DEBUG("tcp_ssl_new_client: failed to allocate ssl\n");
tcp_ssl_free(tcp);
return -1;
}
return tcp_ssl->fd;
}
/////////////////////////////////////////////////////////
int tcp_ssl_new_server(struct tcp_pcb *tcp, SSL_CTX* ssl_ctx)
{
tcp_ssl_t * tcp_ssl;
if (tcp == NULL)
{
return -1;
}
if (ssl_ctx == NULL)
{
return -1;
}
if (tcp_ssl_get(tcp) != NULL)
{
TCP_SSL_DEBUG("tcp_ssl_new_server: tcp_ssl already exists\n");
return -1;
}
tcp_ssl = tcp_ssl_new(tcp);
if (tcp_ssl == NULL)
{
return -1;
}
tcp_ssl->type = TCP_SSL_TYPE_SERVER;
tcp_ssl->ssl_ctx = ssl_ctx;
_tcp_ssl_has_client = 1;
tcp_ssl->ssl = ssl_server_new(ssl_ctx, tcp_ssl->fd);
if (tcp_ssl->ssl == NULL)
{
TCP_SSL_DEBUG("tcp_ssl_new_server: failed to allocate ssl\n");
tcp_ssl_free(tcp);
return -1;
}
return tcp_ssl->fd;
}
/////////////////////////////////////////////////////////
int tcp_ssl_free(struct tcp_pcb *tcp)
{
if (tcp == NULL)
{
return -1;
}
tcp_ssl_t * item = tcp_ssl_array;
if (item->tcp == tcp)
{
tcp_ssl_array = tcp_ssl_array->next;
if (item->tcp_pbuf != NULL)
{
pbuf_free(item->tcp_pbuf);
}
TCP_SSL_DEBUG("tcp_ssl_free: %d\n", item->fd);
if (item->ssl)
ssl_free(item->ssl);
if (item->type == TCP_SSL_TYPE_CLIENT && item->ssl_ctx)
ssl_ctx_free(item->ssl_ctx);
if (item->type == TCP_SSL_TYPE_SERVER)
_tcp_ssl_has_client = 0;
free(item);
return 0;
}
while (item->next && item->next->tcp != tcp)
item = item->next;
if (item->next == NULL)
{
return ERR_TCP_SSL_INVALID_CLIENTFD_DATA;//item not found
}
tcp_ssl_t * i = item->next;
item->next = i->next;
if (i->tcp_pbuf != NULL)
{
pbuf_free(i->tcp_pbuf);
}
TCP_SSL_DEBUG("tcp_ssl_free: %d\n", i->fd);
if (i->ssl)
ssl_free(i->ssl);
if (i->type == TCP_SSL_TYPE_CLIENT && i->ssl_ctx)
ssl_ctx_free(i->ssl_ctx);
if (i->type == TCP_SSL_TYPE_SERVER)
_tcp_ssl_has_client = 0;
free(i);
return 0;
}
/////////////////////////////////////////////////////////
#ifdef AXTLS_2_0_0_SNDBUF
int tcp_ssl_sndbuf(struct tcp_pcb *tcp)
{
int expected;
int available;
int result = -1;
if (tcp == NULL)
{
return result;
}
tcp_ssl_t * tcp_ssl = tcp_ssl_get(tcp);
if (!tcp_ssl)
{
TCP_SSL_DEBUG("tcp_ssl_sndbuf: tcp_ssl is NULL\n");
return result;
}
available = tcp_sndbuf(tcp);
if (!available)
{
TCP_SSL_DEBUG("tcp_ssl_sndbuf: tcp_sndbuf is zero\n");
return 0;
}
result = available;
while ((expected = ssl_calculate_write_length(tcp_ssl->ssl, result)) > available)
{
result -= (expected - available) + 4;
}
if (expected > 0)
{
//TCP_SSL_DEBUG("tcp_ssl_sndbuf: tcp_sndbuf is %d from %d\n", result, available);
return result;
}
return 0;
}
#endif // AXTLS_2_0_0_SNDBUF
/////////////////////////////////////////////////////////
int tcp_ssl_write(struct tcp_pcb *tcp, uint8_t *data, size_t len)
{
if (tcp == NULL)
{
return -1;
}
tcp_ssl_t * tcp_ssl = tcp_ssl_get(tcp);
if (!tcp_ssl)
{
TCP_SSL_DEBUG("tcp_ssl_write: tcp_ssl is NULL\n");
return 0;
}
tcp_ssl->last_wr = 0;
#ifdef AXTLS_2_0_0_SNDBUF
int expected_len = ssl_calculate_write_length(tcp_ssl->ssl, len);
int available_len = tcp_sndbuf(tcp);
if (expected_len < 0 || expected_len > available_len)
{
TCP_SSL_DEBUG("tcp_ssl_write: data will not fit! %u < %d(%u)\r\n", available_len, expected_len, len);
return -1;
}
#endif // AXTLS_2_0_0_SNDBUF
int rc = ssl_write(tcp_ssl->ssl, data, len);
//TCP_SSL_DEBUG("tcp_ssl_write: %u -> %d (%d)\r\n", len, tcp_ssl->last_wr, rc);
if (rc < 0)
{
if (rc != SSL_CLOSE_NOTIFY)
{
TCP_SSL_DEBUG("tcp_ssl_write error: %d\r\n", rc);
}
return rc;
}
return tcp_ssl->last_wr;
}
/////////////////////////////////////////////////////////
/**
Reads data from the SSL over TCP stream. Returns decrypted data.
@param tcp_pcb *tcp - pointer to the raw tcp object
@param pbuf *p - pointer to the buffer with the TCP packet data
@return int
0 - when everything is fine but there are no symbols to process yet
< 0 - when there is an error
> 0 - the length of the clear text characters that were read
*/
int tcp_ssl_read(struct tcp_pcb *tcp, struct pbuf *p)
{
if (tcp == NULL)
{
return -1;
}
tcp_ssl_t* fd_data = NULL;
int read_bytes = 0;
int total_bytes = 0;
uint8_t *read_buf;
fd_data = tcp_ssl_get(tcp);
if (fd_data == NULL)
{
TCP_SSL_DEBUG("tcp_ssl_read: tcp_ssl is NULL\n");
return ERR_TCP_SSL_INVALID_CLIENTFD_DATA;
}
if (p == NULL)
{
TCP_SSL_DEBUG("tcp_ssl_read:p == NULL\n");
return ERR_TCP_SSL_INVALID_DATA;
}
//TCP_SSL_DEBUG("READY TO READ SOME DATA\n");
fd_data->tcp_pbuf = p;
fd_data->pbuf_offset = 0;
do
{
read_bytes = ssl_read(fd_data->ssl, &read_buf);
//TCP_SSL_DEBUG("tcp_ssl_ssl_read: %d\n", read_bytes);
if (read_bytes < SSL_OK)
{
if (read_bytes != SSL_CLOSE_NOTIFY)
{
TCP_SSL_DEBUG("tcp_ssl_read: read error: %d\n", read_bytes);
}
total_bytes = read_bytes;
break;
}
else if (read_bytes > 0)
{
if (fd_data->on_data)
{
fd_data->on_data(fd_data->arg, tcp, read_buf, read_bytes);
}
total_bytes += read_bytes;
}
else
{
if (fd_data->handshake != SSL_OK)
{
fd_data->handshake = ssl_handshake_status(fd_data->ssl);
if (fd_data->handshake == SSL_OK)
{
//TCP_SSL_DEBUG("tcp_ssl_read: handshake OK\n");
if (fd_data->on_handshake)
fd_data->on_handshake(fd_data->arg, fd_data->tcp, fd_data->ssl);
}
else if (fd_data->handshake != SSL_NOT_OK)
{
TCP_SSL_DEBUG("tcp_ssl_read: handshake error: %d\n", fd_data->handshake);
if (fd_data->on_error)
fd_data->on_error(fd_data->arg, fd_data->tcp, fd_data->handshake);
return fd_data->handshake;
}
}
}
} while (p->tot_len - fd_data->pbuf_offset > 0);
tcp_recved(tcp, p->tot_len);
fd_data->tcp_pbuf = NULL;
pbuf_free(p);
return total_bytes;
}
/////////////////////////////////////////////////////////
SSL * tcp_ssl_get_ssl(struct tcp_pcb *tcp)
{
tcp_ssl_t * tcp_ssl = tcp_ssl_get(tcp);
if (tcp_ssl)
{
return tcp_ssl->ssl;
}
return NULL;
}
/////////////////////////////////////////////////////////
bool tcp_ssl_has(struct tcp_pcb *tcp)
{
return tcp_ssl_get(tcp) != NULL;
}
/////////////////////////////////////////////////////////
int tcp_ssl_is_server(struct tcp_pcb *tcp)
{
tcp_ssl_t * tcp_ssl = tcp_ssl_get(tcp);
if (tcp_ssl)
{
return tcp_ssl->type;
}
return -1;
}
/////////////////////////////////////////////////////////
void tcp_ssl_arg(struct tcp_pcb *tcp, void * arg)
{
tcp_ssl_t * item = tcp_ssl_get(tcp);
if (item)
{
item->arg = arg;
}
}
/////////////////////////////////////////////////////////
void tcp_ssl_data(struct tcp_pcb *tcp, tcp_ssl_data_cb_t arg)
{
tcp_ssl_t * item = tcp_ssl_get(tcp);
if (item)
{
item->on_data = arg;
}
}
/////////////////////////////////////////////////////////
void tcp_ssl_handshake(struct tcp_pcb *tcp, tcp_ssl_handshake_cb_t arg)
{
tcp_ssl_t * item = tcp_ssl_get(tcp);
if (item)
{
item->on_handshake = arg;
}
}
/////////////////////////////////////////////////////////
void tcp_ssl_err(struct tcp_pcb *tcp, tcp_ssl_error_cb_t arg)
{
tcp_ssl_t * item = tcp_ssl_get(tcp);
if (item)
{
item->on_error = arg;
}
}
/////////////////////////////////////////////////////////
static tcp_ssl_file_cb_t _tcp_ssl_file_cb = NULL;
static void * _tcp_ssl_file_arg = NULL;
/////////////////////////////////////////////////////////
void tcp_ssl_file(tcp_ssl_file_cb_t cb, void * arg)
{
_tcp_ssl_file_cb = cb;
_tcp_ssl_file_arg = arg;
}
/////////////////////////////////////////////////////////
int ax_get_file(const char *filename, uint8_t **buf)
{
//TCP_SSL_DEBUG("ax_get_file: %s\n", filename);
if (_tcp_ssl_file_cb)
{
return _tcp_ssl_file_cb(_tcp_ssl_file_arg, filename, buf);
}
*buf = 0;
return 0;
}
/////////////////////////////////////////////////////////
tcp_ssl_t* tcp_ssl_get_by_fd(int fd)
{
tcp_ssl_t * item = tcp_ssl_array;
while (item && item->fd != fd)
{
item = item->next;
}
return item;
}
/////////////////////////////////////////////////////////
/*
The LWIP tcp raw version of the SOCKET_WRITE(A, B, C)
*/
int ax_port_write(int fd, uint8_t *data, uint16_t len)
{
tcp_ssl_t *fd_data = NULL;
int tcp_len = 0;
err_t err = ERR_OK;
//TCP_SSL_DEBUG("ax_port_write: %d, %d\n", fd, len);
fd_data = tcp_ssl_get_by_fd(fd);
if (fd_data == NULL)
{
//TCP_SSL_DEBUG("ax_port_write: tcp_ssl[%d] is NULL\n", fd);
return ERR_MEM;
}
if (data == NULL || len == 0)
{
return 0;
}
if (tcp_sndbuf(fd_data->tcp) < len)
{
tcp_len = tcp_sndbuf(fd_data->tcp);
if (tcp_len == 0)
{
TCP_SSL_DEBUG("ax_port_write: tcp_sndbuf is zero: %d\n", len);
return ERR_MEM;
}
}
else
{
tcp_len = len;
}
if (tcp_len > 2 * fd_data->tcp->mss)
{
tcp_len = 2 * fd_data->tcp->mss;
}
err = tcp_write(fd_data->tcp, data, tcp_len, TCP_WRITE_FLAG_COPY);
if (err < ERR_OK)
{
if (err == ERR_MEM)
{
TCP_SSL_DEBUG("ax_port_write: No memory %d (%d)\n", tcp_len, len);
return err;
}
TCP_SSL_DEBUG("ax_port_write: tcp_write error: %d\n", err);
return err;
}
else if (err == ERR_OK)
{
//TCP_SSL_DEBUG("ax_port_write: tcp_output: %d / %d\n", tcp_len, len);
err = tcp_output(fd_data->tcp);
if (err != ERR_OK)
{
TCP_SSL_DEBUG("ax_port_write: tcp_output err: %d\n", err);
return err;
}
}
fd_data->last_wr += tcp_len;
return tcp_len;
}
/////////////////////////////////////////////////////////
/*
The LWIP tcp raw version of the SOCKET_READ(A, B, C)
*/
int ax_port_read(int fd, uint8_t *data, int len)
{
tcp_ssl_t *fd_data = NULL;
uint8_t *read_buf = NULL;
uint8_t *pread_buf = NULL;
u16_t recv_len = 0;
//TCP_SSL_DEBUG("ax_port_read: %d, %d\n", fd, len);
fd_data = tcp_ssl_get_by_fd(fd);
if (fd_data == NULL)
{
TCP_SSL_DEBUG("ax_port_read: tcp_ssl[%d] is NULL\n", fd);
return ERR_TCP_SSL_INVALID_CLIENTFD_DATA;
}
if (fd_data->tcp_pbuf == NULL || fd_data->tcp_pbuf->tot_len == 0)
{
return 0;
}
read_buf = (uint8_t*) calloc(fd_data->tcp_pbuf->len + 1, sizeof(uint8_t));
pread_buf = read_buf;
if (pread_buf != NULL)
{
recv_len = pbuf_copy_partial(fd_data->tcp_pbuf, read_buf, len, fd_data->pbuf_offset);
fd_data->pbuf_offset += recv_len;
}
if (recv_len != 0)
{
memcpy(data, read_buf, recv_len);
}
if (len < recv_len)
{
TCP_SSL_DEBUG("ax_port_read: got %d bytes more than expected\n", recv_len - len);
}
free(pread_buf);
pread_buf = NULL;
return recv_len;
}
/////////////////////////////////////////////////////////
void ax_wdt_feed() {}
/////////////////////////////////////////////////////////
#endif // ASYNC_TCP_SSL_ENABLED

View File

@@ -0,0 +1,73 @@
#ifndef LWIPR_COMPAT_H
#define LWIPR_COMPAT_H
#include <async_config.h>
#if ASYNC_TCP_SSL_ENABLED
#include "lwipopts.h"
/*
All those functions will run only if LWIP tcp raw mode is used
*/
#if LWIP_RAW==1
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include "include/ssl.h"
#define ERR_TCP_SSL_INVALID_SSL -101
#define ERR_TCP_SSL_INVALID_TCP -102
#define ERR_TCP_SSL_INVALID_CLIENTFD -103
#define ERR_TCP_SSL_INVALID_CLIENTFD_DATA -104
#define ERR_TCP_SSL_INVALID_DATA -105
#define TCP_SSL_TYPE_CLIENT 0
#define TCP_SSL_TYPE_SERVER 1
#define tcp_ssl_ssl_write(A, B, C) tcp_ssl_write(A, B, C)
#define tcp_ssl_ssl_read(A, B) tcp_ssl_read(A, B)
typedef void (* tcp_ssl_data_cb_t)(void *arg, struct tcp_pcb *tcp, uint8_t * data, size_t len);
typedef void (* tcp_ssl_handshake_cb_t)(void *arg, struct tcp_pcb *tcp, SSL *ssl);
typedef void (* tcp_ssl_error_cb_t)(void *arg, struct tcp_pcb *tcp, int8_t error);
typedef int (* tcp_ssl_file_cb_t)(void *arg, const char *filename, uint8_t **buf);
uint8_t tcp_ssl_has_client();
int tcp_ssl_new_client(struct tcp_pcb *tcp);
SSL_CTX * tcp_ssl_new_server_ctx(const char *cert, const char *private_key_file, const char *password);
int tcp_ssl_new_server(struct tcp_pcb *tcp, SSL_CTX* ssl_ctx);
int tcp_ssl_is_server(struct tcp_pcb *tcp);
int tcp_ssl_free(struct tcp_pcb *tcp);
int tcp_ssl_read(struct tcp_pcb *tcp, struct pbuf *p);
#ifdef AXTLS_2_0_0_SNDBUF
int tcp_ssl_sndbuf(struct tcp_pcb *tcp);
#endif
int tcp_ssl_write(struct tcp_pcb *tcp, uint8_t *data, size_t len);
void tcp_ssl_file(tcp_ssl_file_cb_t cb, void * arg);
void tcp_ssl_arg(struct tcp_pcb *tcp, void * arg);
void tcp_ssl_data(struct tcp_pcb *tcp, tcp_ssl_data_cb_t arg);
void tcp_ssl_handshake(struct tcp_pcb *tcp, tcp_ssl_handshake_cb_t arg);
void tcp_ssl_err(struct tcp_pcb *tcp, tcp_ssl_error_cb_t arg);
SSL * tcp_ssl_get_ssl(struct tcp_pcb *tcp);
bool tcp_ssl_has(struct tcp_pcb *tcp);
#ifdef __cplusplus
}
#endif
#endif /* LWIP_RAW==1 */
#endif /* ASYNC_TCP_SSL_ENABLED */
#endif /* LWIPR_COMPAT_H */

View File

@@ -0,0 +1,70 @@
# Code formatting rules for Arduino libraries, modified from for KH libraries:
#
# https://github.com/arduino/Arduino/blob/master/build/shared/examples_formatter.conf
#
# astyle --style=allman -s2 -t2 -C -S -xW -Y -M120 -f -p -xg -H -xb -c --xC120 -xL *.h *.cpp *.ino
--mode=c
--lineend=linux
--style=allman
# -r or -R
#--recursive
# -c => Converts tabs into spaces
convert-tabs
# -s2 => 2 spaces indentation
--indent=spaces=2
# -t2 => tab =2 spaces
#--indent=tab=2
# -C
--indent-classes
# -S
--indent-switches
# -xW
--indent-preproc-block
# -Y => indent classes, switches (and cases), comments starting at column 1
--indent-col1-comments
# -M120 => maximum of 120 spaces to indent a continuation line
--max-continuation-indent=120
# -xC120 => maxcodelength will break a line if the code exceeds # characters
--max-code-length=120
# -f =>
--break-blocks
# -p => put a space around operators
--pad-oper
# -xg => Insert space padding after commas
--pad-comma
# -H => put a space after if/for/while
pad-header
# -xb => Break one line headers (e.g. if/for/while)
--break-one-line-headers
# -c => Converts tabs into spaces
#--convert-tabs
# if you like one-liners, keep them
#keep-one-line-statements
# -xV
--attach-closing-while
#unpad-paren
# -xp
remove-comment-prefix

View File

@@ -0,0 +1,6 @@
#!/bin/bash
for dir in . ; do
find $dir -type f \( -name "*.c" -o -name "*.h" -o -name "*.cpp" -o -name "*.ino" \) -exec astyle --suffix=none --options=./utils/astyle_library.conf \{\} \;
done