1
Appendix A: Source Code for ESP32 Chat System
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <map>
#include <deque>
const char* AP_SSID = "ESP32_Chat";
const char* AP_PASS = "12345678";
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
// map client id -> username
std::map<uint32_t, String> clientNames;
// === NEW === rate limiting structures
struct ClientActivity {
std::deque<unsigned long> timestamps; // last request times (ms)
unsigned long banUntil = 0; // ban expiry
};
std::map<String, ClientActivity> clientActivity; // key = IP as string
const int MAX_REQUESTS = 4; // max allowed per window
2
const int TIME_WINDOW = 10000; // 10 seconds
const int BAN_DURATION = 30000; // 30 sec ban
// helper: check if IP is banned or exceeding limit
bool isAllowed(const String &ip) {
unsigned long now = millis();
ClientActivity &ca = clientActivity[ip];
// still banned?
if ([Link] > now) return false;
// remove old timestamps
while (![Link]() && now - [Link]() > TIME_WINDOW) {
[Link].pop_front();
// check limit
if ((int)[Link]() >= MAX_REQUESTS) {
[Link] = now + BAN_DURATION; // ban
[Link]();
return false;
// record request
[Link].push_back(now);
3
return true;
void notifyAll(const String &msg) {
[Link](msg);
void handleWebSocketEvent(AsyncWebSocket * serverPtr, AsyncWebSocketClient * client,
AwsEventType type, void * arg, uint8_t *data, size_t len) {
String ip = client->remoteIP().toString();
if(type == WS_EVT_CONNECT) {
// === NEW === limit connections
if (!isAllowed(ip)) {
[Link]("BLOCKED WS connect from %s\n", ip.c_str());
client->close(); // immediately disconnect
return;
[Link]("WS: Client #%u connected from %s\n", client->id(), ip.c_str());
client->printf("SERVER: Please login by sending: login:YourName");
else if(type == WS_EVT_DISCONNECT) {
[Link]("WS: Client #%u disconnected\n", client->id());
4
auto it = [Link](client->id());
if(it != [Link]()){
String leftMsg = String("SYSTEM: ") + it->second + " left the chat";
notifyAll(leftMsg);
[Link](it);
else if(type == WS_EVT_DATA) {
// === NEW === limit data/message spam
if (!isAllowed(ip)) {
[Link]("BLOCKED data from %s\n", ip.c_str());
client->close();
return;
String msg = "";
for(size_t i=0;i<len;i++) msg += (char)data[i];
[Link]("WS: Data from #%u: %s\n", client->id(), msg.c_str());
if([Link]("login:")) {
String name = [Link](6);
[Link]();
if([Link]() == 0) {
client->printf("SERVER: invalid name");
5
return;
clientNames[client->id()] = name;
client->printf("SERVER: Welcome %s", name.c_str());
String announce = String("SYSTEM: ") + name + " joined the chat";
notifyAll(announce);
else if([Link]("msg:")) {
String body = [Link](4);
[Link]();
auto it = [Link](client->id());
String from = (it != [Link]()) ? it->second : String("Unknown");
String out = from + ": " + body;
notifyAll(out);
else {
client->printf("SERVER: unknown command");
// ... keep your webpage code unchanged ...
const char index_html[] PROGMEM = R"rawliteral(
<!doctype html>
<html>
6
<head>
<meta charset="utf-8">
<title>ESP32 Chat</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
background: #f0f2f5;
h3 {
margin-bottom: 10px;
color: #333;
#status {
margin-bottom: 10px;
font-weight: bold;
color: #0066cc;
#log {
width: 100%;
height: 300px;
border: 1px solid #ccc;
overflow-y: auto;
padding: 8px;
background: #fff;
7
box-shadow: inset 0 0 4px rgba(0,0,0,0.1);
border-radius: 6px;
font-size: 14px;
#controls {
margin-top: 12px;
input, button {
padding: 8px;
font-size: 14px;
border-radius: 4px;
border: 1px solid #ccc;
input:focus {
outline: none;
border-color: #0066cc;
button {
background: #0066cc;
color: white;
border: none;
cursor: pointer;
button:hover {
background: #004999;
8
#msg {
width: 70%;
</style>
</head>
<body>
<h3>IOT Chat (PROJECT BY STINKBUG)</h3>
<div id="status">Not connected</div>
<div id="log"></div>
<div id="controls">
<input id="name" placeholder="Your name">
<button id="btnLogin">Login</button><br><br>
<input id="msg" placeholder="Type a message...">
<button id="btnSend">Send</button>
</div>
<script>
let ws;
function log(s) {
let d = [Link]('log');