22// Copyright 2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov, Jorge Novo
33
44/*
5- This example demonstrates how to create a TCP Chargen server with the
5+ This example demonstrates how to create a TCP Chargen server with the
66 AsyncTCP library. Run on the remote computer:
7-
7+
88 $ nc <IPAddressforESP32> 19
9-
9+
1010 it shows a continuous stream of characters like this:
1111
1212 #$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghij
2121
2222 If the pattern shows broken your ESP32 is probably too busy to serve the
2323 data or the network is congested.
24-
24+
2525*/
2626
2727#include < Arduino.h>
@@ -40,140 +40,140 @@ const size_t PATTERN_LENGTH_FULL = 95;
4040#define WIFI_PASSWORD " YourPassword"
4141
4242// This is the main asynchronous server object
43- AsyncServer* AsyncServerChargen = nullptr ;
43+ AsyncServer * AsyncServerChargen = nullptr ;
4444// This is the pointer to the single connected client
45- AsyncClient* AsyncClientChargen = nullptr ;
45+ AsyncClient * AsyncClientChargen = nullptr ;
4646// Tracks the current position in the pattern rotation for the Chargen protocol.
4747size_t startIndex = 0 ;
4848
49- void makeAndSendLine (); // Forward declaration
49+ void makeAndSendLine (); // Forward declaration
5050
5151// --- Callback Functions ---
5252
5353// Called when the client acknowledges receiving data. We use this to send more.
54- void handleClientAck (void * arg, AsyncClient* client, size_t len, uint32_t time) {
55- if (!client->disconnected () && client->space () >= (LINE_LENGTH + 2 )) {
56- makeAndSendLine ();
57- }
54+ void handleClientAck (void * arg, AsyncClient * client, size_t len, uint32_t time) {
55+ if (!client->disconnected () && client->space () >= (LINE_LENGTH + 2 )) {
56+ makeAndSendLine ();
57+ }
5858}
5959
6060// Called periodically while the client is connected.
61- void handleClientPoll (void * arg, AsyncClient* client) {
62- // We can reuse the same logic as the ACK handler.
63- // Just try to send more data if there's space.
64- handleClientAck (arg, client, 0 , 0 );
61+ void handleClientPoll (void * arg, AsyncClient * client) {
62+ // We can reuse the same logic as the ACK handler.
63+ // Just try to send more data if there's space.
64+ handleClientAck (arg, client, 0 , 0 );
6565}
6666
6767// It handles errors that are not normal disconnections.
68- void handleClientError (void * arg, AsyncClient* client, int error) {
69- // The error codes are defined in esp_err.h
70- Serial.printf (" Client error! Code: %d, Message: %s\n " , error, client->errorToString (error));
71-
72- // If the client is the one we have stored, clean it up.
73- if (AsyncClientChargen == client) {
74- Serial.println (" Cleaning up global client pointer due to error." );
75- AsyncClientChargen = nullptr ;
76- }
77- // We do not need to call "delete client" here because onDisconnect will do it.
78- // If the error is critical, we will do it.
79- if (client->connected ()) {
80- client->close ();
81- }
68+ void handleClientError (void * arg, AsyncClient * client, int error) {
69+ // The error codes are defined in esp_err.h
70+ Serial.printf (" Client error! Code: %d, Message: %s\n " , error, client->errorToString (error));
71+
72+ // If the client is the one we have stored, clean it up.
73+ if (AsyncClientChargen == client) {
74+ Serial.println (" Cleaning up global client pointer due to error." );
75+ AsyncClientChargen = nullptr ;
76+ }
77+ // We do not need to call "delete client" here because onDisconnect will do it.
78+ // If the error is critical, we will do it.
79+ if (client->connected ()) {
80+ client->close ();
81+ }
8282}
8383
8484// Called when the client disconnects.
85- void handleClientDisconnect (void * arg, AsyncClient* client) {
86- Serial.println (" Client disconnected." );
87- // Set the global client pointer to null to allow a new client to connect.
88- if (AsyncClientChargen == client) {
89- AsyncClientChargen = nullptr ;
90- }
91- delete client;
85+ void handleClientDisconnect (void * arg, AsyncClient * client) {
86+ Serial.println (" Client disconnected." );
87+ // Set the global client pointer to null to allow a new client to connect.
88+ if (AsyncClientChargen == client) {
89+ AsyncClientChargen = nullptr ;
90+ }
91+ delete client;
9292}
9393
9494// Called when a new client tries to connect.
95- void handleClient (void * arg, AsyncClient* client) {
96- // If there is already a client connected, reject the new one.
97- if (AsyncClientChargen) {
98- Serial.printf (" New connection from %s rejected. Server is busy.\n " , client->remoteIP ().toString ().c_str ());
99- client->close ();
100- return ;
101- }
102-
103- // Accept the new client.
104- Serial.printf (" New client connected from %s\n " , client->remoteIP ().toString ().c_str ());
105- AsyncClientChargen = client;
106- startIndex = 0 ; // Reset pattern for the new client.
107-
108- // Called when previously sent data is acknowledged by the client.
109- // This is the core engine for continuous data transmission (Chargen).
110- AsyncClientChargen->onAck (handleClientAck, nullptr );
111-
112- // Called periodically by the AsyncTCP task.
113- // Serves as a backup to resume transmission if the buffer was full and the ACK wasn't received.
114- AsyncClientChargen->onPoll (handleClientPoll, nullptr );
115-
116- // Called when a communication error (e.g., protocol failure or timeout) occurs.
117- // Essential for cleaning up the global client pointer and preventing resource leaks.
118- AsyncClientChargen->onError (handleClientError, nullptr );
119-
120- // Called when the client actively closes the connection or if a fatal error occurs.
121- // Responsible for resetting the global client pointer and freeing memory.
122- AsyncClientChargen->onDisconnect (handleClientDisconnect, nullptr );
123-
124- // Start sending data immediately.
125- makeAndSendLine ();
95+ void handleClient (void * arg, AsyncClient * client) {
96+ // If there is already a client connected, reject the new one.
97+ if (AsyncClientChargen) {
98+ Serial.printf (" New connection from %s rejected. Server is busy.\n " , client->remoteIP ().toString ().c_str ());
99+ client->close ();
100+ return ;
101+ }
102+
103+ // Accept the new client.
104+ Serial.printf (" New client connected from %s\n " , client->remoteIP ().toString ().c_str ());
105+ AsyncClientChargen = client;
106+ startIndex = 0 ; // Reset pattern for the new client.
107+
108+ // Called when previously sent data is acknowledged by the client.
109+ // This is the core engine for continuous data transmission (Chargen).
110+ AsyncClientChargen->onAck (handleClientAck, nullptr );
111+
112+ // Called periodically by the AsyncTCP task.
113+ // Serves as a backup to resume transmission if the buffer was full and the ACK wasn't received.
114+ AsyncClientChargen->onPoll (handleClientPoll, nullptr );
115+
116+ // Called when a communication error (e.g., protocol failure or timeout) occurs.
117+ // Essential for cleaning up the global client pointer and preventing resource leaks.
118+ AsyncClientChargen->onError (handleClientError, nullptr );
119+
120+ // Called when the client actively closes the connection or if a fatal error occurs.
121+ // Responsible for resetting the global client pointer and freeing memory.
122+ AsyncClientChargen->onDisconnect (handleClientDisconnect, nullptr );
123+
124+ // Start sending data immediately.
125+ makeAndSendLine ();
126126}
127127
128128void makeAndSendLine () {
129- // Check if the client is valid and has enough space in its send buffer.
130- if (AsyncClientChargen && AsyncClientChargen->canSend () && AsyncClientChargen->space () >= (LINE_LENGTH + 2 )) {
131- // Buffer for the line (72 characters + \r\n)
132- char lineBuffer[LINE_LENGTH + 2 ];
133-
134- // 1. Construct the 72-character line using the rotating pattern.
135- for (size_t i = 0 ; i < LINE_LENGTH; i++) {
136- lineBuffer[i] = CHARGEN_PATTERN_FULL[(startIndex + i) % PATTERN_LENGTH_FULL];
137- }
129+ // Check if the client is valid and has enough space in its send buffer.
130+ if (AsyncClientChargen && AsyncClientChargen->canSend () && AsyncClientChargen->space () >= (LINE_LENGTH + 2 )) {
131+ // Buffer for the line (72 characters + \r\n)
132+ char lineBuffer[LINE_LENGTH + 2 ];
133+
134+ // 1. Construct the 72-character line using the rotating pattern.
135+ for (size_t i = 0 ; i < LINE_LENGTH; i++) {
136+ lineBuffer[i] = CHARGEN_PATTERN_FULL[(startIndex + i) % PATTERN_LENGTH_FULL];
137+ }
138138
139- // 2. Add the standard CHARGEN line terminator (\r\n).
140- lineBuffer[LINE_LENGTH] = ' \r ' ;
141- lineBuffer[LINE_LENGTH + 1 ] = ' \n ' ;
139+ // 2. Add the standard CHARGEN line terminator (\r\n).
140+ lineBuffer[LINE_LENGTH] = ' \r ' ;
141+ lineBuffer[LINE_LENGTH + 1 ] = ' \n ' ;
142142
143- // 3. Write data to the socket.
144- AsyncClientChargen->write (lineBuffer, LINE_LENGTH + 2 );
143+ // 3. Write data to the socket.
144+ AsyncClientChargen->write (lineBuffer, LINE_LENGTH + 2 );
145145
146- // 4. Advance the starting index for the next line (rotation).
147- startIndex = (startIndex + 1 ) % PATTERN_LENGTH_FULL;
148- }
146+ // 4. Advance the starting index for the next line (rotation).
147+ startIndex = (startIndex + 1 ) % PATTERN_LENGTH_FULL;
148+ }
149149}
150150
151151// ---------------------- SETUP & LOOP ----------------------
152152
153153void setup () {
154- Serial.begin (115200 );
155- while (!Serial) {
156- continue ;
157- }
158- // Connecting to WiFi...
159- WiFi.mode (WIFI_STA);
160- WiFi.begin (WIFI_SSID, WIFI_PASSWORD);
161- Serial.print (" Connecting to WiFi..." );
162- while (WiFi.status () != WL_CONNECTED) {
163- delay (500 );
164- Serial.print (" ." );
165- }
166- Serial.println ();
167-
168- // Create the Async TCP Server
169- AsyncServerChargen = new AsyncServer (CHARGEN_PORT);
170- // Set up the callback for new client connections.
171- AsyncServerChargen->onClient (&handleClient, nullptr );
172- AsyncServerChargen->begin ();
173- Serial.printf (" Chargen Server (%s) started on port %d\n " , WiFi.localIP ().toString ().c_str (), CHARGEN_PORT);
154+ Serial.begin (115200 );
155+ while (!Serial) {
156+ continue ;
157+ }
158+ // Connecting to WiFi...
159+ WiFi.mode (WIFI_STA);
160+ WiFi.begin (WIFI_SSID, WIFI_PASSWORD);
161+ Serial.print (" Connecting to WiFi..." );
162+ while (WiFi.status () != WL_CONNECTED) {
163+ delay (500 );
164+ Serial.print (" ." );
165+ }
166+ Serial.println ();
167+
168+ // Create the Async TCP Server
169+ AsyncServerChargen = new AsyncServer (CHARGEN_PORT);
170+ // Set up the callback for new client connections.
171+ AsyncServerChargen->onClient (&handleClient, nullptr );
172+ AsyncServerChargen->begin ();
173+ Serial.printf (" Chargen Server (%s) started on port %d\n " , WiFi.localIP ().toString ().c_str (), CHARGEN_PORT);
174174}
175175
176176void loop () {
177- // The async library handles everything in the background.
178- // No code is needed here for the server to run.
179- }
177+ // The async library handles everything in the background.
178+ // No code is needed here for the server to run.
179+ }
0 commit comments