Skip to content

10.P — Worked Problems: Embedded Systems

Problem 1 — Thermistor front end

Design a temperature measurement for 0 to 60 °C using a 10 kΩ NTC thermistor with \beta = 3950, read by a 12-bit ADC with a 3.3 V reference. Choose the series resistor and find the resolution.

Solution

Thermistor resistance at the extremes:

R(0°\text{C})=10{,}000\exp\left[3950\left(\frac{1}{273.15}-\frac{1}{298.15}\right)\right]=10{,}000\,e^{1.212}=33.6\ \text{k}\Omega

R(60°\text{C})=10{,}000\exp\left[3950\left(\frac{1}{333.15}-\frac{1}{298.15}\right)\right]=10{,}000\,e^{-1.392}=2.49\ \text{k}\Omega

Series resistor. For maximum sensitivity at the middle of the range, choose R_s equal to the thermistor's resistance at the midpoint, 30 °C:

R(30°\text{C})=10{,}000\,e^{-0.132}=8.76\ \text{k}\Omega \;\to\; \text{use } 8.2\ \text{k}\Omega

Output voltages with the thermistor on the top and the fixed resistor to ground:

V=3.3\times\frac{8200}{8200+R_{th}}

TemperatureR_{th}VADC code
0 °C33.6 kΩ0.647 V803
30 °C8.76 kΩ1.594 V1978
60 °C2.49 kΩ2.487 V3086

Codes span 803 to 3086 — 2283 of the 4096 available, so 56% of the range is used. Acceptable; a better match would need an op-amp stage, and the gain is not worth it here.

Resolution at each end:

\text{at 0 °C: } \frac{60}{2283}\times\frac{30-0}{1978-803}=\ldots

more directly, the local slope near 0 °C is (1978-803)/30 = 39.2 codes per degree, so 0.026 °C per code.

Near 60 °C the slope is (3086-1978)/30=36.9 codes/°C, so 0.027 °C per code.

Remarkably uniform, which is what choosing R_s at the midpoint achieves — it linearises the divider over the range.

Self-heating check:

P=\frac{V^2}{R}=\frac{1.594^2}{8760}=0.29\ \text{mW}

With a thermal resistance of 250 °C/W in still air:

\Delta T=0.29\times10^{-3}\times250=0.073\ \text{°C}

Under a tenth of a degree — acceptable, but note it is three times the resolution. For higher accuracy, switch the divider on only during measurement, which reduces the average dissipation by the duty cycle.

Linearisation in software. The relationship is exponential, so either use the beta equation directly, or — for a microcontroller with no floating point — a 64-entry lookup table with linear interpolation, which gives well under 0.1 °C of error.

Problem 2 — Strain gauge amplifier

A load cell has a full bridge of 350 Ω gauges, 2 mV/V sensitivity, rated 100 kg. Excitation is 5 V and the ADC range is 0 to 3.3 V. Design the amplifier and assess the resolution.

Solution

Full-scale output:

V_{out}=2\ \text{mV/V}\times5\ \text{V}=10\ \text{mV}

Gain required to reach 3.3 V, leaving a little headroom — target 3.0 V:

A=\frac{3.0}{0.010}=300

Common-mode voltage: the bridge midpoints sit at 2.5 V, so the amplifier must reject 2.5 V while amplifying 10 mV.

CMRR needed for the common-mode error to stay below 0.01% of full scale, which is 1 µV referred to the input:

\text{CMRR}=\frac{2.5}{10^{-6}}=2.5\times10^6=128\ \text{dB}

This rules out a difference amplifier entirely — with 0.01% resistors it reaches about 100 dB. An instrumentation amplifier is required, and a good monolithic part achieves 130 dB at a gain of 300.

Offset requirement. At a gain of 300, an input offset V_{OS} produces 300V_{OS} at the output. For that to stay under 0.01% of 3.0 V, which is 0.3 mV:

V_{OS}\lt1\ \mu\text{V}

A chopper-stabilised amplifier, since general-purpose parts are 100 times worse. Or accept a larger offset and calibrate it out — a tare measurement at zero load removes offset entirely, and every weighing scale does exactly this.

Resolution with a 12-bit ADC:

\frac{100\ \text{kg}}{4096}\times\frac{4096}{3.0/3.3}\ldots

more simply, 3.0 V of the 3.3 V range is 3724 codes for 100 kg:

\frac{100{,}000\ \text{g}}{3724}=26.9\ \text{g per code}

Adequate for a bathroom scale, useless for a laboratory balance.

With a 24-bit sigma-delta ADC (Chapter 3.5), designed for exactly this application:

\frac{100{,}000}{16.7\times10^6}=0.006\ \text{g per code}

But the noise floor is the real limit. The bridge's own Johnson noise (Chapter 7.1) over a 10 Hz bandwidth:

v_n=\sqrt{4kTRB}=\sqrt{4\times1.38\times10^{-23}\times300\times350\times10}=0.24\ \mu\text{V}

\text{equivalent}=\frac{0.24\times10^{-6}}{10\times10^{-3}}\times100\ \text{kg}=2.4\ \text{g}

So the honest resolution is a few grams, not milligrams, and the extra ADC bits are keeping the quantisation error below the noise rather than revealing anything new. Reducing the bandwidth to 1 Hz by averaging improves it by \sqrt{10} to 0.76 g — which is why a precision balance takes several seconds to settle.

Problem 3 — Sensor fusion

An accelerometer has 0.02 g of noise; a gyroscope drifts at 0.02°/s. Design a complementary filter for tilt, sampling at 100 Hz.

Solution

Accelerometer tilt noise. For small angles, 0.02 g corresponds to

\theta_{noise}=\arcsin(0.02)=1.15°

Noisy but with no drift.

Gyroscope drift:

0.02°/\text{s}\times60=1.2°\ \text{per minute}, \qquad 72°\ \text{per hour}

Smooth but useless over minutes.

Choose the crossover where the two errors are equal. The gyroscope reaches 1.15° of error after

t=\frac{1.15}{0.02}=57\ \text{s}

So a crossover time constant of about 1 second is far more conservative than necessary, and it is the usual choice because it also rejects genuine linear acceleration, which corrupts the accelerometer's tilt estimate far more than its noise does.

Filter coefficient:

\tau=\frac{\alpha\Delta t}{1-\alpha} \;\Rightarrow\; \alpha=\frac{\tau}{\tau+\Delta t}=\frac{1}{1+0.01}=0.990

\theta_n=0.990(\theta_{n-1}+\omega\Delta t)+0.010\,\theta_{accel}

Three lines of code, and it gives the gyroscope's smoothness above 0.16 Hz and the accelerometer's absolute reference below it.

Residual noise:

\theta_{noise,filtered}=1.15°\times\sqrt{\frac{\Delta t}{2\tau}}=1.15\times\sqrt{0.005}=0.081°

Fourteen times better than the raw accelerometer, with no drift.

Residual drift: the accelerometer pulls the estimate back with a 1 s time constant, so the steady-state gyroscope drift contributes

0.02°/\text{s}\times1\ \text{s}=0.02°

Negligible.

When it fails, and this is the important part. Under sustained linear acceleration — a vehicle accelerating, a drone translating — the accelerometer no longer measures gravity alone and reports a false tilt. The filter follows it.

The fix is to reject accelerometer readings whose magnitude differs from 1 g:

\text{if } \left||\mathbf a|-1g\right| \gt 0.1g, \ \text{use gyroscope only}

During those periods the estimate drifts, but at 0.02°/s that is tolerable for the few seconds an acceleration typically lasts. A Kalman filter does this more elegantly by adjusting its gain automatically from the measurement's estimated variance, which is the same idea with the threshold made continuous.

Problem 4 — Edge versus cloud

A machine monitor samples vibration at 20 kHz on three axes. Compare transmitting raw data with edge feature extraction, over Wi-Fi and over LoRaWAN.

Solution

Raw data rate:

20{,}000\times3\times2\ \text{bytes}=120\ \text{kB/s}=10.4\ \text{GB/day}

Over Wi-Fi at 20 Mbit/s effective: 960 kbit/s needed, so 5% of the link — feasible on mains power.

Energy: the Wi-Fi radio would run continuously at about 400 mW. On a battery, this is not a design; it is a mains-powered device.

Over LoRaWAN at 5 kbit/s maximum: 2000 times too slow. Not possible at all.

Edge extraction. Compute a 4096-point FFT once per second per axis, and extract:

  • Overall RMS
  • Peak amplitude
  • Amplitude at 1×, 2×, 3× rotation speed
  • Bearing defect frequencies (four of them)
  • Kurtosis, which indicates impacting

Twelve values per axis, 4 bytes each, once per minute:

12\times3\times4=144\ \text{bytes/minute}=207\ \text{kB/day}

A reduction of 50,000 times.

Over LoRaWAN: 144 bytes exceeds the maximum payload at higher spreading factors, so send every 5 minutes with 12 values per axis, or reduce to the six most diagnostic values.

Airtime at SF9, 125 kHz, for a 51-byte payload: about 185 ms.

\text{Duty cycle}=\frac{0.185}{300}=0.06\% \ ✓

Well within the 1% European limit.

Energy:

0.185\ \text{s}\times120\ \text{mW}=22\ \text{mJ per transmission}

288\ \text{transmissions/day}\times22\ \text{mJ}=6.3\ \text{J/day}

Plus the processing. A 4096-point FFT takes about 200,000 operations; three axes once per second is 600,000 operations per second. On a Cortex-M4 at 80 MHz drawing 10 mA at 3 V, that occupies perhaps 2% of the processor:

0.02\times30\ \text{mW}\times86{,}400=52\ \text{J/day}

Processing costs eight times more than transmission.

And that is worth pausing on, because it inverts the usual assumption. The FFT is expensive enough here that it changes the answer — reducing the analysis rate to once every 10 seconds cuts processing to 5 J/day and total consumption to 11 J/day.

\text{life from a 3 Ah lithium cell (32 kJ)}=\frac{32{,}000}{11}=2900\ \text{days}=8\ \text{years}

Which makes the design viable. The lesson is that "process at the edge" is right, and the processing rate is itself a design parameter that must be budgeted, not assumed free.

Problem 5 — Battery life for a connected sensor

A door sensor reports open and close events, averaging 20 per day, plus an hourly heartbeat. BLE transmission is 30 µJ, sleep current 1.5 µA at 3 V. Battery is a CR2032 at 225 mAh. Find the life.

Solution

Events per day: 20+24=44.

Transmission energy:

44\times30\ \mu\text{J}=1.32\ \text{mJ/day}

Sleep energy:

1.5\ \mu\text{A}\times3\ \text{V}\times86{,}400\ \text{s}=0.389\ \text{J/day}

Sleep dominates by a factor of 295.

Total: 0.390 J/day.

Battery energy:

225\ \text{mAh}\times3\ \text{V}=0.675\ \text{Wh}=2430\ \text{J}

\text{life}=\frac{2430}{0.390}=6230\ \text{days}=17\ \text{years}

Beyond the cell's ten-year shelf life, so the design is done and self-discharge will be the limit.

Now the reality checks that change the answer.

Coin cell pulse capability. A CR2032's internal resistance rises from about 10 Ω when new to over 100 Ω near the end of life. A BLE transmission draws 10 mA for a few milliseconds:

\Delta V=0.010\times100=1\ \text{V}

A cell at 2.8 V drops to 1.8 V during the pulse — below the radio's minimum, so the transmission fails.

The fix is a 100 µF capacitor across the cell, supplying the pulse and recharging slowly between transmissions:

\Delta V=\frac{I\Delta t}{C}=\frac{0.010\times0.003}{100\times10^{-6}}=0.3\ \text{V}

Acceptable ✓, and it extends the usable life substantially because the cell can now be discharged much further.

Temperature. A CR2032 at -10 °C has perhaps 60% of its rated capacity and triple the internal resistance. For an outdoor door sensor, size for the cold case, which gives about 10 years rather than 17.

Self-discharge at 1% per year removes another 10% over a decade.

Honest answer: 8 to 10 years, limited by shelf life and temperature rather than by usage. And the design decision that made it possible was reducing the sleep current, not the transmission energy — because at 44 events a day, the radio is almost irrelevant.

Problem 6 — Choosing a wireless technology

A city wants to monitor 5000 water meters, reporting daily consumption, with meters in basements and underground chambers, on ten-year batteries. Choose the technology.

Solution

Requirements:

  • 5000 devices
  • One 20-byte message per day
  • Ten-year battery
  • Deep indoor and underground penetration
  • City-wide coverage

Assess each option.

Wi-Fi: requires a network at each site, which does not exist in a chamber. Eliminated.

BLE: 10 m range. Would need 5000 hubs. Eliminated.

Zigbee mesh: could work above ground, and an underground chamber has no neighbours to mesh with. Eliminated for those sites.

NB-IoT: designed for exactly this. 20 dB better penetration than ordinary LTE, achieved by repeating transmissions up to 128 times.

Energy: about 5 J per message including attach.

5\ \text{J/day}\times3650=18{,}250\ \text{J over ten years}

Plus idle. In power saving mode the modem draws about 5 µA:

5\times10^{-6}\times3.6\times86{,}400\times3650=5675\ \text{J}

\text{total}=23{,}925\ \text{J}

A D-size lithium thionyl chloride cell holds 19 Ah at 3.6 V = 246 kJ. Comfortably sufficient.

Cost: a subscription per device, perhaps €1 to €3 per year. 5000 devices at €2 = €10,000 per year, forever.

LoRaWAN: private network, so no subscription.

Energy at SF12 for deep penetration: 1.5 s of airtime at 120 mW = 180 mJ.

0.18\times3650=657\ \text{J over ten years}

Thirty-six times less than NB-IoT. A much smaller cell suffices.

Coverage: needs gateways. A city might need 20 to 50 for reliable deep-indoor coverage, at €1000 to €3000 each.

Capital: 50,000 to 150,000 euro, once.

Compare over ten years:

NB-IoTLoRaWAN
Capital€0 infrastructure€100,000
Subscription€100,000€0
Batterylargersmaller
Controloperator's networkyours
Coverage riskoperator's problemyours

Roughly equal in cost. The decision turns on the non-financial factors.

Recommendation: LoRaWAN, for three reasons.

No dependency on a mobile operator's continued support of a technology. 2G and 3G shutdowns have already stranded deployed meters, and a twenty-year asset should not depend on a network the owner does not control.

Lower energy, giving margin for the cold and wet conditions in a chamber.

The gateways serve other applications too — street lighting, parking, environmental sensing — so the infrastructure cost is shared.

The counter-argument, and it is real: if the utility has no wish to operate a radio network, NB-IoT moves that burden to somebody whose business it is. For a small utility that is the better answer, and the technical comparison does not decide it.

Problem 7 — Watchdog design

Design the watchdog strategy for a heating controller with three tasks: sensor reading at 1 Hz, control loop at 0.2 Hz, and a user interface at 10 Hz.

Solution

The naive approach — clear the watchdog in the main loop — fails if the main loop keeps running while a task has stopped.

The correct approach: each task sets a flag, and the watchdog is cleared only when all flags are set.

c
volatile uint8_t task_flags = 0;
#define F_SENSOR  0x01
#define F_CONTROL 0x02
#define F_UI      0x04
#define F_ALL     0x07

void watchdog_service(void) {
    if (task_flags == F_ALL) {
        wdt_reset();
        task_flags = 0;
    }
}

Each task sets its bit on completion. The service function runs from the slowest task's period.

Timeout selection. The slowest task runs every 5 seconds, so all three flags are set at least that often.

t_{watchdog}=2\times5=10\ \text{seconds}

Twice the slowest period, allowing one missed cycle before a reset. Tighter than that produces spurious resets from ordinary jitter; looser leaves the system hung for too long.

Two refinements that matter:

Record the reset cause. Almost every microcontroller has a register indicating whether the last reset was from power-on, brown-out, external, or the watchdog. Log it to non-volatile memory. A device that resets weekly from the watchdog is telling you something important, and without the log you would never know it was happening.

Safe state on reset. After a watchdog reset the heating output must be off, not resuming its previous value. The output pin's default state at reset must correspond to heat off, which is a hardware decision — if the driver's pull-up turns the heater on when the pin floats, the watchdog has made things worse rather than better.

What the watchdog cannot catch:

A task that runs but computes garbage. It sets its flag faithfully.

A stack overflow that corrupts variables without stopping execution. Stack canaries — a known value below the stack, checked periodically — catch this.

A deadlock where all tasks continue but no progress is made. Add a sanity check: if the temperature has not responded to a full-power heating command for ten minutes, something is wrong regardless of what the tasks report.

That last one is the general principle worth stating: a watchdog checks that code is running, and a plausibility check verifies that it is doing the right thing. Both are needed, and the second is the one most often omitted.

Problem 8 — Smartphone power budget

A phone has a 4500 mAh, 3.85 V battery. Estimate battery life for three usage patterns.

Solution

E=4.5\times3.85=17.3\ \text{Wh}=62{,}300\ \text{J}

Pattern 1 — standby. Modem registered, sensors on, screen off: 12 mW.

t=\frac{17.3}{0.012}=1442\ \text{hours}=60\ \text{days}

Pattern 2 — moderate use. 4 hours screen-on browsing, 20 hours standby.

Browsing: display 800 mW, SoC 900 mW, modem 400 mW = 2.1 W.

E_{screen}=2.1\times4=8.4\ \text{Wh}

E_{standby}=0.012\times20=0.24\ \text{Wh}

\text{total}=8.64\ \text{Wh}

\text{life}=\frac{17.3}{8.64}=2.0\ \text{days}

Pattern 3 — gaming. Display at full brightness 1500 mW, SoC and GPU 4000 mW, thermally limited to about 4.5 W total sustained.

t=\frac{17.3}{4.5}=3.8\ \text{hours}

And the temperature check. With the phone's thermal resistance to ambient around 8 °C/W:

\Delta T=4.5\times8=36\ \text{°C}

In a 25 °C room the case reaches 61 °C — too hot to hold, and above the battery's safe operating range.

So the phone throttles, reducing sustained power to about 3 W:

\Delta T=24\ \text{°C} \;\Rightarrow\; 49\ \text{°C case}

Uncomfortable but tolerable, and battery life extends to 5.8 hours.

This is why sustained benchmark scores are 40 to 60% of peak scores, and the difference is entirely thermal rather than electrical.

Where the design levers are:

Display. 30 to 50% of typical usage energy. Reducing brightness from 100% to 50% saves 400 mW — more than any software optimisation.

Dark mode on OLED saves 15 to 40% of display power depending on content, because black pixels draw nothing.

Refresh rate. Dropping from 120 Hz to 60 Hz for static content saves 100 to 200 mW.

And the honest observation. A user asking why their battery drains fast is usually running the display at full brightness. The engineering effort spent on making the SoC efficient is real and substantial, and the display still dominates, which is why display technology rather than processor technology has driven most recent battery-life improvement.

Problem 9 — Antenna design constraint

A wearable must include a 915 MHz LoRa antenna in a 40 × 30 × 10 mm enclosure. Assess the options.

Solution

\lambda=\frac{3\times10^8}{915\times10^6}=328\ \text{mm}, \qquad \frac{\lambda}{4}=82\ \text{mm}

Twice the longest dimension of the enclosure.

Option 1 — a chip antenna. A ceramic component about 8 × 3 mm.

Efficiency: 20 to 40%, so a loss of 4 to 7 dB against an ideal antenna. And it needs a ground plane of at least \lambda/4 to work at its rated efficiency, which is 82 mm and does not fit — so real efficiency is nearer 10 to 20%, a loss of 7 to 10 dB.

Option 2 — a meandered trace on the circuit board, folding the electrical length into the available space.

Efficiency: 30 to 50% with careful design and tuning. Free in component cost, and it consumes board area and requires measurement to tune.

Option 3 — a helical antenna, a coil of wire wound to reduce the physical length.

Efficiency: 40 to 60%, and it needs vertical space, which the 10 mm dimension does not offer.

Option 4 — use the enclosure or the strap. A wearable's strap can contain a printed antenna, giving the length that the body of the device cannot.

Efficiency: 50 to 70%. Mechanically awkward and electrically the best answer.

The link budget consequence. Taking option 2 at 40% efficiency, a 4 dB loss:

LoRa at SF12 has a sensitivity of -137 dBm. With a 14 dBm transmitter, a 4 dB antenna loss at each end and a 3 dB body absorption loss:

\text{link budget}=14-4-3+(-(-137))-4=140\ \text{dB}

At 915 MHz, 140 dB of path loss corresponds to:

140=32.4+20\log_{10}d_{km}+20\log_{10}(915)

20\log_{10}d=140-32.4-59.2=48.4 \;\Rightarrow\; d=263\ \text{km free space}

With a realistic urban exponent of 3.5, referencing 1 km where the free-space loss is 91.6 dB:

140=91.6+35\log_{10}d \;\Rightarrow\; \log_{10}d=1.38 \;\Rightarrow\; d=24\ \text{km}

Still excellent, which is the point: LoRa's enormous link budget means a poor antenna is survivable. The same 4 dB loss on a Bluetooth link would halve the range from 10 m to 6 m, and on a Wi-Fi link it would be decisive.

Recommendation: a meandered PCB trace, tuned by measurement rather than simulation, with a matching network of two components whose values are determined on the bench. And the body is part of the antenna — the design must be measured on a wrist, not on a table, because human tissue detunes an antenna substantially.

Problem 10 — Sensor sampling and settling

A 12-bit ADC multiplexes eight channels at 1 kHz each. Source impedances range from 1 kΩ to 100 kΩ, and the sample capacitor is 15 pF. Find the required settling time and diagnose a likely fault.

Solution

Settling to 12-bit accuracy means the error must fall below half an LSB, which is one part in 8192:

t=RC\ln(2^{13})=RC\times9.01

For the 1 kΩ source:

t=1000\times15\times10^{-12}\times9.01=135\ \text{ns}

For the 100 kΩ source:

t=100{,}000\times15\times10^{-12}\times9.01=13.5\ \mu\text{s}

