Skip to content

patali/desky

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Desky

A touchscreen desk puck on the Waveshare ESP32-S3-Touch-AMOLED-1.43 (466×466 round AMOLED), plus a small menu-bar agent for your computer. You move between circular screens with a press-and-hold picker.

The screens:

  1. Desktop — set the paired Mac's microphone gain or speaker volume, toggled by a pill at the top. Absolute and bidirectional: the ring follows changes made on the Mac itself.
  2. Focus — a pomodoro timer. Pick 15 / 30 / 60 minutes and start; pixel sand pours in and fills the circle as time elapses, counting down in the centre. On completion the sand fades to an ember glow behind a breathing DONE, and — since the board has no speaker — the paired desktop's menu bar flashes the news.
  3. Credits — how much Claude Code session and weekly limit is left, with reset countdowns.
  4. Bluetooth — step an Android phone's media volume over BLE HID (down / mute / up).
  5. Settings — Wi-Fi, Bluetooth and per-agent pairing status; includes Change Wi-Fi.
  6. Idle — a clock and a little running dinosaur; takes over after 10 minutes of inactivity and dims the panel to protect the AMOLED. (A running Focus session holds it off.)

The UI came from a Claude Design mockup; the module shell is built so a new screen is one file plus one line in a registry. See docs/ARCHITECTURE.md.

Layout

firmware/     ESP-IDF v5.4 / LVGL 9 project for the puck
agent/        Go menu-bar agent, one per desktop (macOS; Windows compile-only)
docs/         architecture and design notes

The agent

A menu-bar app that exposes this machine's audio and Claude usage to the puck, and surfaces the Focus timer's completion in the menu bar. It advertises over mDNS (_deskcomp._tcp); the puck discovers every agent on the LAN automatically. Install on a Mac with one command:

cd agent && ./install.sh

That builds it, installs it as a login LaunchAgent, and starts it. See agent/README.md for pairing, the wire protocol, and Windows notes.

The firmware

cd firmware
source ./idfenv.sh                 # minimal ESP-IDF env; see the file header for why
idf.py menuconfig                  # → Desk Companion → Timezone (optional; defaults to IST)
idf.py build flash monitor

Requires ESP-IDF v5.4+. Nothing secret goes in the build: Wi-Fi is set from a phone (below) and the puck and agent pair with a 6-digit code (below) — no SSID, password, or shared key to configure or reflash.

Wi-Fi setup

On first boot, or whenever the saved network won't join, the puck hosts its own Wi-Fi network and shows a QR code plus a URL. Scan the QR with a phone to join the puck's setup network, and a captive portal opens a small form; enter your home network's name and password. The puck saves them (in NVS, never in the build) and reboots onto your network. To switch networks later, use Settings → Wi-Fi → Change Wi-Fi, which drops the saved credentials and re-runs setup.

The home password is posted over the puck's local setup network as plain HTTP — the same trust model as every consumer-router setup portal. It is stored in NVS and never logged.

Pairing

The puck and agent authenticate with a per-device key established once by pairing; there is no password to set or reflash. On the puck's Settings page, tap Pair next to an agent — the puck shows a 6-digit code, the Mac pops a dialog for it, you type it in. Both derive a key (X25519 ECDH, with the code folded in) and store it; later connections authenticate silently. Forget and re-pair from either side.

Proportionate for a trusted LAN, not a vault: safe against a passive sniffer and against uninvited devices on the network, but not against a determined active MITM during the pairing exchange (that would need a full PAKE). The agent's --open flag skips pairing for dev.

The two transports

The desktop and the Android phone are controlled in genuinely different ways, and the UI reflects that rather than hiding it:

  • Desktop (over the agent's WebSocket) is absolute and bidirectional — a real ring, with the level pushed back when it changes on the Mac itself.
  • Bluetooth (BLE HID to the phone) is relative and write-only. The puck sends volume up / down / mute but cannot read the phone's volume, so that page is three keys with no gauge — it never shows a confident bar that would silently drift.

Status

Verified on the real board (the CO5300 panel variant — Waveshare ships two under one SKU):

  • Display, touch, Wi-Fi, BLE all working; the phone pairs as "Desky" over BLE HID.
  • All six modules run on hardware. Desktop mic/output control verified live against macOS; the Credits page shows real session/weekly limits; the Focus timer fills, pauses, completes and flashes the Mac's menu bar; the idle clock runs and the panel survives multi-minute soaks with no watchdog resets.
  • Wi-Fi provisioning verified end to end — a fresh puck boots into setup, a phone joins the QR network, and the entered credentials bring it up on the home LAN after a reboot.
  • Pairing verified end to end — a boot self-test confirms the firmware crypto matches the agent's byte-for-byte, and a live pairing plus silent reconnect both work.

Not verified: the Windows agent compiles and vets but has never run on real hardware; its output-volume path and the menu-bar completion flash are untested there. A companion Android app (which would make the phone target absolute and readable instead of relative BLE HID) is intentionally out of scope for now — the capability flags in dc_vol_target.h let it slot in later without touching the UI.

A note on the panel

The AMOLED is mounted at 270°, and its SH8601/CO5300 controller can't rotate in hardware, so every frame is rotated on the CPU before it's flushed — the most expensive thing the puck draws. The rendering is built around that: a fused, cache-blocked rotate+byte-swap, a 60 Hz refresh, the LVGL task pinned to its own core, and per-module animations (the Focus sand, the idle dino) that dirty only small regions rather than repainting the whole circle. Module switches are an instant cut for the same reason — a full-screen slide stutters against the rotation cost.

Credits

The little running dinosaurs on the Idle screen are from arks' Dino Characters pack: https://arks.itch.io/dino-characters. The pixel-art sprite sheets were a perfect fit for this 466×466 panel — small, characterful, and readable at a glance from across a desk. Thank you to the artist for making them available.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages