ESP32, AsyncTCP przykład klienta
AsyncTCP
, wraz z omówieniem podstaw konfiguracji, diagnostyki oraz dobrych praktyk. loop()
, pełny zestaw callbacków (‐Connect, ‐Data, ‐Disconnect, ‐Error, ‐Timeout), przykładowy mechanizm automatycznego ponownego łączenia i periodycznego wysyłania danych.AsyncTCP
?1.1. Biblioteka pracuje bezblokująco – opiera się na zdarzeniach realizowanych przez podsystem FreeRTOS/LwIP.
1.2. Dzięki jednorazowej rejestracji callbacków kod użytkownika pozostaje prosty: pętla loop()
może wykonywać inne zadania (czujniki, UI, logika aplikacyjna).
1.3. AsyncTCP
obsługuje wiele gniazd jednocześnie (klient/serwer), co przy klasycznym WiFiClient
wymagałoby złożonego ręcznego multiplexer’a.
2.1. Arduino IDE
• Płytki: Espressif ESP32 (Board Manager URL: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
).
• Biblioteka: AsyncTCP by me-no-dev
(aktualna gałąź 1.1.3).
2.2. PlatformIO
platform = espressif32
framework = arduino
lib_deps = me-no-dev/AsyncTCP
#include <WiFi.h>
#include <AsyncTCP.h>
/* --- konfiguracja użytkownika --- */
constexpr char SSID[] = "TwojaSiecWiFi";
constexpr char PASS[] = "HasloWiFi";
constexpr char HOST[] = "192.168.1.100"; // IP lub domena serwera
constexpr uint16_t PORT = 8080; // port serwera
/* --- obiekty globalne --- */
AsyncClient client; // pojedyncze połączenie
bool connected = false; // flaga stanu
uint32_t lastTx = 0; // znacznik czasu dla transmisji okresowej
/* --- deklaracje pomocnicze --- */
void connectToServer();
/* ---------- SETUP ---------- */
void setup()
{
Serial.begin(115200);
/* 1. Wi-Fi (blokujące, dozwolone w setup) */
WiFi.mode(WIFI_STA);
WiFi.begin(SSID, PASS);
Serial.print(F("Łączenie z Wi-Fi"));
while (WiFi.status() != WL_CONNECTED) {
delay(400);
Serial.print('.');
}
Serial.printf("\nPołączono! IP ESP32: %s\n", WiFi.localIP().toString().c_str());
/* 2. Rejestracja callbacków */
client.onConnect([](void*, AsyncClient* c){
connected = true;
Serial.println(F(">> CONNECT OK"));
c->write("Hello from ESP32!\n");
});
client.onData([](void*, AsyncClient* c, void* data, size_t len){
Serial.printf(">> RX %uB: ", len);
Serial.write((uint8_t*)data, len); // wypisz odebrane bajty
Serial.println();
});
client.onDisconnect([](void*, AsyncClient*){
connected = false;
Serial.println(F(">> DISCONNECT"));
});
client.onError([](void*, AsyncClient*, int8_t err){
Serial.printf(">> ERROR %d\n", err);
connected = false;
});
client.onTimeout([](void*, AsyncClient*, uint32_t time){
Serial.printf(">> TIMEOUT after %lu ms\n", time);
});
/* 3. Pierwsze łączenie */
connectToServer();
}
/* ---------- LOOP ---------- */
void loop()
{
/* cykliczna wysyłka co 10 s */
if (connected && (millis() - lastTx > 10'000)) {
lastTx = millis();
String payload = "Ping: " + String(lastTx) + "\n";
if (client.space() >= payload.length() && client.canSend())
client.write((uint8_t*)payload.c_str(), payload.length());
}
/* prosty autoretry co 5 s gdy rozłączony */
static uint32_t lastRetry = 0;
if (!connected && (millis() - lastRetry > 5'000)) {
lastRetry = millis();
connectToServer();
}
}
/* ---------- FUNKCJE ---------- */
void connectToServer()
{
Serial.printf("Próba połączenia z %s:%u ...\n", HOST, PORT);
if (!client.connect(HOST, PORT))
Serial.println(F("init/connect NIEUDANE (brak buf. albo zły host)"));
}
4.1. Po wywołaniu client.connect()
stos TCP w LwIP inicjuje strukturę PCB, ale funkcja natychmiast zwraca – samo zestawienie trzech pakietów (SYN–SYN/ACK–ACK) obsługuje RTOS w osobnym wątku sieciowym.
4.2. Po otrzymaniu ACK biblioteka wykonuje callback onConnect
, sygnalizując gotowość wysyłania.
4.3. client.write()
buforuje dane w ramkach LwIP do czasu wolnego okienka TCP i potwierdzeń (onAck()
dostępny opcjonalnie).
• 2024: Espressif publikuje ESP-IDF component esp32-async/asynctcp
(wersje 3.x) kompatybilny z Arduino-ESP32 ≥ 3.0 i ESP-IDF v5; warto rozważyć migrację przy projektach budowanych natywnie w ESP-IDF.
• Coraz częściej wykorzystywany jest security layer TLS (AsyncSSL
lub WiFiClientSecure
) – w zastosowaniach produkcyjnych zaleca się szyfrowanie, do czego potrzebny jest większy przydział RAM (≥ 300 kB).
• W projektach wielozadaniowych popularne staje się przypisywanie AsyncClient
do oddzielnego rdzenia (funkcje FreeRTOS xTaskCreatePinnedToCore
) i komunikacja przez kolejki.
• Funkcja space()
zwraca wolny bufor wysyłkowy (domyślnie 536 B, konfigurowalny w CONFIG_ASYNC_TCP_RX_SIZE
).
• Kod błędu w onError
odpowiada definicjom lwIP err_t (np. -11
= ERR_TIMEOUT
, -12
= ERR_RTE
).
• Połączenie można zamknąć elegancko wywołując client.close()
; wyśle FIN i wywoła onDisconnect
.
• Jeśli konieczne jest sklejanie wiadomości z wielu ramek, zastosuj własny bufor i delimitery (\n
, STX/ETX, nagłówek z długością, JSON‐RPC itp.).
• W środowiskach przemysłowych połączenia nieszyfrowane (raw TCP) mogą naruszać politykę IoT Security – rozważ TLS lub przynajmniej wewnętrzny VLAN.
• Nadmierny ruch generowany przez pingi/keep-alive może łamać regulaminy sieci operatorów komórkowych – przestrzegaj umów SLA.
• Biblioteka AsyncTCP
jest licencjonowana na MIT – zgodnie z licencją należy zachować adnotację o autorach w dokumentacji produktu.
nc -l -p 8080
lub Python socketserver
– łatwe logowanie i echo. Core Debug
na poziom „Verbose” (menuconfig
w IDF) – zobaczysz pakiety SYN/ACK w terminalu. tcp_keepalive
(client.setAckTimeout()
w wersjach 1.2+). esp_get_free_heap_size()
) – każde otwarte gniazdo ≈ 11 kB. • Na starych wersjach Arduino-ESP32 ≤ 2.0.5 występuje błąd „Guru Meditation: lwip-tcp” po dużym ruchu – zaktualizuj SDK.
• AsyncClient
NIE obsługuje IPv6; jeśli sieć to wymusza, potrzebny jest niskopoziomowy moduł LwIP.
• Przy wielu klientów (>5) konieczna może być zmiana CONFIG_LWIP_MAX_SOCKETS
i rozmiaru sterty.
• Integracja AsyncTCP
z MQTT-over-TLS (np. biblioteka AsyncMqttClient) – pełna asynchroniczność publish/subscribe.
• Mechanizmy zerowej konfiguracji (mDNS, DNS-SD) do dynamicznego wyszukiwania serwerów.
• Analiza wydajności: wpływ tcp_window_size
i nagle_disable
na throughput.
• Hard RT tasks: przeniesienie warstwy biznesowej na drugi rdzeń (CORE1) i synchronizacja za pomocą FreeRTOS queue.
Przedstawiony przykład pokazuje, jak w kilku liniach uruchomić w pełni asynchronicznego klienta TCP na ESP32, który:
• nie blokuje głównej pętli,
• poprawnie reaguje na zdarzenia sieciowe,
• automatycznie wznawia połączenie.
Zaktualizowana biblioteka AsyncTCP
(2024) pozostaje lekkim i elastycznym rozwiązaniem do komunikacji surowym TCP. W środowiskach produkcyjnych zalecam dodanie TLS, watchdogów połączeń i dokładnego monitoringu pamięci.