A hundred times longer, and this is where designs fail.

Available time per channel:

\frac{1}{8\times1000}=125\ \mu\text{s}

Plenty — 13.5 µs of settling in a 125 µs slot is comfortable.

Now the fault. Suppose the sampling rate is raised to 10 kHz per channel:

\frac{1}{8\times10{,}000}=12.5\ \mu\text{s per channel}

And the ADC's own acquisition window is typically a fraction of that — say 2 µs.

\text{settling achieved}=e^{-2/1.5}=e^{-1.33}=0.264

Only 74% settled. The reading is 26% of the way from the true value towards the previous channel's value.

The symptom this produces: a channel's reading depends on which channel was sampled before it. A high-impedance channel next to a low one in the scan order shows a consistent offset that changes when the scan order changes.

That is a distinctive and confusing fault, and it is usually diagnosed only after considerable time spent looking for a wiring error.

Three fixes:

Buffer the high-impedance channels with a unity-gain op-amp (Chapter 2.5). The correct answer — the buffer's output impedance is a fraction of an ohm and settling becomes irrelevant.

Extend the acquisition time, if the ADC allows it. Reduces the achievable sample rate.

Add a capacitor at the ADC input, at least 20 times the sample capacitor. It supplies the charge quickly and recharges slowly from the source — and it slows the response to genuine signal changes, which is fine for temperature and wrong for anything fast.

The general rule that avoids the whole problem:

R_{source}\lt\frac{t_{acquisition}}{9\,C_{sample}}

With a 2 µs window and 15 pF: R_{source}\lt14.8 kΩ. Anything above that must be buffered, and stating this rule on the schematic saves the next engineer a day.

Problem 11 — IoT data volume and cost

A fleet of 500 vehicles reports position every 10 seconds over cellular. Calculate the data volume and cost, and optimise it.

Solution

Naive approach: a JSON message with timestamp, latitude, longitude, speed and heading.

\{\text{"t":1234567890,"lat":51.5074,"lon":-0.1278,"spd":45,"hdg":270}\}\approx70\ \text{bytes}

Plus overhead: TCP/IP headers 40 bytes, TLS record 30 bytes, MQTT 5 bytes.

\text{total}=145\ \text{bytes per message}

Per vehicle per day:

\frac{86{,}400}{10}\times145=8640\times145=1.25\ \text{MB/day}

Fleet:

500\times1.25=626\ \text{MB/day}=18.8\ \text{GB/month}

At a typical IoT tariff of €0.50/MB: €9,400 per month. Unaffordable.

Optimisation 1 — binary encoding.

  • Timestamp: 4 bytes
  • Latitude and longitude as 32-bit integers scaled by 10^7: 8 bytes
  • Speed: 1 byte
  • Heading: 1 byte

\text{payload}=14\ \text{bytes}

Optimisation 2 — batch. Send 30 positions in one message every 5 minutes.

30\times14+75\ \text{overhead}=495\ \text{bytes per message}

288\ \text{messages/day}\times495=143\ \text{kB/day per vehicle}

A reduction of 8.7 times.

Optimisation 3 — delta encoding. After the first position, send only the change, which fits in 2 bytes per coordinate for a 10-second interval at any plausible speed.

\text{first: }14, \text{ subsequent: }6 \;\Rightarrow\; 14+29\times6=188\ \text{bytes payload}

188+75=263\ \text{bytes per message} \;\Rightarrow\; 76\ \text{kB/day}

Optimisation 4 — report by exception. Send a position only when the vehicle has moved more than 50 m, changed heading by more than 15°, or 5 minutes have elapsed.

A vehicle parked overnight sends 1 message per 5 minutes rather than 30 positions. For a typical duty cycle of 8 hours driving and 16 parked:

\text{driving: }96\ \text{messages}\times263=25\ \text{kB}

\text{parked: }192\ \text{messages}\times89=17\ \text{kB}

\text{total}=42\ \text{kB/day}

Final result:

500\times42\ \text{kB}\times30=630\ \text{MB/month}

Cost: 315 euro per month.

From €9,400 to €315 — a factor of 30 — and the position data delivered to the application is the same.

The breakdown of where the saving came from:

OptimisationFactor
Binary encoding5.0
Batching1.7
Delta encoding1.9
Report by exception1.8

Batching gave the smallest gain and it is the one most people do first. The largest single gain was abandoning JSON — and the reason it is used anyway is that it is readable and easy to debug, which is a real benefit that is worth paying for during development and not in production.

Problem 12 — Secure boot chain

Design the secure boot for a connected device, and identify what it protects against and what it does not.

Solution

The chain:

1 — ROM bootloader. Immutable, burned into the silicon at manufacture. Contains a public key hash. Cannot be modified after production, which is what makes it a root of trust.

2 — First-stage bootloader in flash, signed. The ROM verifies its signature before executing it.

3 — Second-stage bootloader, verified by the first.

4 — Application, verified by the second.

Each stage verifies the next before transferring control. A break anywhere stops the boot.

Signature verification. ECDSA with a 256-bit curve: signature 64 bytes, public key 64 bytes, verification about 10 ms on a Cortex-M4.

Rollback protection. A monotonic counter in one-time-programmable fuses, incremented when a security-relevant update is installed. An image with a lower version number is rejected, preventing an attacker from installing an older signed image with a known vulnerability.

Key storage. The signing private key must be in a hardware security module, never on a build server, with signing as a service that logs every use.

What this protects against:

  • Modified firmware. An attacker with physical access cannot make the device run their code.
  • Downgrade attacks.
  • Supply chain substitution during manufacture.

What it does not protect against, and this matters:

A vulnerability in the signed firmware itself. Secure boot verifies authenticity, not correctness. A buffer overflow in your own signed code is executed faithfully.

Runtime compromise. Once running, the application can be attacked through its network interfaces. Secure boot is a boot-time measure only, and it says nothing about what happens afterwards.

Physical extraction of data. Reading flash directly with a probe. Requires flash encryption as a separate measure, with the key in the hardware.

Side-channel attacks. Power analysis or timing analysis during signature verification, extracting the key. Requires constant-time implementations and power-analysis countermeasures.

The practical additions that complete it:

Debug port disabled in production, permanently, by blowing a fuse. A JTAG port left enabled defeats every measure above, and it has been found enabled on shipped products repeatedly.

Flash encryption, with the key in hardware, so extracted flash is useless.

A watchdog that cannot be disabled, so a compromised application cannot silence it.

And the honest summary. Secure boot raises the cost of attack from "download the firmware, modify it, flash it back" to "find a software vulnerability or mount a physical attack on the silicon." That is a large increase and it is not a guarantee, and treating it as one is the mistake that follows implementing it.

Problem 13 — Complete embedded design

Design a solar-powered environmental monitor: temperature, humidity, particulates and noise, reporting hourly, unattended for five years.

Solution

Power budget first, because it constrains everything.

Consumers:

ItemPowerDutyAverage
Microcontroller sleep5 µW99%5 µW
Microcontroller active30 mW0.5%150 µW
Temperature/humidity3 µWcontinuous3 µW
Particulate sensor (fan)100 mW1% (36 s/hour)1 mW
Microphone + processing15 mW2%300 µW
LoRa transmission120 mW0.02%24 µW
Total1.48 mW

The particulate sensor's fan dominates, at 68% of the budget — which is typical, since it is the only mechanical component.

Daily energy:

1.48\ \text{mW}\times86{,}400=128\ \text{J/day}

Solar panel. Sizing for the worst month, December at 55° latitude: about 0.5 peak sun hours per day.

E_{needed}=\frac{128}{0.75\ \text{(charge efficiency)}}=171\ \text{J/day}

P_{panel}=\frac{171}{0.5\times3600}=95\ \text{mW}

Add a factor of 3 for dirt, snow, cloudy weeks and panel degradation:

P_{panel}=285\ \text{mW} \;\to\; \textbf{a 0.5 W panel}

Battery. Sized for the longest expected period with no useful sun — take 10 days:

E=128\times10=1280\ \text{J}=0.36\ \text{Wh}

Plus depth-of-discharge limiting to 50% for cycle life (Chapter 9.3), and cold-weather derating of 40%:

E_{battery}=\frac{0.36}{0.5\times0.6}=1.2\ \text{Wh} \;\to\; \textbf{a 500 mAh LiFePO4 cell}

LiFePO4 rather than lithium-ion, for three reasons: it tolerates a wider temperature range, it survives thousands of shallow cycles, and its 3.2 V matches the electronics without a converter.

Charge controller. A simple linear charger wastes the difference between panel and battery voltage. At these power levels an MPPT controller's own consumption may exceed what it saves, so a linear charger with a well-matched panel is the right choice — a rare case where the simpler option wins on efficiency.

Sensors.

Temperature and humidity: a digital sensor in a radiation shield, mounted so it is not warmed by the enclosure.

Particulates: an optical sensor with a fan, run for 30 seconds per hour with the first 20 discarded to let the airflow stabilise.

Noise: a MEMS microphone with A-weighting and an RMS calculation over 30 seconds, computing L_{Aeq}.

Communication. LoRaWAN at SF9, 40-byte payload hourly.

\text{airtime}=165\ \text{ms}, \qquad \text{duty}=0.005\% \ ✓

Reliability over five years.

The fan is the only wearing part, rated perhaps 30,000 hours continuously. At 1% duty that is 3 million hours of calendar time — not a limit.

Desiccant in the enclosure, replaced never, so specify a sealed IP67 enclosure with a Gore vent — a membrane that passes water vapour but not liquid, preventing the pressure differences that pump moist air in and out.

Watchdog, brown-out detection, two firmware images with fallback, CRC on configuration.

Store and forward with a 7-day buffer in external flash, so a gateway outage loses nothing.

7\times24\times40\ \text{bytes}=6.7\ \text{kB} \;\to\; \text{a 1 Mbit flash is ample}

Cost estimate:

ItemCost
Microcontroller + LoRa module€12
Temperature/humidity sensor€4
Particulate sensor€25
MEMS microphone€2
Solar panel and charger€8
Battery€5
Enclosure, vent, mounting€20
PCB and assembly€15
Total€91

The particulate sensor is the most expensive component and consumes most of the power — which is worth noticing, because if the deployment does not genuinely need particulate data, removing it halves the cost and cuts the power by two thirds, allowing a much smaller panel and battery.

That is the design question to ask first, and the discipline of asking it is what separates a system that gets deployed from one that gets designed.

Problem 14 — Diagnose an intermittent field failure

Two hundred sensors have been deployed for eight months. Around 3% stop reporting each month, and returned units work perfectly on the bench. Diagnose.

Solution

"Works on the bench" is the decisive clue. The failure is environmental, intermittent, or has been cleared by the power cycle that testing involves.

Work through the candidates by what fits that signature.

Software hang recovered by power cycling. The returned unit was power-cycled on arrival, clearing whatever state it was stuck in.

Test: is the watchdog enabled and correctly serviced? Check the reset-cause register log if one exists — and if it does not, that is the first thing to add.

Battery voltage under load. The cell measures fine at rest and collapses during a transmission, as Problem 5 analysed. On the bench, at room temperature and with a fresh charge, it works.

Test: measure the terminal voltage during a transmission at the lowest expected temperature. Look for the internal resistance rising with age.

Moisture ingress. Water in a connector or on the board causes leakage that stops reporting. On the bench it dries out within hours and works perfectly.

Test: weigh returned units against new ones. Water is heavy and this is a decisive measurement — a few grams of gain confirms it immediately.

Temperature. A component out of specification at the extremes. The bench is 22 °C; the field may be -15 or +55.

Test: put a returned unit in a temperature chamber and cycle it.

Flash wear. If the device writes a counter to the same location frequently, endurance is exhausted after a predictable time. Eight months is suspicious — check the write rate against the endurance figure.

Test: compute writes per day and compare with the rated endurance. A device writing once a minute reaches 10,000 cycles in seven days if it uses one location, and in 350 days with 50-way wear levelling.

Network-side causes. Gateway coverage degraded, or the network operator changed something.

Test: are the failures geographically clustered? A map of failures is the single most informative diagnostic, and it takes an afternoon.

The diagnostic sequence, cheapest first:

  1. Map the failures. Clustering points at the network or at a shared environmental factor; uniform distribution points at the devices themselves.
  2. Plot failures against date. A steady 3% per month suggests wear or a random process; a rising rate suggests progressive degradation.
  3. Weigh the returns.
  4. Test at temperature extremes.
  5. Add logging to a batch of units — reset cause, battery voltage under load, temperature — and redeploy. The next failure then explains itself.

The most probable answer, given the specifics: a steady 3% monthly rate over eight months, cleared by a power cycle, is the signature of a software hang with an ineffective or absent watchdog. Moisture would usually show clustering by location and rise in wet months; battery problems would rise over time rather than stay steady; flash wear would show a sharp onset at a predictable date.

The immediate action is not to find the bug but to make the failure self-clearing: a correctly serviced watchdog turns a permanent field failure into a one-minute outage that nobody notices. Then add the logging that identifies the actual cause, and fix it in the next release.

And the design lesson. A device that cannot tell you why it failed will cost far more in field time than the logging would have cost in development. Reset cause, uptime, battery voltage and error counters should be in every message from the first deployment, because the alternative is guessing.


That closes Volume III. From charge on a balloon to a phone in your hand, and every step in between derived rather than asserted.