Skip to content

Add macOS host support#6

Merged
HermannBjorgvin merged 1 commit into
mainfrom
macos-support
May 14, 2026
Merged

Add macOS host support#6
HermannBjorgvin merged 1 commit into
mainfrom
macos-support

Conversation

@HermannBjorgvin

Copy link
Copy Markdown
Owner

Summary

Cherry-picks the macOS-specific pieces from #5 so they can land independently while the 1.8" board port iterates separately.

Credits Chris Davidson (@lorddavidson) as the original author of all the macOS code — thanks!

What's included

  • daemon/claude_usage_daemon.py — Python rewrite of the bash daemon using bleak (CoreBluetooth on macOS). Mirrors the bash daemon's behavior: 60 s poll, refresh-on-notify, exponential reconnect backoff, MAC/UUID caching.
  • daemon/com.user.claude-usage-daemon.plist — LaunchAgent template (Mac equivalent of the systemd unit).
  • install-mac.sh — installs to ~/Library/LaunchAgents/, sets up a Python venv with bleak + httpx, primes Bluetooth permission interactively.
  • flash-mac.sh — wraps pio run -t upload with /dev/cu.usbmodem* autodetect.
  • README.md — split into "macOS installation" and "Linux installation" sections with parallel Flash / Pair / Install subsections.

What's not included

The 1.8" board firmware port from #5 is intentionally left out — those button-wiring questions are still being discussed on the original PR and don't block macOS support.

What changed vs #5

  • flash-mac.sh simplified: dropped the -e <env> flag and the waveshare_amoled_18 default (no second env exists on this branch).
  • .gitignore additions trimmed to the macOS-relevant entries (daemon/.venv/, __pycache__/, *.pyc).
  • README rearranged so macOS and Linux are sibling H2s rather than the macOS section sitting under a Linux-flavored Prerequisites header.

Test plan

  • Linux side untouched at runtime — install.sh / flash.sh / bash daemon / systemd unit unchanged
  • macOS install end-to-end (flash → pair → install daemon → see usage update on device) — needs a Mac to verify

Cherry-picks the macOS-specific pieces from PR #5: Python daemon using
bleak (CoreBluetooth backend), LaunchAgent template, flash-mac.sh, and
install-mac.sh. Token is read from the macOS Keychain ("Claude
Code-credentials" service); Linux behavior is unchanged. README split
into "macOS installation" and "Linux installation" sections with
parallel Flash / Pair / Install subsections.

Co-Authored-By: Chris Davidson <36679917+lorddavidson@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@HermannBjorgvin
HermannBjorgvin merged commit 2774ae8 into main May 14, 2026
@HermannBjorgvin
HermannBjorgvin deleted the macos-support branch May 14, 2026 18:40
carroarmato0 pushed a commit to carroarmato0/Clawdmeter that referenced this pull request May 16, 2026
@georgetasioulis

georgetasioulis commented May 17, 2026

Copy link
Copy Markdown
Contributor

Thanks for adding macOS host support, I just received my ESP32-S3-Touch-AMOLED-2.16 device today and can provide some real-world feedback with:

macOS install end-to-end (flash → pair → install daemon → see usage update on device) — needs a Mac to verify
% ./flash-mac.sh 
=== Flashing Clawdmeter ===
Port: /dev/cu.usbmodem2101

Processing waveshare_amoled_216 (platform: https://github.com/pioarduino/platform-espressif32/releases/download/55.03.38-1/platform-espressif32.zip; board: esp32-s3-devkitc-1; framework: arduino)
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Library Manager: Installing moononournation/GFX Library for Arduino @ ^1.5.6
Unpacking  [####################################]  100%
Library Manager: GFX Library for Arduino@1.6.5 has been installed!
Library Manager: Installing lewisxhe/SensorLib @ ^0.2.6
Unpacking  [####################################]  100%
Library Manager: SensorLib@0.2.6 has been installed!
Library Manager: Installing lewisxhe/XPowersLib @ ^0.2.7
Unpacking  [####################################]  100%
Library Manager: XPowersLib@0.2.9 has been installed!
Library Manager: Installing lvgl/lvgl @ ^9.2.0
Unpacking  [####################################]  100%
Library Manager: lvgl@9.5.0 has been installed!
Library Manager: Installing bblanchon/ArduinoJson @ ^7.0.0
Unpacking  [####################################]  100%
Library Manager: ArduinoJson@7.4.3 has been installed!
Library Manager: Installing h2zero/NimBLE-Arduino @ ^2.1.1
Unpacking  [####################################]  100%
Library Manager: NimBLE-Arduino@2.5.0 has been installed!
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/espressif32/esp32-s3-devkitc-1.html
PLATFORM: Espressif 32 (55.3.38) > Espressif ESP32-S3-DevKitC-1-N8 (8 MB QD, No PSRAM)
HARDWARE: ESP32S3 240MHz, 320KB RAM, 8MB Flash
DEBUG: Current (esp-builtin) On-board (esp-builtin) External (cmsis-dap, esp-bridge, esp-prog, esp-prog-2, iot-bus-jtag, jlink, minimodule, olimex-arm-usb-ocd, olimex-arm-usb-ocd-h, olimex-arm-usb-tiny-h, olimex-jtag-tiny, tumpa)
PACKAGES: 
 - contrib-piohome @ 3.4.4 
 - framework-arduinoespressif32 @ 3.3.8 
 - framework-arduinoespressif32-libs @ 5.5.4+sha.735507283d 
 - tool-esptoolpy @ 5.2.0 
 - tool-xtensa-esp-elf-gdb @ 16.3.0+20250913 
 - toolchain-xtensa-esp-elf @ 14.2.0+20260121
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 50 compatible libraries
Scanning dependencies...
Dependency Graph
|-- GFX Library for Arduino @ 1.6.5
|-- SensorLib @ 0.2.6
|-- XPowersLib @ 0.2.9
|-- lvgl @ 9.5.0
|-- ArduinoJson @ 7.4.3
|-- NimBLE-Arduino @ 2.5.0
|-- Wire @ 3.3.8
Building in release mode
Compiling .pio/build/waveshare_amoled_216/lib488/SPI/SPI.cpp.o
[...]

during compiling I got a bunch of warnings like:

[...]
In file included from .pio/libdeps/waveshare_amoled_216/lvgl/src/core/../misc/../lv_conf_kconfig.h:15,
                 from .pio/libdeps/waveshare_amoled_216/lvgl/src/core/../misc/../lv_conf_internal.h:50,
                 from .pio/libdeps/waveshare_amoled_216/lvgl/src/core/../misc/lv_area.h:16,
                 from .pio/libdeps/waveshare_amoled_216/lvgl/src/core/../misc/lv_area_private.h:17,
                 from .pio/libdeps/waveshare_amoled_216/lvgl/src/core/lv_obj_pos.c:9:
/Users/myusername/.platformio/packages/framework-arduinoespressif32-libs/esp32s3/qio_opi/include/sdkconfig.h:593:9: warning: "CONFIG_BT_NIMBLE_ROLE_CENTRAL" redefined
  593 | #define CONFIG_BT_NIMBLE_ROLE_CENTRAL 1
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<command-line>: note: this is the location of the previous definition
/Users/myusername/.platformio/packages/framework-arduinoespressif32-libs/esp32s3/qio_opi/include/sdkconfig.h:596:9: warning: "CONFIG_BT_NIMBLE_ROLE_OBSERVER" redefined
  596 | #define CONFIG_BT_NIMBLE_ROLE_OBSERVER 1
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<command-line>: note: this is the location of the previous definition
/Users/myusername/.platformio/packages/framework-arduinoespressif32-libs/esp32s3/qio_opi/include/sdkconfig.h:612:9: warning: "CONFIG_BT_NIMBLE_MAX_CONNECTIONS" redefined
  612 | #define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3
[...]

but in the end it flashed successfully:

[...]
                            Memory Type Usage Summary                             
┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Memory Type/Section ┃ Used [bytes] ┃ Used [%] ┃ Remain [bytes] ┃ Total [bytes] ┃
┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ Flash Code          │       681456 │          │                │               │
│    .text            │       681456 │          │                │               │
│ Flash Data          │       503540 │          │                │               │
│    .rodata          │       503284 │          │                │               │
│    .appdesc         │          256 │          │                │               │
│ DIRAM               │       200739 │    58.74 │         141021 │        341760 │
│    .text            │        96479 │    28.23 │                │               │
│    .bss             │        83360 │    24.39 │                │               │
│    .data            │        19872 │     5.81 │                │               │
│    .vectors         │         1028 │      0.3 │                │               │
└─────────────────────┴──────────────┴──────────┴────────────────┴───────────────┘
Total image size: 1302375 bytes (.bin may be padded larger)
Note: The reported total sizes may be smaller than those in the technical reference manual due to reserved memory and application configuration. The total flash size available for the application is not included by default, as it cannot be reliably determined due to the presence of other data like the bootloader, partition table, and application partition size.
Retrieving maximum program size .pio/build/waveshare_amoled_216/firmware.elf
Checking size .pio/build/waveshare_amoled_216/firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [===       ]  31.5% (used 103232 bytes from 327680 bytes)
Flash: [====      ]  39.0% (used 1302119 bytes from 3342336 bytes)
Building .pio/build/waveshare_amoled_216/firmware.bin
esptool v5.2.0
Creating ESP32-S3 image...
Merged 2 ELF sections.
Successfully created ESP32-S3 image.
Creating binary "firmware.factory.bin" with:
    Offset   | File
 -  0x0000   | bootloader.bin
 -  0x8000   | partitions.bin
 -  0xe000   | boot_app0.bin
 -  0x10000  | firmware.bin
Successfully created combined binary image.
Configuring upload protocol...
AVAILABLE: cmsis-dap, esp-bridge, esp-builtin, esp-prog, esp-prog-2, espota, esptool, iot-bus-jtag, jlink, minimodule, olimex-arm-usb-ocd, olimex-arm-usb-ocd-h, olimex-arm-usb-tiny-h, olimex-jtag-tiny, tumpa
CURRENT: upload_protocol = esptool
Looking for upload port...
Uploading .pio/build/waveshare_amoled_216/firmware.bin
esptool v5.2.0
Connected to ESP32-S3 on /dev/cu.usbmodem2101:
Chip type:          ESP32-S3 (QFN56) (revision v0.2)
Features:           Wi-Fi, BT 5 (LE), Dual Core + LP Core, 240MHz, Embedded PSRAM 8MB (AP_3v3)
Crystal frequency:  40MHz
USB mode:           USB-Serial/JTAG
MAC:                1c:db:d4:xx:xx:xx

Stub flasher running.
Changing baud rate to 921600...
Changed.

Configuring flash size...
Auto-detected flash size: 16MB
Flash parameters set to 0x024f.
SHA digest in image updated.
Flash will be erased from 0x00000000 to 0x00004fff...
Compressed 19984 bytes to 13013...
Wrote 19984 bytes (13013 compressed) at 0x00000000 in 0.2 seconds (695.2 kbit/s).
Hash of data verified.
Flash will be erased from 0x00008000 to 0x00008fff...
Compressed 3072 bytes to 146...
Wrote 3072 bytes (146 compressed) at 0x00008000 in 0.0 seconds (545.4 kbit/s).
Hash of data verified.
Flash will be erased from 0x0000e000 to 0x0000ffff...
Compressed 8192 bytes to 47...
Wrote 8192 bytes (47 compressed) at 0x0000e000 in 0.1 seconds (963.2 kbit/s).
Hash of data verified.
Flash will be erased from 0x00010000 to 0x0014dfff...
Compressed 1302528 bytes to 666623...
Wrote 1302528 bytes (666623 compressed) at 0x00010000 in 7.6 seconds (1367.3 kbit/s).
Hash of data verified.

Hard resetting via RTS pin...
======================================================================================== [SUCCESS] Took 173.46 seconds ========================================================================================

=== Done ===

After that I tried to find the "Clawdmeter" device via System Settings → Bluetooth, but it wouldn't show up. Turns out it wasn't necessary. I then proceeded with ./install-mac.sh granted permissions for Bluetooth ons step 4/5 (Ctrl+C) and after a few seconds I could see in claude-usage-daemon.out.log that it connected:

[12:42:19] Scanning for 'Claude Controller' (8.0s)...
[12:42:27] Found: 830Exxxx-2EC9-xxxx-xxxx-6A46DFBAxxxx
[12:42:27] Connecting to 830Exxxx-2EC9-xxxx-xxxx-6A46DFBAxxxx...
[12:43:01] Connected
[12:43:01] Refresh requested by device
[12:43:05] Sending: {"s":0,"sr":0,"w":0,"wr":0,"st":"unknown","ok":true}
[12:43:05] Sending: {"s":0,"sr":0,"w":0,"wr":0,"st":"unknown","ok":true}

(I hadn't used claude at all yet today - as soon as I launched claude it started reporting usage).

Glad to provide more information if needed.

@HermannBjorgvin

Copy link
Copy Markdown
Owner Author

Thanks for the real world example!

Glad to hear you managed to get it working!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants