acme-camera: assumptions, build & OS, security, event anatomy, power budget, risks; components to appendix

10112a6 · Gabriel Morell · 2026-10-13 19:19

5 files +330 -121

Files changed

modified content/presentations/acme-camera.md
+327 −121
@@ -18,6 +18,27 @@ reveal_transition = "slide"
18 18
19 19 A wireless security system architecture supporting long operational life
20 20
21 +Note:
22 +The pitch: months on a charge, a person-confirmed alert in seconds, and fleet data that proves both
23 +
24 +---
25 +
26 +# Assumptions
27 +
28 +| Area | Assumption |
29 +|---|---|
30 +| Battery | ~6000 mAh Li-ion pack |
31 +| External power | regulated 5 V, solar included |
32 +| Activity | ~20 events/day, ~10 s clips |
33 +| Network | 2.4 GHz Wi-Fi to a home AP |
34 +| Recording | event clips, live view on demand |
35 +| Backend | exists, we own the device side |
36 +
37 +Note:
38 +These drive every number later in the deck, so happy to rerun the math if Acme's are different
39 +Solar is just another 5 V source, the panel's own controller handles MPPT
40 +No continuous recording, a battery camera can't afford it
41 +
21 42 ---
22 43
23 44 # Hardware Design
@@ -132,6 +153,39 @@ infctl: inference controller for on-chip npus to load models (bonus)
132 153
133 154 ---
134 155
156 +# Build & OS
157 +
158 +- Yocto on the SOC vendor's BSP
159 +- Read-only rootfs in A/B slots, writable `/data`
160 +- Signed images, built reproducibly in CI
161 +- Rust daemons as sandboxed systemd units
162 +
163 +Note:
164 +Yocto for layers, an SDK for app developers, and license manifests / SBOM for free; Buildroot if the chosen vendor only supports that
165 +Kernel trimmed to what the board needs, suspend-to-RAM tuned so a wake is a resume, not a boot
166 +Read-only rootfs means an update is a whole-slot swap, nothing drifts between devices
167 +/data holds config, credentials and the clip buffer, and is the only thing a factory reset wipes
168 +
169 +--
170 +
171 +## Process Model
172 +
173 +| Unit | Can reach |
174 +|---|---|
175 +| broker | `/run/broker.sock` |
176 +| mcuctl | UART to the MCU |
177 +| powerctl | PMIC over I²C |
178 +| cameractl | sensor, ISP, clip buffer |
179 +| portalctl | soft-AP, HTTP |
180 +| configctl | `/data/config` |
181 +
182 +Note:
183 +Each unit runs as its own user with ProtectSystem=strict and DeviceAllow for only its hardware
184 +The broker checks the connecting uid (SO_PEERCRED) against per-topic ACLs, so portalctl can request config but never write it
185 +A crashed daemon is restarted by systemd and counted in telemetry
186 +
187 +---
188 +
135 189 # Boot Plan
136 190
137 191 <iframe src="https://diogramos.neiam.co/embed/C0ICOZGl94HAS7JuVHy8OjPN?mode=frame"
@@ -143,6 +197,267 @@ Uses an A+B boot that only changes the boot target once we've booted successfull
143 197
144 198 ---
145 199
200 +# Security
201 +
202 +Trust from boot to cloud
203 +
204 +Note:
205 +Home security buyers ask about this first, and a compromised camera is a camera pointed into someone's home
206 +
207 +--
208 +
209 +## Layers
210 +
211 +| Layer | Measure |
212 +|---|---|
213 +| Boot | secure boot, signed A/B images |
214 +| Keys | secure element / OTP, never in rootfs |
215 +| Storage | encrypted `/data` and clips |
216 +| Processes | per-service users, sandboxed units |
217 +| Broker | per-topic ACLs by peer uid |
218 +| Setup | soft-AP only while unprovisioned |
219 +| Cloud | per-device certs, mutual TLS |
220 +
221 +Note:
222 +The claim token from setup is exchanged for a per-device cert, nothing shared across the fleet
223 +Telemetry carries metrics only, no video and nothing personal beyond the display name
224 +Factory reset wipes /data and revokes the device cert server side
225 +
226 +---
227 +
228 +# Components
229 +
230 +| Component | Role |
231 +|---|---|
232 +| <a href="#/broker">broker</a> | MQTT on a Unix socket |
233 +| <a href="#/mcuctl">mcuctl</a> | motion and lights via the MCU |
234 +| <a href="#/powerctl">powerctl</a> | power modes, PMIC telemetry |
235 +| <a href="#/portalctl">portalctl</a> | setup and status portals |
236 +| <a href="#/cameractl">cameractl</a> | frames to storage and cloud |
237 +| <a href="#/configctl">configctl</a> | source of truth for settings |
238 +| <a href="#/infctl">infctl</a> | on-device inference (bonus) |
239 +
240 +Note:
241 +Details and types for each are in the appendix, the links jump there
242 +
243 +---
244 +
245 +# Anatomy of an Event
246 +
247 +| Step | t |
248 +|---|---|
249 +| PIR trips, MCU wakes the SOC | 0 |
250 +| SOC resumes from suspend | ~200 ms |
251 +| Radar confirms a person | ~300 ms |
252 +| First frame from the sensor | ~700 ms |
253 +| Wi-Fi reconnects | ~1.5 s |
254 +| Notification sent | ~2 s |
255 +| Clip uploaded, back to sleep | ~15 s |
256 +
257 +Note:
258 +The SOC resumes in parallel with the radar check, if the radar doesn't confirm it goes straight back to sleep
259 +Capture starts before the network is up, frames go to the local clip buffer first, so the first frame isn't waiting on Wi-Fi
260 +~15 s at ~400 mA is the ~1.5 mAh per event the power budget uses
261 +Keeping Wi-Fi associated while asleep would cut 1.5 s but costs standby current, a config trade-off
262 +
263 +---
264 +
265 +# Metrics
266 +
267 +- Performance within the configured power envelope
268 +
269 +_leading to insights for better defaults_ <!-- .element: class="fragment" -->
270 +
271 +- Battery life across the fleet <!-- .element: class="fragment" -->
272 +
273 +Note:
274 +Every config choice (motion mode, light mode, schedules) trades responsiveness for battery,
275 +so the metrics are about how well a device does inside the envelope its owner picked,
276 +and fleet data tells us which defaults to ship
277 +
278 +--
279 +
280 +## Targets
281 +
282 +| Metric | Target |
283 +|---|---|
284 +| Battery life, default config | ≥ 3 months |
285 +| Standby draw, SOC asleep | ≤ 100 µA |
286 +| Motion → first frame | < 1 s p95 |
287 +| Motion → notification | < 5 s p95 |
288 +| False wakes (no person) | < 20% |
289 +| Crash-free wakes | > 99.9% |
290 +| OTA success, first try | > 99.5% |
291 +
292 +Note:
293 +Assumptions: ~6000 mAh pack, 20 events/day, 10 s clips, ~400 mA while the SOC, Wi-Fi and IR are up
294 +Each event costs roughly 1.5 mAh, so events dominate and standby has to stay tiny
295 +Wake latency is the user-visible one: a late first frame means a clip of someone's back
296 +False wakes are PIR triggers the radar doesn't confirm, they cost battery for nothing
297 +A/B boot means a failed OTA rolls back rather than bricks, the target is about not needing to
298 +
299 +--
300 +
301 +## Power Budget
302 +
303 +| Item | mAh/day |
304 +|---|---|
305 +| Standby, 100 µA × 24 h | 2.4 |
306 +| Events, 20 × 1.5 mAh | 30 |
307 +| Heartbeat + telemetry | ~0.5 |
308 +| Self-discharge, ~2%/month | ~4 |
309 +| **Total** | **~37** |
310 +| **6000 mAh × 80% usable** | **~130 days** |
311 +
312 +Note:
313 +Events are ~80% of the budget, so the false wake target and clip length matter more than standby
314 +40 events/day halves it to ~65 days, cold weather takes another chunk off usable capacity
315 +This is the table the fleet data should confirm or correct
316 +
317 +--
318 +
319 +## Slicing the Fleet
320 +
321 +- Firmware version
322 +- Motion / light / power mode
323 +- Battery vs solar vs external
324 +- Temperature and Wi-Fi signal
325 +
326 +Note:
327 +Averages hide problems: a battery life drop on one firmware, or in cold climates, or on weak Wi-Fi
328 +These are the dimensions every metric gets broken down by
329 +Firmware cohorts gate staged OTA rollouts: a regression in one cohort halts the rollout
330 +
331 +---
332 +
333 +# Instrumentation
334 +
335 +What we measure, how it leaves the device, and how often
336 +
337 +Note:
338 +Combines environmental and fuel gauge data with what each component already knows
339 +
340 +--
341 +
342 +## Signals
343 +
344 +| Source | Signals |
345 +|---|---|
346 +| powerctl | voltage, current, charge |
347 +| mcuctl | wake reason, radar confirm |
348 +| cameractl | wake → frame latency |
349 +| network | connect time, RSSI |
350 +| system | fw, boot slot, crashes |
351 +| environment | temperature, light |
352 +
353 +Note:
354 +powerctl also reports time to empty, mcuctl how long the lights were on, cameractl clip length, network upload time, system the reboot reason
355 +Each component publishes its own metrics to the broker, so nothing new has to reach into another process
356 +Energy per wake comes from the fuel gauge before and after, which ties every event to its battery cost
357 +
358 +--
359 +
360 +## Collection
361 +
362 +- Components publish metrics to the broker
363 +- A telemetry agent aggregates and buffers to flash
364 +- MCU logs hourly samples while the SOC sleeps
365 +- Uploads ride along when Wi-Fi is already up
366 +
367 +Note:
368 +Never wake the radio just to report, telemetry should be well under 1% of the energy budget
369 +The MCU keeps a small ring buffer of sleep samples (battery, temperature, wake counts) that the SOC drains on its next wake
370 +Buffered to flash so a dead battery or lost network doesn't lose history
371 +Upload is MQTT over TLS to the backend, the same connection the device already holds for events
372 +
373 +--
374 +
375 +## Reporting Cadence
376 +
377 +| When | What |
378 +|---|---|
379 +| Every event | wake summary: reason, latency, energy |
380 +| Daily heartbeat | counters, battery state, sleep samples |
381 +| After a crash or rollback | coredump and reboot reason |
382 +| On demand | debug logs, per device |
383 +
384 +Note:
385 +The heartbeat goes out even with no events, so a silent device is a signal on its own
386 +Coredumps are symbolicated server side, the device only ships the raw dump
387 +On demand logs are for support cases, off by default to save power and bandwidth
388 +
389 +--
390 +
391 +## Fleet Dashboards
392 +
393 +- Projected battery life per device vs target
394 +- Latency and false wake rate per cohort
395 +- Alerts: projected life below target, missed heartbeat, crash spikes after OTA
396 +
397 +Note:
398 +Projected life comes from the measured drain rate, so we can flag a device months before it dies
399 +Missed heartbeat for 48 h means dead, offline or stuck, worth an owner notification
400 +A crash spike in a new firmware cohort pauses the rollout automatically
401 +
402 +---
403 +
404 +# Risks
405 +
406 +| Risk | Mitigation |
407 +|---|---|
408 +| SOC vendor BSP quality | evaluate two SOCs early |
409 +| Battery life misses target | measure from the first prototype |
410 +| Wake latency | resume, don't boot |
411 +| Weak Wi-Fi | local clip buffer, RSSI telemetry |
412 +| RF certification | pre-certified Wi-Fi module |
413 +| Fleet regressions | staged OTA, A/B rollback |
414 +
415 +Note:
416 +BSP quality is the biggest schedule risk for camera SOCs, kernel age and ISP blobs vary a lot between vendors
417 +Battery life is the product, so it's measured on every build, not at the end
418 +
419 +--
420 +
421 +## Trade-offs
422 +
423 +| Choice | Gains | Costs |
424 +|---|---|---|
425 +| PIR wakes radar | fewer false alerts | ~100 ms |
426 +| Radar only | catches still people | standby draw |
427 +| Lights on motion | color night video | battery |
428 +| Longer clips | more context | battery, upload |
429 +| Wi-Fi kept up | faster live view | standby draw |
430 +| On-device inference | smarter alerts | NPU power, BOM |
431 +
432 +Note:
433 +Every row is a battery-life knob, which is why the metrics are per configuration
434 +The defaults are a product decision, fleet data tells us whether they were the right ones
435 +
436 +--
437 +
438 +## Delivery Plan
439 +
440 +| Phase | Outcome |
441 +|---|---|
442 +| EVT, ~2 mo | dev kits, BSP up, power measured |
443 +| DVT, ~3 mo | custom board, all daemons, OTA |
444 +| PVT, ~3 mo | certification, ~100 device pilot |
445 +| Launch | staged rollout, dashboards live |
446 +
447 +Note:
448 +Rough durations, mostly gated by the board spins and certification
449 +Telemetry ships in EVT firmware, the pilot fleet is where the targets get validated
450 +
451 +---
452 +
453 +# Appendix
454 +
455 +Component details and types
456 +
457 +---
458 +
459 +<!-- .slide: id="broker" -->
460 +
146 461 # Component : Broker
147 462
148 463 - MQTT
@@ -153,6 +468,8 @@ TODO: topic layout and which components publish/subscribe
153 468
154 469 ---
155 470
471 +<!-- .slide: id="mcuctl" -->
472 +
156 473 # Component : MCUctl
157 474
158 475 Owns the MCU link: motion sensing and lighting
@@ -191,6 +508,8 @@ enum LightControlMode {
191 508
192 509 ---
193 510
511 +<!-- .slide: id="powerctl" -->
512 +
194 513 # Component: Powerctl
195 514
196 515 Power modes, schedules and PMIC telemetry
@@ -241,6 +560,8 @@ struct PowerReading {
241 560
242 561 ---
243 562
563 +<!-- .slide: id="portalctl" -->
564 +
244 565 # Component: Portalctl
245 566
246 567 First-boot setup, then a status portal
@@ -337,6 +658,8 @@ Read-only view assembled from the other components via the broker
337 658
338 659 ---
339 660
661 +<!-- .slide: id="cameractl" -->
662 +
340 663 # Component: Cameractl
341 664
342 665 Note:
@@ -344,6 +667,8 @@ Reads frames from the camera and decides to do further processing depending on c
344 667
345 668 ---
346 669
670 +<!-- .slide: id="configctl" -->
671 +
347 672 # Component: Configctl
348 673
349 674 Note:
@@ -355,129 +680,10 @@ Source of truth for various configurations set from the portal
355 680
356 681 --
357 682
683 +<!-- .slide: id="infctl" -->
684 +
358 685 # Component: Infctl
359 686
360 687 Note:
361 688 This one is -- because it's a bonus one that we may not get into
362 689 Configures on-device inference using NPU hardware available in modern camera SOCs
363 −
364 −---
365 −
366 −# Metrics
367 −
368 −- Performance within the configured power envelope
369 −
370 −_leading to insights for better defaults_ <!-- .element: class="fragment" -->
371 −
372 −- Battery life across the fleet <!-- .element: class="fragment" -->
373 −
374 −Note:
375 −Every config choice (motion mode, light mode, schedules) trades responsiveness for battery,
376 −so the metrics are about how well a device does inside the envelope its owner picked,
377 −and fleet data tells us which defaults to ship
378 −
379 −--
380 −
381 −## Targets
382 −
383 −| Metric | Target |
384 −|---|---|
385 −| Battery life, default config | ≥ 3 months |
386 −| Standby draw, SOC asleep | ≤ 100 µA |
387 −| Motion → first frame | < 1 s p95 |
388 −| Motion → notification | < 5 s p95 |
389 −| False wakes (no person) | < 20% |
390 −| Crash-free wakes | > 99.9% |
391 −| OTA success, first try | > 99.5% |
392 −
393 −Note:
394 −Assumptions: ~6000 mAh pack, 20 events/day, 10 s clips, ~400 mA while the SOC, Wi-Fi and IR are up
395 −Each event costs roughly 1.5 mAh, so events dominate and standby has to stay tiny
396 −Wake latency is the user-visible one: a late first frame means a clip of someone's back
397 −False wakes are PIR triggers the radar doesn't confirm, they cost battery for nothing
398 −A/B boot means a failed OTA rolls back rather than bricks, the target is about not needing to
399 −
400 −--
401 −
402 −## Slicing the Fleet
403 −
404 −- Firmware version
405 −- Motion / light / power mode
406 −- Battery vs solar vs external
407 −- Temperature and Wi-Fi signal
408 −
409 −Note:
410 −Averages hide problems: a battery life drop on one firmware, or in cold climates, or on weak Wi-Fi
411 −These are the dimensions every metric gets broken down by
412 −Firmware cohorts gate staged OTA rollouts: a regression in one cohort halts the rollout
413 −
414 −---
415 −
416 −# Instrumentation
417 −
418 −What we measure, how it leaves the device, and how often
419 −
420 −Note:
421 −Combines environmental and fuel gauge data with what each component already knows
422 −
423 −--
424 −
425 −## Signals
426 −
427 −| Source | Signals |
428 −|---|---|
429 −| powerctl | voltage, current, charge |
430 −| mcuctl | wake reason, radar confirm |
431 −| cameractl | wake → frame latency |
432 −| network | connect time, RSSI |
433 −| system | fw, boot slot, crashes |
434 −| environment | temperature, light |
435 −
436 −Note:
437 −powerctl also reports time to empty, mcuctl how long the lights were on, cameractl clip length, network upload time, system the reboot reason
438 −Each component publishes its own metrics to the broker, so nothing new has to reach into another process
439 −Energy per wake comes from the fuel gauge before and after, which ties every event to its battery cost
440 −
441 −--
442 −
443 −## Collection
444 −
445 −- Components publish metrics to the broker
446 −- A telemetry agent aggregates and buffers to flash
447 −- MCU logs hourly samples while the SOC sleeps
448 −- Uploads ride along when Wi-Fi is already up
449 −
450 −Note:
451 −Never wake the radio just to report, telemetry should be well under 1% of the energy budget
452 −The MCU keeps a small ring buffer of sleep samples (battery, temperature, wake counts) that the SOC drains on its next wake
453 −Buffered to flash so a dead battery or lost network doesn't lose history
454 −Upload is MQTT over TLS to the backend, the same connection the device already holds for events
455 −
456 −--
457 −
458 −## Reporting Cadence
459 −
460 −| When | What |
461 −|---|---|
462 −| Every event | wake summary: reason, latency, energy |
463 −| Daily heartbeat | counters, battery state, sleep samples |
464 −| After a crash or rollback | coredump and reboot reason |
465 −| On demand | debug logs, per device |
466 −
467 −Note:
468 −The heartbeat goes out even with no events, so a silent device is a signal on its own
469 −Coredumps are symbolicated server side, the device only ships the raw dump
470 −On demand logs are for support cases, off by default to save power and bandwidth
471 −
472 −--
473 −
474 −## Fleet Dashboards
475 −
476 −- Projected battery life per device vs target
477 −- Latency and false wake rate per cohort
478 −- Alerts: projected life below target, missed heartbeat, crash spikes after OTA
479 −
480 −Note:
481 −Projected life comes from the measured drain rate, so we can flag a device months before it dies
482 −Missed heartbeat for 48 h means dead, offline or stuck, worth an owner notification
483 −A crash spike in a new firmware cohort pauses the rollout automatically
modified sass/reveal-neiam.scss
+3 −0
@@ -58,7 +58,10 @@
58 58 }
59 59 }
60 60
61 + // Tables carry the dense slides (targets, budgets, risks), so set them a
62 + // little smaller than body text to fit 6-7 rows under a heading.
61 63 table {
64 + font-size: 0.75em;
62 65 th { color: var(--accent); }
63 66 th, td { border-bottom: var(--border) solid var(--base-300); }
64 67 }

Parents: 663be22