+++ title = “Acme Camera” description = “” date = 2026-10-13
[extra] reveal_theme = “black” reveal_transition = “slide” +++
Acme Camera
A Proposal
Product Overview
A wireless security system architecture supporting long operational life
Hardware Design
SOC + MCU
–
MCU
ultra-low power attached to motion sensors, lighting, and power control
SOC
attached to camera/storage and spends most of its time in deep sleep
–
MCU Candidates
- nRF54L
- nRF52840
SOC Candidates
- Rockchip RV1106
- Ingenic T41
- Ambarella CV7
Motion
Configurable
PIR
plus
MMWave
Note: Power Efficient Setup, PIR is used to wake the SOC and then mmwave is used to determine if people are present
LED Driver
to support Infrared and Visible Spots for lighting areas for improved visibility
Environmental
- Temperature
- Luminosity
Power Subsystem
- Battery Charge Controller & Gauge
–
What about solar?
Let’s set the expectation that any input power source provides regulated 5V, solar included
–
Solar Charge Controller
- TI bq25504
- e-peas AEM10941 / AEM13920:
Note: nPM1300 does not provide the MPP support for the solar cells so we need an external IC to properly do it
Software Design
Note: mcuctl sets the motion and light mode on the embedded mcu powerctl controls the power domain for the battery charger and reads the fuel gauge portalctl provides the initial configuration UI and subsequent status web portals cameractl reads frames and dispatches them to whatever has been configured configctl is the source of truth that gets configuration requests broker: mqtt, likely mosquitto infctl: inference controller for on-chip npus to load models (bonus)
Boot Plan
Note: Uses an A+B boot that only changes the boot target once we’ve booted successfully and connected to the backend
Component : Broker
- MQTT
Note: on unix socket for the ability to have granular security TODO: why a unix socket over TCP (file permissions / peer credentials per component) TODO: topic layout and which components publish/subscribe
Component : MCUctl
Owns the MCU link: motion sensing and lighting
Note: Talks to MCU and sets/gets the motion detection and light settings
–
Motion
Which sensors watch for motion
enum MotionDetectionMode {
OnlyPIR,
PIRWakesRadar,
OnlyRadar,
Off
}
–
Lighting
When the visible and IR spots come on
enum LightControlMode {
DuskDawn,
Motion{cooldown: u32},
Off
}
Component: Powerctl
Power modes, schedules and PMIC telemetry
Note: Collects power usage data, rundown and fuel gauge from the PMIC
–
Power Modes
Awake on detection, or on a schedule
enum PowerCtlMode {
Detection{cooldown: u32},
Timer{configs: Vec<TimerConfig>},
}
struct TimerConfig {
start_time: u64,
end_time: u64,
schedule_days: HashSet<Weekday>
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum Weekday { M, T, W, R, F, S, U }
–
Power Readings
One sample from the PMIC
enum PowerSource { Battery, External }
struct PowerReading {
source: PowerSource,
timestamp: u64,
voltage_mv: u32,
current_ma: i32, // + charge / - drain
state_of_charge: Option<u8>, // fuel gauge, %
time_to_empty_s: Option<u32>, // rundown estimate
}
Component: Portalctl
First-boot setup, then a status portal
Note: Interactive wireless authentication and cloud configuration
–
Modes
Which portal is up, and setup progress
enum PortalMode {
Setup { ap_ssid: String, ap_psk: String },
Status,
Off,
}
enum ProvisioningState {
AwaitingCredentials,
JoiningNetwork,
Claiming,
Provisioned,
Failed { reason: String },
}
Note: Setup is a soft-AP on first boot / factory reset, Status once on the home network, Off once cloud-managed
–
Credentials
What setup collects from the user
enum WifiSecurity { Open, Wpa2Psk, Wpa3Sae }
struct WifiCredentials {
ssid: String,
security: WifiSecurity,
passphrase: Option<String>,
}
struct CloudClaim {
claim_token: String, // QR / app
display_name: String,
}
Note: claim_token comes from a QR code or the app and is exchanged for device certs
–
Config Requests
Changes sent to configctl via the broker
enum ConfigRequest {
Network(WifiCredentials),
Cloud(CloudClaim),
DisplayName(String),
}
Note: portalctl publishes these over the broker; configctl validates and applies them
–
Status Portal
The status portal’s read-only view
enum BootSlot { A, B }
struct DeviceStatus {
display_name: String,
firmware_slot: BootSlot, // A+B boot
state_of_charge: Option<u8>, // powerctl
motion_mode: MotionDetectionMode, // mcuctl
cloud_connected: bool,
}
Note: Read-only view assembled from the other components via the broker
Component: Cameractl
Note: Reads frames from the camera and decides to do further processing depending on config
Component: Configctl
Note: Source of truth for various configurations set from the portal
- Network configuration
- Display Name
- Cloud Configuration
- On-Device Processing Configuration
–
Component: Infctl
Note: This one is – because it’s a bonus one that we may not get into Configures on-device inference using NPU hardware available in modern camera SOCs
Metrics
- Performance within the configured power envelope
leading to insights for better defaults
- Battery life across the fleet
Note: Every config choice (motion mode, light mode, schedules) trades responsiveness for battery, so the metrics are about how well a device does inside the envelope its owner picked, and fleet data tells us which defaults to ship
–
Targets
| Metric | Target |
|---|---|
| Battery life, default config | ≥ 3 months |
| Standby draw, SOC asleep | ≤ 100 µA |
| Motion → first frame | < 1 s p95 |
| Motion → notification | < 5 s p95 |
| False wakes (no person) | < 20% |
| Crash-free wakes | > 99.9% |
| OTA success, first try | > 99.5% |
Note: Assumptions: ~6000 mAh pack, 20 events/day, 10 s clips, ~400 mA while the SOC, Wi-Fi and IR are up Each event costs roughly 1.5 mAh, so events dominate and standby has to stay tiny Wake latency is the user-visible one: a late first frame means a clip of someone’s back False wakes are PIR triggers the radar doesn’t confirm, they cost battery for nothing A/B boot means a failed OTA rolls back rather than bricks, the target is about not needing to
–
Slicing the Fleet
- Firmware version
- Motion / light / power mode
- Battery vs solar vs external
- Temperature and Wi-Fi signal
Note: Averages hide problems: a battery life drop on one firmware, or in cold climates, or on weak Wi-Fi These are the dimensions every metric gets broken down by Firmware cohorts gate staged OTA rollouts: a regression in one cohort halts the rollout
Instrumentation
What we measure, how it leaves the device, and how often
Note: Combines environmental and fuel gauge data with what each component already knows
–
Signals
| Source | Signals |
|---|---|
| powerctl | voltage, current, charge |
| mcuctl | wake reason, radar confirm |
| cameractl | wake → frame latency |
| network | connect time, RSSI |
| system | fw, boot slot, crashes |
| environment | temperature, light |
Note: powerctl also reports time to empty, mcuctl how long the lights were on, cameractl clip length, network upload time, system the reboot reason Each component publishes its own metrics to the broker, so nothing new has to reach into another process Energy per wake comes from the fuel gauge before and after, which ties every event to its battery cost
–
Collection
- Components publish metrics to the broker
- A telemetry agent aggregates and buffers to flash
- MCU logs hourly samples while the SOC sleeps
- Uploads ride along when Wi-Fi is already up
Note: Never wake the radio just to report, telemetry should be well under 1% of the energy budget The MCU keeps a small ring buffer of sleep samples (battery, temperature, wake counts) that the SOC drains on its next wake Buffered to flash so a dead battery or lost network doesn’t lose history Upload is MQTT over TLS to the backend, the same connection the device already holds for events
–
Reporting Cadence
| When | What |
|---|---|
| Every event | wake summary: reason, latency, energy |
| Daily heartbeat | counters, battery state, sleep samples |
| After a crash or rollback | coredump and reboot reason |
| On demand | debug logs, per device |
Note: The heartbeat goes out even with no events, so a silent device is a signal on its own Coredumps are symbolicated server side, the device only ships the raw dump On demand logs are for support cases, off by default to save power and bandwidth
–
Fleet Dashboards
- Projected battery life per device vs target
- Latency and false wake rate per cohort
- Alerts: projected life below target, missed heartbeat, crash spikes after OTA
Note: Projected life comes from the measured drain rate, so we can flag a device months before it dies Missed heartbeat for 48 h means dead, offline or stuck, worth an owner notification A crash spike in a new firmware cohort pauses the rollout automatically