Skip to content

Commit 20e6639

Browse files
committed
example: Simplified version of a TCP Chargen server.
1 parent 6e92263 commit 20e6639

1 file changed

Lines changed: 152 additions & 0 deletions

File tree

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// SPDX-License-Identifier: LGPL-3.0-or-later
2+
// Copyright 2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov, Jorge Novo
3+
4+
/*
5+
This example demonstrates how to cretate a TCP Chargen server with the
6+
AsyncTCP library. Run on the remote computer:
7+
8+
$ nc <IPAddressforESP32> 19
9+
10+
it shows a continuous stream of characters like this:
11+
12+
#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghij
13+
$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijk
14+
%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkl
15+
&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklm
16+
'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmn
17+
()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmno
18+
)*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop
19+
*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopq
20+
+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqr
21+
22+
If the pattern shows broken your ESP32 is probably too busy to serve the
23+
data or the network is congested.
24+
25+
*/
26+
27+
#include <Arduino.h>
28+
#include <AsyncTCP.h>
29+
#include <WiFi.h>
30+
31+
// The default TCP Chargen port number is 19, see RFC 864 (Character Generator Protocol)
32+
#define CHARGEN_PORT 19
33+
const size_t LINE_LENGTH = 72;
34+
35+
// Full pattern of printable ASCII characters (95 characters)
36+
const char CHARGEN_PATTERN_FULL[] = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
37+
const size_t PATTERN_LENGTH_FULL = 95;
38+
39+
#define WIFI_SSID "Your_SSID"
40+
#define WIFI_PASSWORD "Your_PASSWORD"
41+
42+
// --- Global state for a SINGLE client ---
43+
// This is the main asynchronous server object
44+
AsyncServer* AsyncServerChargen = nullptr;
45+
// This is the pointer to the single connected client
46+
AsyncClient* AsyncClientChargen= nullptr;
47+
// This is the pointer to the stream of data.
48+
size_t startIndex = 0;
49+
50+
51+
void makeAndSendLine(); // Forward declaration
52+
53+
// --- Callback Functions ---
54+
55+
// Called when the client acknowledges receiving data. We use this to send more.
56+
void handleClientAck(void* arg, AsyncClient* client, size_t len, uint32_t time) {
57+
if (!client->disconnected() && client->space() > (LINE_LENGTH + 2)) {
58+
makeAndSendLine(); // <--- ¡Aquí está la cadena!
59+
}
60+
}
61+
62+
// Called periodically while the client is connected.
63+
// This function has the correct signature for onPoll: void(void*, AsyncClient*).
64+
void handleClientPoll(void* arg, AsyncClient* client) {
65+
// We can reuse the same logic as the ACK handler.
66+
// Just try to send more data if there's space.
67+
handleClientAck(arg, client, 0, 0);
68+
}
69+
70+
// Called when the client disconnects.
71+
void handleClientDisconnect(void* arg, AsyncClient* client) {
72+
Serial.println("Client disconnected.");
73+
// Set the global client pointer to null to allow a new client to connect.
74+
AsyncClientChargen = nullptr;
75+
}
76+
77+
// Called when a new client tries to connect.
78+
void handleClient(void* arg, AsyncClient* client) {
79+
// If there is already a client connected, reject the new one.
80+
if (AsyncClientChargen) {
81+
Serial.printf("New connection from %s rejected. Server is busy.\n", client->remoteIP().toString().c_str());
82+
client->close();
83+
return;
84+
}
85+
86+
// Accept the new client.
87+
Serial.printf("New client connected from %s\n", client->remoteIP().toString().c_str());
88+
AsyncClientChargen = client;
89+
startIndex = 0; // Reset pattern for the new client.
90+
91+
// Set up callbacks for the new client.
92+
AsyncClientChargen->onAck(handleClientAck, nullptr);
93+
// onPoll is a good backup to send data if the buffer was full.
94+
AsyncClientChargen->onPoll(handleClientPoll, nullptr);
95+
AsyncClientChargen->onDisconnect(handleClientDisconnect, nullptr);
96+
97+
// Start sending data immediately.
98+
makeAndSendLine();
99+
}
100+
101+
void makeAndSendLine() {
102+
// Check if the client is valid and has enough space in its send buffer.
103+
if (AsyncClientChargen && AsyncClientChargen->canSend() && AsyncClientChargen->space() >= (LINE_LENGTH + 2)) {
104+
// Buffer for the line (72 characters + \r\n)
105+
char lineBuffer[LINE_LENGTH + 2];
106+
107+
// 1. Construct the 72-character line using the rotating pattern.
108+
for (size_t i = 0; i < LINE_LENGTH; i++) {
109+
lineBuffer[i] = CHARGEN_PATTERN_FULL[(startIndex + i) % PATTERN_LENGTH_FULL];
110+
}
111+
112+
// 2. Add the standard CHARGEN line terminator (\r\n).
113+
lineBuffer[LINE_LENGTH] = '\r';
114+
lineBuffer[LINE_LENGTH + 1] = '\n';
115+
116+
// 3. Write data to the socket.
117+
AsyncClientChargen->write(lineBuffer, LINE_LENGTH + 2);
118+
119+
// 4. Advance the starting index for the next line (rotation).
120+
startIndex = (startIndex + 1) % PATTERN_LENGTH_FULL;
121+
}
122+
}
123+
124+
// ---------------------- SETUP & LOOP ----------------------
125+
126+
void setup() {
127+
Serial.begin(115200);
128+
while (!Serial) {
129+
continue;
130+
}
131+
// Connecting to WiFi...
132+
WiFi.mode(WIFI_STA);
133+
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
134+
Serial.print("Connecting to WiFi...");
135+
while (WiFi.status() != WL_CONNECTED) {
136+
delay(500);
137+
Serial.print(".");
138+
}
139+
Serial.println();
140+
141+
// Create the Async TCP Server
142+
AsyncServerChargen = new AsyncServer(CHARGEN_PORT);
143+
// Set up the callback for new client connections.
144+
AsyncServerChargen->onClient(&handleClient, nullptr);
145+
AsyncServerChargen->begin();
146+
Serial.printf("Chargen Server (%s) started on port %d\n", WiFi.localIP().toString().c_str(), CHARGEN_PORT);
147+
}
148+
149+
void loop() {
150+
// The async library handles everything in the background.
151+
// No code is needed here for the server to run.
152+
}

0 commit comments

Comments
 (0)