Home network monitoring system running on an ESP32-S2 mini (WEMOS). It performs periodic ARP scans, keeps track of devices, and sends notifications when new or blacklisted devices appear on the network.
- ARP scanner — periodic network scan (configurable interval 1-60 min)
- MAC lists — whitelist, blacklist and unknown devices, with descriptions
- Telegram notifications — aggregated message per scan
- Generic webhook — HTTP POST to ntfy.sh, Discord, Slack, IFTTT, Home Assistant, Gotify, or any custom server
- Backup / restore — export and import MAC lists as JSON
- Bulk move — move all MACs from one list to another
- Vendor lookup — manufacturer name from MAC OUI (~60 vendors)
- Web UI — responsive interface with session authentication
- Log viewer — in-RAM buffer for WARNING and ERROR messages
- mDNS —
network-watcher.local - Factory reset — via button or web UI
Four reasons that make NetworkWatcher a practical, self-contained alternative to a Raspberry Pi–based monitor — with no cloud dependency and near-zero running cost.
- Runs on a WEMOS S2 Mini (ESP32-S2) board that costs just a few euros, so the whole setup stays affordable.
- Draws a fraction of the power required by a mini-PC, and can stay on 24/7 with a negligible impact on your electricity bill.
- ARP scans run as often as once per minute, so new devices are spotted almost immediately.
- Telegram and webhook notifications reach your phone in real time when an unknown or blacklisted device appears.
- The on-board LED gives instant visual feedback without opening the web UI: solid for a blacklist match, blinking for unknown devices.
- A built-in web interface (password-protected) is reachable from any PC or phone at
http://network-watcher.local— no command line required. - Everything runs on the chip: your data, history and MAC addresses stay local and are never sent to third-party servers, except for the notifications you explicitly configure.
- JSON backup and restore let you save your device lists, move them to another board, or recover them after a failure in seconds.
- OUI vendor lookup with a built-in database of ~60 manufacturers helps you identify what just joined the network (Apple, Samsung, Tuya, and more).
- Generic webhooks integrate with Discord, Slack, Home Assistant, ntfy.sh, Gotify, or any custom endpoint — perfect for existing home-automation setups.
A gallery of the user interface sections, showing the configuration and monitoring pages in miniature.
| Component | Details |
|---|---|
| Board | ESP32-S2 mini (WEMOS) |
| Flash | 4 MB |
| PSRAM | 2 MB |
| LED | GPIO 15, active high |
| Button | GPIO 0 (BOOT), active low |
| Console | USB CDC (native USB-OTG) |
- ESP-IDF v6.1
- Python 3.12+
- Tera Term (or PuTTY) for the serial console on Windows
cd NetworkWatcher
idf.py buildThe board exposes two different COM ports depending on the mode:
powershell
Get-CimInstance Win32_SerialPort | Select-Object DeviceID, Name
| Mode | Buttons | Port (example) |
|---|---|---|
| Download (flashing) | Hold BOOT, press and release RST, then release BOOT | COM3 (UART bridge) |
| Console (USB CDC) | Press RST only | COM6 (native USB CDC) |
⚠️ The exact port numbers vary from system to system. ReplaceCOM3and
COM6in the commands below with the ports found on your machine.
- Hold BOOT, press and release RST, then release BOOT (download mode)
- Run (replace
COM3with your download port):
idf.py -p COM3 flash- Press RST only
- Open Tera Term on the console port (example
COM6), 115200, 8N1,
auto-reconnect enabled
The native USB CDC console requires Tera Term or PuTTY.
idf.py monitorhas known issues on Windows with USB CDC.
The port may change number after a flash: re-check with the command above.
- After flashing, the device starts in Access Point mode:
- SSID:
Network Watcher - Password:
12345678
- SSID:
- Connect to the AP and open
http://192.168.4.1 - Login with admin / admin
- Change the password when prompted (mandatory)
- Configure your home WiFi from the Setup page
- (Optional) Configure Telegram bot and/or generic webhook for notifications
After the first-time setup, the device is reachable at:
| Method | Address | Notes |
|---|---|---|
| mDNS | http://network-watcher.local |
Works on macOS, iOS, Linux. On Windows may require Bonjour |
| Direct IP | http://<device-ip> |
Check your router's DHCP leases |
The device's IP in STA mode is shown at boot on the serial console:
(xxxx) main: IP assigned: 192.168.1.174
You can also find it in the router's admin page under DHCP leases or
Connected devices, looking for the hostname network-watcher or the
board's MAC address (shown at boot).
By default the IP may change across reboots. To always reach the device at the same address:
- Open your router's admin page (usually
http://192.168.1.1) - Find the DHCP reservation / Static DHCP / Address reservation section
- Add a new entry with:
- MAC address: the board's MAC (shown at boot:
wifi:mac: xx:xx:xx:xx:xx:xx) - IP address: any free address in your LAN (e.g.
192.168.1.174)
- MAC address: the board's MAC (shown at boot:
- Save and reboot the device
From that point on, the router always assigns the same IP to NetworkWatcher.
If the device cannot connect to your home WiFi, it stays in Access Point mode:
- SSID:
Network Watcher - Password:
12345678 - Address:
http://192.168.4.1
To force AP mode manually, hold BOOT for 5 seconds.
Once configured, the device connects to your home WiFi and starts scanning
every 5 minutes (configurable). When new or blacklisted devices appear, a
notification is sent via the configured channels.
The web UI is available at:
http://192.168.4.1in AP modehttp://network-watcher.localin STA mode (mDNS)http://<device-ip>in STA mode
| Action | Effect |
|---|---|
| Short press | LED pulse |
| 5 seconds long press | Reset WiFi credentials, restart in AP mode |
| 3 rapid clicks + 1 within 5s | Factory reset (all settings) |
| Pattern | Meaning |
|---|---|
| Off | All lists clean |
| Slow blink | Unknown devices present |
| Solid on | Blacklist match detected |
| Fast blink | WiFi connecting |
| Heartbeat | Connected and idle |
| Error (3 blinks) | Error |
| Quick flash | Scan in progress |
NetworkWatcher/
├── CMakeLists.txt
├── sdkconfig.defaults
├── partitions.csv
├── LICENSE
├── NOTICE
├── DISCLAIMER.md
├── README.md
├── CHANGELOG.md
├── main/
│ └── main.c
├── components/
│ ├── config/ Shared constants and types
│ ├── storage/ NVS + LittleFS wrapper
│ ├── led/ LED patterns
│ ├── button/ Button event detection
│ ├── wifi_manager/ AP/STA management
│ ├── auth/ Session auth (SHA-256 + salt)
│ ├── mdns_service/ mDNS announcement
│ ├── web_server/ HTTP server + REST API
│ ├── arp_scanner/ ARP scan task
│ ├── mac_list/ MAC list management
│ ├── settings/ Application settings
│ ├── time_service/ SNTP sync
│ ├── telegram/ Telegram Bot API
│ ├── webhook/ Generic HTTP webhook
│ ├── oui_lookup/ MAC vendor lookup
│ ├── log_buffer/ In-RAM log ring buffer
│ └── esp_littlefs/ LittleFS library (local)
├── data/
└── www/ Web UI (LittleFS image)
| Name | Type | Size | Purpose |
|---|---|---|---|
| nvs | data/nvs | 24 KB | WiFi, admin, Telegram, Webhook credentials |
| phy_init | data/phy | 4 KB | RF calibration |
| factory | app/factory | 1.5 MB | Firmware |
| storage | data/spiffs | 1 MB | LittleFS: web UI + MAC lists |
| backup | data/0x40 | 256 KB | (unused, reserved) |
- MAC lists are stored in LittleFS. A full
idf.py flashwill overwrite the
storage partition and erase them. Use the Backup feature in the UI
before flashing a new full image, or useidf.py app-flashto update only
the firmware. - Telegram and webhook credentials are stored in NVS, not in the storage
partition, so they survive a full flash. - The Log viewer buffer is volatile: it is cleared on every reboot.
Before using NetworkWatcher, please read the DISCLAIMER.
NetworkWatcher is intended for monitoring your own network. Scanning
networks you do not own or are not authorized to monitor is illegal in
many jurisdictions.
Licensed under the Apache License, Version 2.0.
See LICENSE for the full text and NOTICE for
third-party attributions.
damarant
Repository: https://github.com/damarant/NetworkWatcher
