COMMANDS

~/Downloads/arduino-ide_2.3.4_Linux_64bit.AppImage &> /dev/null
sudo apt install python3-pip
pip3 install pyserial
sudo usermod -aG userName dialout
sudo chmod a+rw /dev/ttyUSB0

EXAMPLES

(1) HTTP API Endpoints with JSON

// ESP32-WROOM-32U

#include <WiFi.h>
#include <WebServer.h>
#include <ArduinoJson.h>
#include <HTTPClient.h>

// WiFi credentials
const char* ssid = "wifi_name";
const char* password = "wifi_password";

// Create a WebServer object on port 80
WebServer server(80);

// Handler for "/"
void handleRoot() {
  String body = "<!DOCTYPE html><html><head><title>ESP32</title></head><body>";
  body += "<h1>ESP32 HTTP Server!</h1>\n\n";
  body += "<a href=\"/status\">Show Status</a><br>\n";
  body += "<a href=\"/push\">Push Status</a><br>\n";
  body += "<a href=\"/serial\">Output Status</a><br>\n";
  body += "</body></html>";
  server.send(200, "text/html", body);
  Serial.println("Client IP: " + server.client().remoteIP().toString());
}

String getStatusJson() {
  StaticJsonDocument<512> jsonData;
  jsonData["wifi_connected"] = String(WiFi.status() == WL_CONNECTED ? "true" : "false");
  jsonData["signal_strength"] = String(WiFi.RSSI());
  jsonData["ssid"] = WiFi.SSID();
  jsonData["ip"] = WiFi.localIP().toString();
  jsonData["uptime"] = String(millis() / 1000);
  jsonData["free_heap"] = String(ESP.getFreeHeap());
  jsonData["chip_model"] = String(ESP.getChipModel());
  jsonData["flash_size"] = String(ESP.getFlashChipSize());
  String jsonString;
  serializeJson(jsonData, jsonString);
  return jsonString;
}

void pushNotification(const String &message = "") {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    http.begin("http://ntfy.sh/subscription_topic");
    http.addHeader("Content-Type", "application/json");

    String payload;
    if (message.length() > 0) {
      payload = message;
    } else {
      payload = getStatusJson();
    }

    int httpResponseCode = http.POST(payload);
    if (httpResponseCode == 200) {
      Serial.println("Push notification succeeded!");
    } else {
      Serial.println("Push failed with error: " + String(httpResponseCode));
    }

    http.end();
  }
}

// Handler for "/status"
void handleStatus() {
  server.send(200, "application/json", getStatusJson());
}

// Handler for "/push"
void handlePush() {
  pushNotification();
  server.send(200, "text/plain", "");
}

// Handler for "/serial"
void handleSerial() {
  Serial.println(getStatusJson());
  server.send(200, "text/plain", "");
}

// Handler for 404
void handleNotFound() {
  server.send(404, "text/plain", "404: Not Found");
}

void setup() {
  Serial.begin(115200);
  delay(1000);

  Serial.println("");
  Serial.println("Serial Started");

  Serial.println("Starting WiFi");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.println("Connecting...");
  }
  Serial.println("Connected to WiFi!");
  Serial.println("IP Address: " + WiFi.localIP().toString());

  // Define URL routes
  server.on("/", handleRoot);
  server.on("/status", handleStatus);
  server.on("/push", handlePush);
  server.on("/serial", handleSerial);
  server.onNotFound(handleNotFound);

  // Start the server
  server.begin();
  Serial.println("HTTP server started");

  // Send push notification with IP address
  StaticJsonDocument<48> jsonData;
  jsonData["ip"] = WiFi.localIP().toString();
  String jsonString;
  serializeJson(jsonData, jsonString);
  pushNotification(jsonString);
}

void loop() {
  server.handleClient();
}

(2) A TCP/UDP Honey-Pot named Albedu0

Albedo is the property of a material to absorb or reflect radiation. So, when the albedo value is close to 1, it reflects almost the totality. When close to zero, it does not reflect anything.

Analogously, this honey-pot does not respond on the application layer (after the TCP 3-way handshake) but instead notifies the network administrator that someone is probing it.

// ESP32-WROOM-32U - Albedu0

#include <WiFi.h>
#include <WiFiUdp.h>
#include <HTTPClient.h> 

const char* ssid     = "wifi_name";
const char* password = "wifi_password";
const char* ntfyUrl  = "http://ntfy.sh/subscription_topic";

const uint16_t tcpPorts[] = {21, 22, 23, 80, 445, 3389, 8080};
const uint16_t udpPorts[] = {53, 123, 161, 1900};
constexpr size_t N_TCP = sizeof(tcpPorts) / sizeof(tcpPorts[0]);
constexpr size_t N_UDP = sizeof(udpPorts) / sizeof(udpPorts[0]);

WiFiServer* tcpSrv[N_TCP];
WiFiUDP     udpSrv[N_UDP];
uint32_t    hitCount = 0;

String toHex(const uint8_t* b, size_t n) {
  String s;
  for (size_t i = 0; i < n; i++) {
    if (b[i] < 0x10) s += '0';
    s += String(b[i], HEX);
    if (i + 1 < n) s += ' ';
  }
  return s;
}

String toAscii(const uint8_t* b, size_t n) {
  String s;
  for (size_t i = 0; i < n; i++) s += (b[i] >= 32 && b[i] < 127) ? (char)b[i] : '.'; return s; } void snitch(const String& proto, const IPAddress& src, uint16_t sport, uint16_t dport, const uint8_t* data, size_t len) { hitCount++; String body; body += "proto=" + proto + "\n"; body += "src=" + src.toString() + ":" + String(sport) + "\n"; body += "dst=" + WiFi.localIP().toString() + ":" + String(dport) + "\n"; body += "bytes=" + String(len) + "\n"; if (len > 0) {
    body += "hex="   + toHex(data, len)   + "\n";
    body += "ascii=" + toAscii(data, len) + "\n";
  }
  body += "rssi="   + String(WiFi.RSSI()) + "dBm\n";
  body += "uptime=" + String(millis() / 1000) + "s\n";
  body += "hit#"    + String(hitCount);

  Serial.println("---- HIT ----");
  Serial.println(body);

  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    http.begin(ntfyUrl);
    http.addHeader("Title", "Honeypot " + proto + " :" + String(dport));
    http.addHeader("Tags", "warning");
    http.addHeader("Priority", "high");
    int code = http.POST(body);
    Serial.println("ntfy -> " + String(code));
    http.end();
  }
}

// TCP
void pollTcp() {
  for (size_t i = 0; i < N_TCP; i++) { WiFiClient c = tcpSrv[i]->accept();
    if (!c) continue;

    IPAddress src = c.remoteIP();
    uint16_t sport = c.remotePort();

    uint8_t buf[64];
    size_t len = 0;
    unsigned long t0 = millis();
    while (millis() - t0 < 80 && len < sizeof(buf)) {
      while (c.available() && len < sizeof(buf)) buf[len++] = c.read();
      if (!c.connected()) break;
      delay(5);
    }
    c.stop();

    snitch("TCP", src, sport, tcpPorts[i], buf, len);
  }
}

// UDP
void pollUdp() {
  for (size_t i = 0; i < N_UDP; i++) {
    int sz = udpSrv[i].parsePacket();
    if (sz <= 0) continue;

    IPAddress src = udpSrv[i].remoteIP();
    uint16_t sport = udpSrv[i].remotePort();

    uint8_t buf[64];
    int len = udpSrv[i].read(buf, sizeof(buf));
    if (len < 0) len = 0;

    snitch("UDP", src, sport, udpPorts[i], buf, len);
  }
}

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println("\nHoneypot booting");

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
  Serial.println("\nWiFi up: " + WiFi.localIP().toString());

  for (size_t i = 0; i < N_TCP; i++) { tcpSrv[i] = new WiFiServer(tcpPorts[i]); tcpSrv[i]->begin();
    tcpSrv[i]->setNoDelay(true);
  }
  for (size_t i = 0; i < N_UDP; i++) udpSrv[i].begin(udpPorts[i]);

  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    http.begin(ntfyUrl);
    http.addHeader("Title", "Honeypot online");
    http.POST("watching " + WiFi.localIP().toString());
    http.end();
  }
  Serial.println("Listening.");
}

void loop() {
  if (WiFi.status() != WL_CONNECTED) { WiFi.reconnect(); delay(500); return; }
  pollTcp();
  pollUdp();
}

DOCUMENTATION

  • ESP32 WebServer [Link]
  • ESP32 HTTPClient [Link]
  • ArduinoJson [Link]

ONLINE SERVICES

ntfy (pronounced “notify”) is a simple HTTP-based pub-sub notification service that sends alerts to your phone or desktop [Link]. It is open-source and can be self-hosted [Link].

healthchecks.io is an open-source, HTTP-based monitoring tool that collects events from multiple sources, displays them in a single dashboard, and sends notifications for failures or missing events [Link]. Read more about self-hosting it at [Link].