This is the companion post to Idle Signal Farmer. That post tells the story. This one has the data.
Token Economics of Embedded C
The codebase is 11,068 lines of C. Embedded C is expensive in tokens.
Per-Statement Comparison
| C Code | Tokens | Python Equivalent | Tokens |
|---|---|---|---|
static volatile uint32_t | 5 | count: int | 3 |
event->disc.addr.val[0] | 8 | event.disc.addr[0] | 6 |
memcpy(&dst, &src, sizeof(src)) | 10 | dst = src.copy() | 5 |
esp_err_t init(void *ctx) | 8 | def init(ctx): | 4 |
LVGL’s Verbose Naming
LVGL function names are particularly verbose. The lv_obj_ prefix on every call adds overhead:
| LVGL Function | Tokens | Breakdown |
|---|---|---|
lv_obj_set_flex_flow() | 6 | lv _obj _set _f lex _flow |
lv_obj_set_style_pad_top() | 6 | lv _obj _set _style _pad _top |
lv_obj_set_style_border_color() | 6 | lv _obj _set _style _border _color |
A single call like lv_obj_set_style_pad_top(label, 5, 0); costs ~12 tokens. With hundreds of UI calls in sf_ui.c, this adds up fast. A 1k LoC file could blow past the 25k token limit, triggering compactions much faster.
The Bottom Line
C uses roughly 50% more tokens than Python for equivalent semantics:
- Type declarations (
static,const,volatile) - Pointer syntax (
*,&,->) - Memory operations (
sizeof,memcpy) - Preprocessor directives (
#include,#define) - Verbose library naming (LVGL, ESP-IDF)
Whether this matters depends on your context limits. For a project this size, it meant more context compactions and continuations.
BLE Protocol Findings
From 68 minutes of scanning in an apartment. Just sitting there, passively listening.
| Metric | Value |
|---|---|
| Total advertisements | 51,067 |
| Unique devices | 472 |
| Scan rate | 751/minute |
| Devices with names | 22% |
751 advertisements per minute. That’s 12.5 per second. The radio spectrum is noisy.
Manufacturer Breakdown
- Apple: 66%
- Other/Unknown: 21%
- Samsung: 10%
- Custom: 3%
Two-thirds of traffic was Apple devices. iPhones, AirPods, MacBooks, Apple Watches. All broadcasting constantly.
Samsung’s Dual Personality
The neighbor’s robot vacuum broadcasts two completely different packet formats:
- 8-byte pattern (with device name):
37 36 33 44 04 02 08 0C - 24-byte pattern (no name): Contains the MAC address embedded backwards in bytes 15-20
This kind of protocol quirk only shows up when you stare at real field data.
Stability by Manufacturer
| Manufacturer | RSSI Std Dev | Notes |
|---|---|---|
| Apple | 14.7 dBm | Wildly variable |
| 3.98 dBm | Rock solid |
Apple’s aggressive privacy measures (MAC rotation, variable TX power) make their devices terrible for proximity detection. Which is certainly the point.
Autonomous Work Patterns
Total autonomous execution: 19.4 hours across 248 loops.
Two modes emerged:
Orchestrate Loops (Delegated Work)
The /orchestrate command triggers implementor→reviewer workflows:
| Active Time | Subagents | Date | Task |
|---|---|---|---|
| 117 min | 23 | Dec 22 | Initial project scaffolding from Obsidian |
| 45 min | 18 | Dec 23 | BLE improvement plan implementation |
| 44 min | 16 | Dec 24 | Idle Signal Farmer component creation |
| 43 min | 15 | Dec 23 | Build system and display fixes |
| 28 min | 8 | Dec 24 | UI phase work |
Total: 6.4 hours across 15 orchestrate runs, spawning 221 subagents.
Orchestrate loops show 0 direct edits from the main agent. Work gets delegated to implementor and reviewer subagents. The main agent just coordinates.
Direct Implementation Loops
Claude editing files directly, no delegation:
| Active Time | Edits | Trigger |
|---|---|---|
| 41 min | 9 | Clang optimization research |
| 34 min | 11 | Session continuation - debugging |
| 22 min | 56 | Expert subagent review + fixes |
| 21 min | 0 | BLE log analysis (research only) |
| 18 min | 7 | Commit + fix remaining issues |
Total: 13 hours across 233 loops, 775 direct file edits.
The 21-minute loop with 0 edits is interesting: pure research, reading log files and drawing conclusions. No code changes, but necessary work.