Files
t-embed-insecam-widget/README.md
T
Juli d92a2e53cc On-device menu, persistent settings and 12 favourite slots
Hold the knob to open a six-row menu: save/remove the playing camera as
a favourite (12 NVS slots), play or forget favourites, choose the
source (random insecam or favourites), time per camera, listing,
screen and ring brightness, caption, and reboot into Wi-Fi setup.
Settings live in NVS and override the Kconfig first-boot defaults.
Camera URLs are stored with their COUNTER placeholder so favourites get
a fresh cache-buster on every visit.
2026-09-17 00:57:02 +02:00

86 lines
4.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# insecam desk widget (LilyGO T-Embed)
A little window onto the world's public webcams. The T-Embed's 320×170 IPS
screen shows a random camera from [insecam.org](http://www.insecam.org) — as
live MJPEG video where the camera streams it — and moves on to the next one
every 20 seconds. The knob steps through cameras, the LED ring drains as the
countdown runs. Everything happens on the ESP32-S3; no bridge or PC needed.
insecam lists cameras that are reachable on the public internet without any
password. The widget only ever does what a browser on that site does: fetch a
listing page, then fetch the camera's own snapshot/stream URL.
## Hardware
Either LilyGO T-Embed. The firmware tells them apart at boot by probing the I2C
bus (ES7210 on the 2022 board, BQ27220/BQ25896/PN532 on the CC1101 board) and
picks the matching pin map from `main/board.c`. Nothing beyond the board is
needed: ESP32-S3R8, 16 MB flash, 8 MB PSRAM, ST7789 170×320, EC11 encoder,
APA102 (×7) or WS2812 (×8) ring.
## Build and flash
Needs ESP-IDF 5.5 (on this machine: `. ~/esp/idf55-env.sh`).
```sh
idf.py set-target esp32s3
idf.py build
idf.py -p /dev/ttyACM0 flash monitor
```
The partition table keeps `nvs` where the ESPHome build that shipped on this
unit had it (`0xf90000`), so stored Wi-Fi credentials survive re-flashing.
First-boot defaults (interval, listing, brightness, forced variant) live under
`idf.py menuconfig` → *insecam desk widget*; the on-device menu overrides them
and stores its choices in NVS.
## First boot / Wi-Fi
With no saved network the widget opens the open hotspot **insecam-setup**;
join it with a phone and the captive portal (or `http://192.168.4.1`) asks for
your Wi-Fi. Holding the knob down while the widget starts forces the portal.
## Controls
| input | action |
| --- | --- |
| turn clockwise | next camera |
| turn counter-clockwise | previous camera (history of 24) |
| click | pause / resume the countdown (the live stream keeps playing) |
| double-click | hide / show the caption |
| hold | open the menu (hold again, or *Close*, to leave it) |
In the menu: turn to move, click to select or toggle, click a number to edit it
(turn changes it, click confirms), hold to go back. Entries:
* **Save as favourite / Remove from favourites** — the camera that was playing;
saving asks for one of the 12 slots (overwrites it).
* **Favourites** — the 12 slots; click one to play it. A saved camera shows a
yellow *FAV n* marker while it plays.
* **Source** — *Random* draws from insecam, *Favourites* cycles the saved slots.
* **Time per camera**, **Listing** (mixed / top rated / newest), **Screen
brightness**, **Ring brightness**, **Caption**.
* **Forget a favourite**, **Wi-Fi setup** (reboots into the hotspot), and the
board name with its IP.
The menu closes by itself after 45 s without input.
## How it works
* `insecam.c` fetches one listing page (`/en/byrating/` or `/en/bynew/`, random
page) — six cameras per request, so insecam sees one hit every two minutes.
* `httpstream.c` is a small raw-socket HTTP client: `esp_http_client` gives up on
bodies without `Content-Length`, which is exactly what MJPEG cameras send.
* `jpegframe.c` pulls complete JPEG files out of the byte stream by walking the
marker structure (so an EXIF thumbnail's EOI can't truncate a frame).
* `jpegdec.c` decodes with Espressif's `esp_new_jpeg`, scaling inside the
decoder to the smallest multiple-of-8 size that still covers the screen, then
bilinear-resamples the remainder to a full-bleed 320×170 (≈100 ms for a
1280×800 frame, so MJPEG streams play at a few fps).
* `slideshow.c` runs the state machine: connect, play frames until the interval
elapses or the knob moves, skip dead cameras, keep history for "previous".
* `ui.c` is LVGL 9 via `esp_lvgl_port`: a canvas for the frame, a translucent
caption bar, a LIVE/PAUSED badge, a hairline progress bar and a six-row menu page.
* `settings.c` keeps the settings blob and the 12 favourite slots in NVS
(namespace `widget`); `menu.c` is the knob-driven menu that edits them.