<!-- SPDX-License-Identifier: CC-BY-4.0 -->
# Where a node's battery actually goes

Run 2026-10-07 with [tools/power_budget.py](../../tools/power_budget.py).
Reproduce with `make power`.

The question: if soil moisture does not need updating often, what does slowing it
down save? The intuition is that a less necessary sensor should sample less and the
battery lasts longer. **Mostly wrong, and wrong in a way worth knowing**, because
the saving is real but it is not where it looks.

## What was modelled

A node with one or more channels, each a datastream with its own sample interval and
its own tolerance for staleness. Three costs: the quiescent sleep draw paid every
second, a per-reading cost to wake and settle a sensor, and a per-message cost of the
radio at full current for the whole time-on-air. Airtime comes from the same
Semtech formula as the Q5 measurement, at EU868 DR0 (SF12) with CBOR packed arrays.

Currents are documented assumptions, not measurements: an SX1276-class radio at
14 dBm (120 mA), an STM32L-class MCU (8 mA awake, 10 µA asleep), a capacitive soil
probe (5 mA for 100 ms), one 18650 at 2600 mAh. The *ratios* below survive being
wrong about any of those by a factor; the absolute day counts do not, and a real
figure needs an electrical spec the project does not yet have.

## Result

| Configuration | Transmit | Sampling | Sleep | Total | Days |
| --- | --- | --- | --- | --- | --- |
| A — soil only, hourly | 1.46 (85%) | 0.009 (0.5%) | 0.24 (14%) | 1.71 | **1518** |
| B — soil only, sampled 5 min, sent hourly | 4.50 (93%) | 0.10 (2%) | 0.24 (5%) | 4.85 | 537 |
| C — trough only, 5 min latency | 17.57 (98%) | 0.10 (0.6%) | 0.24 (1.3%) | 17.92 | 145 |
| D — both on one node, soil hourly | 19.15 (98%) | 0.11 (0.6%) | 0.24 (1.2%) | 19.50 | 133 |
| E — both on one node, soil also 5 min | 19.15 (98%) | 0.21 (1.1%) | 0.24 (1.2%) | 19.59 | **133** |

mAh/day.

## Three findings

**Reading a sensor is a rounding error.** Sampling is 0.5–2% of consumption in every
configuration. Slowing a sensor down *to spare the sensor* saves nothing measurable,
so "soil moisture is less necessary, sample it less" does not buy what it seems to.

**But sampling more often is not free, and the reason matters.** A → B nearly
triples consumption for twelve readings an hour instead of one — and almost none of
the increase is the probe. Twelve readings no longer fit one DR0 message, so the node
sends two. **You pay for what you send**, and sample rate matters only through the
data it generates. That is why the intuition half-works: slowing a sensor usually
slows its reporting too, and the reporting is the bill.

**Co-location dominates everything else.** The same hourly soil probe lasts about
1518 days on its own node and about 133 days on a node a five-minute trough float
keeps awake — eleven times, which nothing in the sample-rate or encoding space comes
close to. D → E is the counterpart: once a node is already transmitting every five
minutes, sampling soil twelve times more often costs **0.5%**, because the extra
reading rides in a message that was going anyway.

So the lever the maintainer was reaching for exists, and it is **placement**, not rate. A
sleepy sensor conserves power by being on a node nothing urgent keeps awake. Put a
soil probe on the trough node and its own sample rate stops mattering almost
entirely.

## What changed in the spec

`expected_interval` was doing two jobs — how often a series is sampled, and
implicitly how fast it must arrive. They are different numbers and only the second
one spends the battery, so §3.1 now also carries **`max_latency`**: how stale a
reading may be when it reaches the gateway.

§5.3.1 states the consequence: **a node's transmit interval is the tightest
`max_latency` among the series it carries.** Latency is not a per-series cost; it is
imposed on everything sharing the radio. And the installation rule follows — do not
put a loose-latency series on a node whose tightest latency is short, unless the
wiring makes it unavoidable.

## The tension it exposed

§4.2 runs health checks in the foundation, §5.1 says the node applies nothing, and
the power model says you pay for what you send. Together: **detection resolution
equals transmitted resolution.** Catching a stuck probe within the hour means sending
hourly-resolution data, even for a series a farmer only reads weekly. On a shared
node that is nearly free; on a sleepy node it is most of the budget.

A node could send a compact aggregate instead — sample count, min, max, and the
length of the current unchanged run are enough to keep a flatline and a rate
excursion detectable in a handful of bytes. But then the node has computed something,
which §5.1 forbids and §3.2's "raw is exactly what the node sent" complicates. That
is **open-questions Q20**, with a recommendation (a third datastream `kind`) and the
reason it wants settling before node firmware rather than after.

## Sensitivity

A faster data rate is worth more than any configuration choice here: DR5 instead of
DR0 multiplies a trough node's life by more than five, because the cost is airtime.
And sleep current only matters on a sleepy node — a careless 150 µA halves node A's
life and barely touches node C's, which is already dominated by its radio.
