blooth-cli is a Go-based macOS CLI that sends structured Agent actions to an ESP32-C3 e-paper device over BLE.
docs/ product and technical specs
cli/ Go CLI source
firmware/ ESP32-C3 PlatformIO project (GxEPD2_420 e-paper driver)
fixtures/ sample action payloads
skills/ Cursor/Claude skill for Agent integration
- Download the CLI and flash the firmware
- Power on the device — it shows
SChat-HUD-XXXX(last 4 hex of MAC) on screen - Install the Skill in Cursor / Claude
- Ask the agent: "Show this on my desk display"
# Apple Silicon
curl -L https://github.com/qinwenshi/blooth-cli/releases/latest/download/blooth-cli-darwin-arm64 -o blooth-cli
chmod +x blooth-cli && xattr -dr com.apple.quarantine blooth-cli
sudo mv blooth-cli /usr/local/bin/
# Intel Mac
curl -L https://github.com/qinwenshi/blooth-cli/releases/latest/download/blooth-cli-darwin-amd64 -o blooth-cli
chmod +x blooth-cli && xattr -dr com.apple.quarantine blooth-cli
sudo mv blooth-cli /usr/local/bin/
xattr -dr com.apple.quarantineremoves the macOS Gatekeeper flag on unsigned binaries downloaded from the internet.
blooth-cli device scan # find nearby BLE devices
blooth-cli device use <uuid> # save as default device
blooth-cli device default # show saved default
blooth-cli device forget # clear saved default
blooth-cli device status [--device <uuid>]
blooth-cli device capabilities [--device <uuid>]
blooth-cli device clear [--device <uuid>]
blooth-cli doctor ble # diagnose BLE permission
blooth-cli render text-card [--device <uuid>] --title "..." --body "..." --footer "..."
blooth-cli render status-card [--device <uuid>] --status info --title "..." --summary "..."
blooth-cli render image-card [--device <uuid>] --image <file|url|-> --layout top|right|full --title "..."
blooth-cli wait button [--device <uuid>] --buttons confirm,menu --timeout-ms 120000
blooth-cli action run --file fixtures/actions/text-card.json
blooth-cli versionBLE environment variables (required for all BLE commands):
export BLOOTH_SERVICE_UUID=7a1f0001-7f3b-4b2a-8e5a-3c613a11d001
export BLOOTH_STATUS_CAPS_UUID=7a1f1002-7f3b-4b2a-8e5a-3c613a11d001
export BLOOTH_CONTROL_UUID=7a1f0002-7f3b-4b2a-8e5a-3c613a11d001
export BLOOTH_DATA_IN_UUID=7a1f0003-7f3b-4b2a-8e5a-3c613a11d001
export BLOOTH_DATA_OUT_UUID=7a1f0004-7f3b-4b2a-8e5a-3c613a11d001
export BLOOTH_EVENT_UUID=7a1f0005-7f3b-4b2a-8e5a-3c613a11d001A Cursor/Claude skill is included in skills/blooth-epd/. It lets an Agent display content and wait for button input without knowing BLE details.
See skills/blooth-epd/README.md for installation and usage.
| Item | Spec |
|---|---|
| MCU | ESP32-C3 (RISC-V, 160 MHz, 400 KB SRAM) |
| Module | ESP32C3-FN4 CrossAir (4 MB Flash) |
| USB | CDC on boot (no USB-Serial chip needed) |
| BLE | Bluetooth 5.0 LE via NimBLE stack |
| Item | Spec |
|---|---|
| Model | Waveshare 4.2" e-Paper HAT |
| Resolution | 400 × 300 px, Black & White |
| Driver IC | SSD1683 (GxEPD2_420) |
| Interface | SPI |
| Partial refresh | ✅ supported (fast mode, max 6 before full refresh) |
SPI pin wiring:
| Signal | GPIO |
|---|---|
| CS (chip select) | 3 |
| DC (data/cmd) | 4 |
| RST (reset) | 5 |
| SCK (clock) | 6 |
| MOSI (data) | 7 |
| BUSY | 8 |
| Button | GPIO | Logic | BLE name | Status |
|---|---|---|---|---|
| Confirm | 21 | Active-LOW, INPUT_PULLUP | confirm |
✅ working |
| Menu (BOOT) | 9 | Active-LOW, INPUT_PULLUP | menu |
✅ working |
| Back | 1 | Active-HIGH, pull-down | back |
❌ not populated on this unit |
| Next | 20 | Active-HIGH, INPUT_PULLDOWN | next |
❌ not populated on this unit |
Service UUID: 7a1f0001-7f3b-4b2a-8e5a-3c613a11d001
| Characteristic | UUID suffix | Properties | Description |
|---|---|---|---|
| control | 7a1f0002 |
WRITE, WRITE_NR | Job header (action + job_id + payload_len) |
| data_in | 7a1f0003 |
WRITE, WRITE_NR | Chunked payload bytes |
| data_out | 7a1f0004 |
NOTIFY, READ | ACK / DONE / ERROR responses |
| event_notify | 7a1f0005 |
NOTIFY | Button press events |
| status_caps | 7a1f1002 |
READ | Device state + capabilities JSON |
- Device name:
SChat-HUD-XXXX(XXXX = last 4 hex digits of MAC) — unique per device, no scan conflicts - Display persistence: last rendered content saved to NVS, restored on reboot
- Word wrap: long body text auto-wraps at word boundaries (Latin) or character boundaries (CJK)
- Chinese support: U8g2
wqy16_t_gb2312font (full GB2312: 6763 CJK + all punctuation) - Error screen: firmware shows ERROR header + message on display for all protocol errors
Download blooth-epd-firmware-vX.Y.Z.bin from the latest release:
pip install esptool
# Put board in flash mode: hold BOOT button, press RST, release BOOT
esptool.py --chip esp32c3 --port /dev/cu.usbmodem* \
--baud 460800 write_flash 0x0 blooth-epd-firmware-vX.Y.Z.binAfter flashing, press RST to reboot. The display shows the device name (SChat-HUD-XXXX).
# Requires PlatformIO
cd firmware
pio run --target uploadmake build # compile CLI binary → ./blooth-cli
make fmt # go fmt ./cli/...
make run-scan # stub device scan (no hardware needed)
# With hardware:
make run-capabilities DEVICE=<uuid>
make run-render DEVICE=<uuid> TITLE="Hello" BODY="Line 1\nLine 2"
make run-wait-button DEVICE=<uuid> TITLE="Continue?" BODY="Press confirm"Stub mode (no hardware):
export BLOOTH_TRANSPORT=stub
make run-scan- CLI: macOS 12+, CoreBluetooth
- Build from source: Go 1.22+
- Firmware build: PlatformIO
- Firmware flash: esptool (
pip install esptool)
