Waveshare ESP32-S3-Touch-AMOLED-1.8 · Wi-Fi research tool

A watch face on the outside.
A network sinkhole underneath.

BadAP turns an ESP32-S3 into a wrist-worn access point that answers every DNS, HTTP, HTTPS, NTP, and mail request a connected client throws at it — and quietly logs the rest. On the wrist, it just tells the time.

14 sinkholed & passive protocols 4 log destinations: screen, Serial, SD, web 1 tap-count away from stealth
BADAP00:00:00
DNS 192.168.4.11 -> api.example.com
SNI 192.168.4.11 -> login.example.com
POP3 USER research@example.com
HTTP GET /update.json
DNS 192.168.4.14 -> cdn.example.net
NTP query answered

Tap the watch 5 times quickly to unlock the log view — exactly like the real device.

Overview

One open access point, every request answered.

Boot it, and it starts an open (or WPA2) Wi-Fi network. Anything that joins gets DNS answers that all point back to the device, a web server that says "200 OK" to everything, and fake DNS-over-TLS, NTP, and mail servers that log whatever a client tries before rejecting it. Everything that can't be answered convincingly is still listened to and logged.

Active sinkholes

Answers, then logs

DNS, HTTP, HTTPS/TLS, DNS-over-TLS, NTP, POP3, IMAP, and SMTP all get a real-enough reply so the client keeps talking — every hostname, path, and credential attempt is captured on the way past.

Passive listeners

Silent, but recording

mDNS, SSDP, LLMNR, NBNS, WS-Discovery, and STUN are logged without a reply, so device discovery chatter and STUN binding requests show up without the sinkhole ever interacting.

Logging

Four destinations at once

Every event lands on the AMOLED log, Serial, the SD card (if present), and a live view on the web dashboard — simultaneously, no configuration required.

On-device control

No re-flash to reconfigure

Every sinkhole and listener is a toggle in an on-device settings menu, written back to /sinkhole_config.txt on the SD card.

Protocols

What it answers, and what it just listens to.

ServicePortBehavior
DNS sinkholeUDP/53Resolves every query to the AP's own IP address.
HTTP sinkholeTCP/80Replies "200 OK" to anything, or serves real files from /www on the SD card, falling back to /www/index.html for unmatched paths — captive-portal style.
HTTPS / DoT sinkholeTCP/443, TCP/853Reads the ClientHello, logs the SNI hostname, then drops the connection — unless a valid cert.pem + key.pem pair is on the SD card, in which case port 443 performs a real self-signed TLS handshake so a browser's own trust warning can be checked.
NTP sinkholeUDP/123Replies with a structurally valid time packet, using the onboard RTC if it's been set.
POP3 / IMAP / SMTP sinkholeTCP/110, 143, 25Speaks just enough of each greeting/AUTH exchange to prompt a client for credentials, logs what's sent, then rejects the login. Plaintext auth (USER/PASS, LOGIN, AUTH PLAIN/LOGIN) is flagged distinctly from challenge/response methods like APOP or CRAM-MD5.
mDNS / SSDP passiveUDP/5353, UDP/1900Logs multicast discovery traffic without answering.
LLMNR / NBNS passiveUDP/5355, UDP/137Logs legacy Windows name-resolution broadcasts without answering.
WS-Discovery passiveUDP/3702Logs device-discovery probes without answering.
STUN passiveUDP/3478Logs NAT-traversal binding requests without answering.

On-device UI

Built for a thumb, not a mouse.

BADAP
14:22:07
86%
DNS 10.0.0.4 -> telemetry.io
SNI 10.0.0.4 -> api.vendor.com
SMTP AUTH PLAIN captured
mDNS _airplay._tcp query
DNS 10.0.0.9 -> ntp.pool.org
SSDP M-SEARCH ssdp:all
SNI 10.0.0.4 -> cdn.assets.io

Log view

Word-wrapped, colour-coded by protocol, and touch-scrollable — drag up to review history, drag down to catch up, and it auto-follows new lines when you're at the bottom. A side scrollbar thumb shows roughly where you are.

Header & footer

Both bars sit inset from the edges to clear the panel's own rounded corners. The header carries the title, a live UTC clock, and battery; the footer carries the connected client count and an explicit Settings button.

Settings menu

A scrollable list of on/off pill toggles — one per sinkhole or listener — reached from the header or the footer's Settings button. Back and Save config live as fixed footer buttons, reachable from anywhere in the list.

PWR button

A tap toggles the screen on or off — debounced, and everything keeps sinkholing while the screen is dark. A 6-second-plus hold is still a full hardware power-off regardless of what the sketch is doing.

Stealth mode

What a bystander sees is an ordinary watch.

When the stealth clock face is on (the default), every screen wake shows a plain analog watch face — no branding, no log, nothing that reads as a research tool. Five taps anywhere on the screen, in quick succession, switch through to the real log view. A single tap, or five taps spread too far apart, does nothing visible.

Re-locks on every wake

Turning the screen off and back on with the PWR button always returns to the clock face, so it's never left showing mid-scroll logs. Anything that happened while locked keeps logging in the background — nothing is lost, it's just not on screen.

Toggleable

It's one row in the settings menu — clockface in the config file — so it can be turned off entirely if you'd rather the screen wake straight into the log, same as before.

Web dashboard

The same log, from any browser on the network.

A lightweight dashboard runs on TCP/7777, streaming the same events shown on-device to anyone browsing to the AP's address. It also doubles as the easiest way to set the onboard RTC — punch in a time manually, or sync it to whatever clock the browsing device already has.

Configuration

Every setting lives in one text file.

/sinkhole_config.txt on the SD card holds every flag as a simple key=1 / key=0 line. It's created with sensible defaults on first boot, editable by hand, or entirely from the on-device settings menu.

dns / http / https
Core active sinkholes. On by default.
httpscert
Try a real cert.pem/key.pem on port 443 before falling back to sniff-only.
dot / ntp
DNS-over-TLS and NTP sinkholes.
mdns / ssdp / llmnr / nbns / wsd / stun
The six passive, log-only listeners.
pop3 / imap / smtp
Fake mail servers that prompt for, and log, credentials.
webui / www
The port-7777 dashboard, and serving /www from the SD card.
captiveportal / sdlog / pwrbtn
OS captive-portal probe handling, SD logging, and PWR-button screen toggling.
clockface
The stealth default screen described above. On by default.

The access point itself defaults to SSID Research with password 12345678 — both editable in the same config file.

Setup

From bare board to running BadAP.

  1. Install the board's libraries

    Install XPowersLib and SensorLib (both by lewisxhe) via the Arduino Library Manager, plus pin_config.h and GFX_Library_for_Arduino from Waveshare's example package for the ESP32-S3-Touch-AMOLED-1.8.

  2. Set one board option

    In Arduino IDE: Tools → USB CDC On Boot → Enabled. Everything else can stay at its default.

  3. (Optional) Prep an SD card

    Drop in /www to serve real files from the HTTP sinkhole, or cert.pem + key.pem to give the HTTPS sinkhole a real handshake. Neither is required — the device works from a blank card, or none at all.

  4. Flash and boot

    Compile and upload. On first boot it writes a default /sinkhole_config.txt to the SD card (if present) and starts the Research access point.

  5. Join, and watch it log

    Connect a test device to the AP. DNS, HTTP, and passive-listener traffic starts appearing on screen, on Serial, and on the port-7777 dashboard immediately.

Use responsibly

This is a traffic-interception tool. Only point it at networks and devices you own, or have explicit written permission to test. Capturing credentials or traffic from anyone else's device without consent is illegal in most jurisdictions.