Project 26: Nerves Cold-Chain Sensor Gateway

Build and flash a real Elixir/Nerves gateway that samples temperature hardware, raises hysteresis-based alarms, stores readings while offline, uploads them in order, and proves signed tentative firmware can validate or roll back safely.

Quick Reference

Attribute Value
Difficulty Level 3
Suggested Seniority Senior Elixir/embedded developer
Time Estimate 20-32 hours
Main Programming Language Elixir on Nerves
Alternative Programming Languages Erlang, C for comparison only
Coolness Level Level 5
Business Potential Level 4
Prerequisites OTP supervision, basic electronics safety, Linux networking, durable data concepts
Key Topics Nerves, Circuits.I2C, sensor sampling, offline store-and-forward, alarms, signed A/B firmware, validation and rollback

1. Learning Objectives

By completing this project, you will be able to:

  1. Read a real digital temperature sensor from a Nerves target through an owned hardware process.
  2. Separate raw samples, calibrated readings, alarm decisions, durable queue records, and upload acknowledgements.
  3. Supervise hardware, storage, networking, and publishing with failure boundaries appropriate to an embedded gateway.
  4. Preserve readings across network loss and device reboot without unbounded flash growth.
  5. Model warning/critical alarms using hysteresis, dwell time, stale-sensor detection, and explicit acknowledgement.
  6. Install only authenticated firmware into an inactive slot, boot it tentatively, validate it after health checks, and demonstrate rollback when validation never occurs.

2. All Theory Needed (Per-Concept Breakdown)

Store-and-Forward Embedded Reliability with Tentative Firmware

Fundamentals

An embedded cold-chain gateway sits between fallible physical hardware and an unreliable network. A temperature reading is not merely a number: it has a sensor identity, sample sequence, monotonic acquisition time, best-known wall time, calibration version, quality status, and persistence state. The gateway must continue sampling and alarming when cloud connectivity disappears, then upload durable readings without reordering or silent loss. OTP supervision helps restart software components, but durability requires an explicit flash-backed queue and bounded retention policy. Firmware updates add another failure boundary. A cryptographic signature authenticates the artifact before installation; an A/B layout preserves the previous image; a tentative boot becomes permanent only after the new system proves sensor, storage, and runtime health. Otherwise the device must roll back without remote intervention.

Deep Dive into the concept

Begin at the physical boundary. Choose a supported development board and a digital sensor such as an SHT31-class I2C device. Confirm voltage, pull-ups, bus address, and wiring against the board and sensor datasheets before powering it. The Elixir process interacting with the bus should own the Circuits.I2C reference and serialize transactions. If arbitrary application processes open the device independently, recovery and coordination become unclear. A sensor worker can perform one bounded transaction per sample interval, decode the documented fixed-point representation, apply a versioned calibration offset, and emit a structured result.

Physical systems fail in ways ordinary web code rarely sees. A cable disconnect may yield an I/O error. A stuck bus may time out. A sensor may return an implausible value or repeat a stale value. The process should classify these outcomes rather than crashing on every error. A transient read error increments a counter and retries at the next interval; repeated failures transition sensor quality to unavailable and raise a stale-sensor alarm. A crashed worker is supervised and reopens the bus, but restart intensity prevents an electrical fault from producing an infinite hot restart loop. The last known value must be marked stale rather than presented as current.

Use integer hundredths of a degree Celsius or another exact unit throughout domain decisions. Floating-point display can be produced at the edge. Each accepted reading receives a gateway-local monotonically increasing sequence. Monotonic time measures intervals and dwell windows; wall time labels events for humans but may jump when network time becomes available. Store both acquisition monotonic data and current UTC confidence. If wall time is unknown at boot, retain an explicit unsynchronized status and later map/upload according to a documented policy rather than inventing certainty.

Alarm logic needs state. A critical-high threshold of 8.00 C should not toggle on every 7.99/8.01 oscillation. Define entry threshold, exit threshold, dwell time, and quality requirements. For example, enter critical only after three consecutive readings above 8.00 C or 60 seconds of continuous violation, then remain critical until readings stay below 7.50 C for 120 seconds. Hysteresis separates entry and exit. Acknowledgement records that an operator saw the alarm; it does not erase the physical condition. Stale-sensor, low-battery, and storage-pressure alarms are separate types with their own lifecycle.

The durable buffer is a small store-and-forward log, not an unbounded copy of the cloud. Persist each reading or bounded batch before reporting it accepted to the sampler. Track upload state with stable gateway ID and sequence range. The publisher reads the oldest unacknowledged batch, sends it with an idempotency key such as {gateway_id, first_seq, last_seq, payload_digest}, and removes or marks it acknowledged only after a valid server response. If the connection fails after the server commits, retrying is safe because the server deduplicates the stable identity. Preserve order unless the protocol explicitly permits gaps.

Flash has finite space and write endurance. Batch writes at a rate consistent with the loss budget, use a database/log designed for embedded persistence, and define retention. When offline storage approaches high-water marks, emit an alarm and apply an explicit policy: lower noncritical sample frequency, compact acknowledged data, or drop oldest low-priority records while preserving alarm transitions. Never let the root filesystem fill unpredictably. Power-loss tests should interrupt writes and prove the store opens, detects incomplete data, and reports any loss within the stated budget.

Networking is its own supervised subsystem. VintageNet reports connectivity changes; the publisher reacts without coupling the sensor loop to network calls. Sampling must proceed when Wi-Fi or Ethernet restarts. The publisher uses bounded connect/request timeouts and backoff with jitter. Avoid spawning a job per reading while offline. One publisher or bounded pipeline drains durable batches, observing server rate limits and storage pressure. Connectivity status changes are signals, not proof that the application endpoint is reachable.

Now consider firmware. A checksum detects accidental corruption but does not prove who produced an image. The release pipeline signs a firmware artifact with a protected private key; the device contains the corresponding trusted public key or uses an authenticated update service. Verification must occur before installation. The gateway rejects unknown signing keys, invalid signatures, incompatible target identifiers, downgrade-disallowed versions, and artifacts exceeding partition limits. Credentials used to authorize update checks are separate from the firmware signing key.

An A/B scheme writes the new artifact to the inactive application partition while the current system continues running. The boot metadata selects the new slot as tentative for the next boot while retaining the previous known-good slot. Power loss during download or inactive-slot write must not corrupt the active image. On the tentative boot, start a health gate with a deadline. Minimum evidence should include: application supervision reaches stable state, persistent store opens and can read/write a probe, sensor returns plausible samples or a deliberately documented degraded acceptance, firmware metadata matches the intended target, and the watchdog/reboot mechanism is alive. Network reachability should usually not be mandatory because an otherwise healthy cold-chain device must boot offline.

Validation is an explicit irreversible decision for that trial: once the runtime marks the firmware valid, it becomes the known-good baseline. Validate only after enough observation to detect early crashes, not immediately in Application.start. If the health gate fails, the application crashes repeatedly, the watchdog resets the system, or power cycles occur before validation, the boot/update mechanism should return to the prior slot according to the configured policy. The device then records a rollback reason locally and uploads it when connectivity returns.

Signed firmware cannot make faulty code safe by itself. It proves authorized origin, not correctness. Tentative validation and rollback limit operational risk. Anti-rollback policy also has tradeoffs: blocking all downgrades may prevent recovery to a secure known-good release; permitting arbitrary older signed firmware may reintroduce vulnerabilities. Define a minimum security version and an operator-approved recovery path. Protect update keys and rotate trust roots through a staged process that does not strand deployed devices.

Supervision ties these boundaries together. A top-level gateway supervisor can start persistent storage first, then sensor supervision, alarm manager, network stack observer, publisher, update client, and firmware health gate. A sensor driver restart must not erase the durable queue. A publisher crash must not stop sampling. A corrupt store may be serious enough to prevent firmware validation and enter a safe degraded mode. The alarm manager rebuilds state from persisted recent alarm transitions or starts explicitly unknown; it does not silently assume normal after reboot.

Validation requires physical evidence. Run on actual hardware, disconnect the sensor, block the network long enough to accumulate records, reboot while offline, restore connectivity, and compare uploaded sequence continuity. For firmware, install a correctly signed good image and observe validation, then install a correctly signed but deliberately non-validating test image and prove automatic return to the previous version. Also attempt a tampered artifact and show it is rejected before slot activation. These are controlled lab drills with recovery access and known-good images available.

How this fits on projects

OTP isolates hardware, storage, network, alarm, and update processes. Elixir structs and pattern matching encode reading quality and alarm transitions. Nerves provides reproducible firmware, device runtime, and update integration; the project proves the complete behavior on a physical target.

Definitions & key terms

  • Store-and-forward: Persist locally, then transmit later with stable identity.
  • Hysteresis: Different entry and exit thresholds that prevent alarm chatter.
  • Dwell time: Required continuous duration before a state transition.
  • Tentative firmware: Newly booted image not yet marked known-good.
  • A/B layout: Active and inactive firmware slots enabling safe replacement/rollback.
  • Firmware validation: Runtime confirmation that the trial image passed health gates.
  • Trust root: Public key or authority accepted for firmware authentication.
  • Watchdog: Independent mechanism that resets a non-responsive system.

Mental model diagram

        PHYSICAL WORLD                           DEVICE SOFTWARE
[temperature sensor/I2C] -> [Sensor Owner] -> [Reading + Quality]
                                                  |          |
                                                  v          v
                                           [Alarm FSM]  [Durable Buffer]
                                                  |          |
                                               local alert   v
                                                        [Publisher]
                                                             |
                                                      unreliable network

        FIRMWARE SAFETY
[signed artifact] -> verify target/signature -> write INACTIVE slot
                                                   |
                                                   v
                                      reboot TENTATIVE image
                                                   |
                              +--------------------+------------------+
                              v                                       v
                   health gate passes                       crash/deadline/power cycle
                   validate known-good                      rollback previous slot

How it works (step-by-step, with invariants and failure modes)

  1. Open the hardware bus in one supervised owner and sample at a monotonic interval.
  2. Classify/decode readings, assign sequence, and persist before downstream acknowledgement.
  3. Feed accepted readings into hysteresis/dwell alarm state.
  4. Drain oldest durable batches only when application connectivity is healthy.
  5. Deduplicate acknowledgements by stable sequence-range identity.
  6. Verify firmware target, version, and cryptographic signature before inactive-slot write.
  7. Boot tentatively and run storage/sensor/runtime health gates until deadline.
  8. Validate the image or allow reset/rollback to the previous known-good slot.
  9. Invariants: network loss never stops sampling; no unacknowledged record is deleted; unauthenticated firmware never boots; a trial does not become permanent without explicit health validation.
  10. Failure modes: sensor disconnect, time jump, flash pressure, torn write, network ambiguity, update power loss, bad signed image, watchdog reset, and invalid trust rotation.

Minimal concrete example

PSEUDOCODE — gateway decisions

on sample_tick:
  result <- sensor.read()
  reading <- classify_and_calibrate(result, monotonic_now, wall_clock_status)
  sequence <- durable_buffer.append(reading)
  alarm_manager.observe(reading)

publisher:
  batch <- durable_buffer.oldest_unacknowledged(limit)
  receipt <- upload(batch, idempotency_key(gateway_id, sequence_range, digest))
  if receipt confirms exact range: durable_buffer.acknowledge(range)

tentative_boot_health_gate:
  require supervision_stable
  require store_read_write_probe
  require sensor_health_policy_satisfied
  require firmware_target_matches
  if all pass before deadline: validate_firmware
  otherwise: do not validate; watchdog/reboot enables rollback

Common misconceptions

  • “If Wi-Fi is connected, uploads are safe.” Link state is not endpoint delivery or acknowledgement.
  • “A checksum makes firmware trusted.” Authenticity requires a verified signature rooted in a trusted key.
  • “Validate immediately after boot.” That destroys the rollback window before real health is known.
  • “Supervision preserves readings.” Restarting a process does not replace durable storage.
  • “Acknowledging an alarm clears the condition.” Acknowledgement and physical recovery are separate.

Check-your-understanding questions

  1. Why store a sequence and idempotency key with offline batches?
  2. Which health checks should not depend on internet access?
  3. Why does a correctly signed test image still need tentative boot and rollback?

Check-your-understanding answers

  1. They preserve order and make ambiguous upload retries safe.
  2. Core boot, storage, sensor policy, and watchdog health; the gateway must operate offline.
  3. A signature proves authorized origin, not absence of defects or hardware incompatibility.

Real-world applications

  • Vaccine, food, and biologics cold-chain monitoring.
  • Remote industrial and agricultural sensor gateways.
  • Resilient appliances requiring unattended updates.

Where you’ll apply it

  • Reading, alarm, buffer, and update requirements in Section 3.
  • supervision and firmware health architecture in Sections 4–5.
  • physical disconnect, reboot, tamper, and rollback drills in Section 6.

References

  • Nerves getting started: https://hexdocs.pm/nerves/getting-started.html
  • Circuits.I2C: https://hexdocs.pm/circuits_i2c/Circuits.I2C.html
  • VintageNet: https://hexdocs.pm/vintage_net/VintageNet.html
  • Nerves.Runtime: https://hexdocs.pm/nerves_runtime/Nerves.Runtime.html
  • NervesHubLink: https://hexdocs.pm/nerves_hub_link/readme.html

Key insight

An unattended gateway is reliable only when it preserves physical evidence offline and treats new firmware as guilty until a real health gate validates it.

Summary

Own hardware access, classify sample quality, persist before upload, use hysteresis-based alarm state, separate sampling from networking, authenticate firmware before inactive-slot installation, and validate tentative boots only after device-local health evidence.

Homework/Exercises to practice the concept

  1. Design high-temperature entry/exit thresholds and dwell periods that avoid chatter.
  2. Calculate storage required for 30 days at one sample per minute with a 64-byte encoded record and 25% overhead.
  3. Decide whether network reachability belongs in the firmware validation gate.
  4. Trace power loss during download, inactive-slot write, first trial boot, and after validation.

Solutions to the homework/exercises

  1. Example: enter above 8.00 C for 60 seconds, exit below 7.50 C for 120 seconds; acknowledge remains separate.
  2. 30 * 24 * 60 * 64 * 1.25 = 3,456,000 bytes, before database/filesystem reserve and logs.
  3. Usually no: core cold-chain operation must remain valid offline; test networking separately and upload trial status later.
  4. Active slot remains safe during download/write; incomplete tentative boot remains unvalidated and rolls back; after validation the new slot is known-good.

3. Project Specification

3.1 What You Will Build

Build a Nerves firmware for one supported board and real I2C temperature sensor. It includes a sensor owner, calibrated reading model, alarm state machine, durable offline buffer, connectivity-aware uploader to a local test server, health/status CLI, signed update client, and tentative firmware validation gate.

3.2 Functional Requirements

  1. Sample a real sensor at a configurable monotonic interval.
  2. Record sequence, raw/calibrated value, calibration version, time quality, and sensor quality.
  3. Raise/clear temperature and stale-sensor alarms using dwell and hysteresis.
  4. Persist unacknowledged readings and alarm transitions across reboot.
  5. Upload ordered batches with stable idempotency identity and acknowledged sequence ranges.
  6. Enforce storage high-water/critical-water policies without filling the device.
  7. Verify firmware signature, target, and version before inactive-slot installation.
  8. Validate tentative firmware only after device-local health gates; otherwise roll back.

3.3 Non-Functional Requirements

  • Sensor sampling continues through network reconfiguration and endpoint outage.
  • Declared worst-case loss after sudden power removal is bounded and measured.
  • Private signing keys never exist on the device or in the repository.
  • Alarms and firmware transitions are visible locally when cloud access is absent.
  • Update and rollback drills are performed with physical recovery access and a known-good artifact.

3.4 Example Usage / Output

$ coldchainctl status
firmware=1.3.0 slot=B validation=confirmed uptime=02:14:33
sensor=sht31 quality=good temperature_c=4.27 last_sample_age_ms=812
alarm=normal buffer_pending=0 network=online last_uploaded_seq=8842

$ coldchainctl simulate-network down
network=offline sampling=continues
after=10m buffer_pending=10 first_seq=8843 last_seq=8852

3.5 Data Formats / Schemas / Protocols

Reading record: gateway/device/sensor IDs, sequence, monotonic acquisition marker, UTC value plus confidence, raw and calibrated hundredths Celsius, calibration version, quality flags, battery/power status. Upload receipt confirms a contiguous sequence range and digest.

3.6 Edge Cases

  • Sensor absent at boot and reconnecting later.
  • One impossible spike versus sustained excursion.
  • Wall clock jumping after NTP synchronization.
  • Network fails after server accepts a batch.
  • Storage reaches high-water while offline for weeks.
  • Sudden power loss during record append or compaction.
  • Tampered, wrong-target, downgraded, or oversized firmware.
  • Signed image boots but sensor process repeatedly crashes, preventing validation.

3.7 Real World Outcome

3.7.1 How to Run (Copy/Paste)

$ export MIX_TARGET=<supported_target>
$ mix deps.get
$ mix firmware
$ mix firmware.burn
$ coldchainctl run-drill offline-buffer
$ coldchainctl run-drill firmware-rollback

3.7.2 Golden Path Demo (Deterministic)

On real hardware, disconnect networking while the sensor continues sampling, reboot once, reconnect, and prove the server receives one contiguous deduplicated sequence. Then reject a tampered artifact, validate a signed healthy image, and automatically roll back a signed deliberately non-validating image.

3.7.3 Exact serial/CLI transcript

$ coldchainctl run-drill full
hardware sensor=sht31 bus=i2c-1 status=reading
network forced_offline=true samples_added=30 alarm_events=1
reboot requested=true buffer_reopened=true pending=30
network restored=true uploaded=30 duplicate_server_records=0 sequence_gaps=0
update tampered_image signature=invalid installed=false
update good_image version=1.3.1 boot=tentative health=passed validation=confirmed
update rollback_fixture version=1.3.2 boot=tentative health=failed reason=sensor_supervision_unstable
watchdog_reset=true rollback_version=1.3.1 rollback_recorded=true
DRILL PASS

4. Solution Architecture

4.1 High-Level Design

                           [Gateway Supervisor]
                 +----------+-----+-----------+------------+
                 v                v           v            v
        [Sensor Supervisor] [Durable Store] [Network] [Update Supervisor]
                 |                |           |            |
        [I2C Sensor Owner] -> [Reading Log] -> [Batch Publisher]   [Update Client]
                 |                |                        verify/write inactive
                 v                v                              |
          [Alarm Manager]   [Storage Monitor]                    v
                 |                                       [Tentative Health Gate]
          local LED/buzzer                                  | validate / no validate
                                                           v
                                                   known-good / rollback

4.2 Key Components

Component Responsibility Key Decision
Sensor Owner Own I2C reference and classify reads One serialized hardware boundary
Reading Log Durable ordered unacknowledged records Bounded retention and power-loss budget
Alarm Manager Hysteresis/dwell/stale state Acknowledgement separate from recovery
Network Observer Report connectivity state Sampling never depends on it
Batch Publisher Ordered idempotent upload Delete only confirmed ranges
Update Client Authenticate and install inactive image Never trust checksum alone
Health Gate Decide tentative validation Device-local evidence and deadline

4.3 Data Structures (No Full Code)

  • Reading struct with exact integer units and quality/time metadata.
  • Alarm state keyed by type with phase, entry time, acknowledgement, and last evidence.
  • Durable batch descriptor with contiguous sequence range and digest.
  • Firmware trial record with previous/new versions, slot, signature key ID, health evidence, deadline, and rollback reason.

4.4 Algorithm Overview

Sampling and alarm evaluation are O(1) per reading. Durable buffer append is amortized storage-dependent work. Upload drains O(k) records per bounded batch. Health validation evaluates a fixed set of probes during one bounded trial window.


5. Implementation Guide

5.1 Development Environment Setup

Select a Nerves-supported target, read board/sensor electrical documentation, and use a current Nerves toolchain. Keep host-only unit tests for pure alarm and batching logic, but reserve hardware integration, power-loss, and firmware drills for the target. Store signing credentials outside source control.

5.2 Project Structure

coldchain_gateway/
├── lib/coldchain/{application,reading,alarm_state}.ex
├── lib/coldchain/sensors/{supervisor,sht31}.ex
├── lib/coldchain/storage/{reading_log,monitor}.ex
├── lib/coldchain/network/{observer,publisher}.ex
├── lib/coldchain/firmware/{update_client,health_gate,trial_record}.ex
├── rootfs_overlay/
├── config/target.exs
├── priv/drills/
└── test/{alarm,buffer,firmware_policy,hardware}/

5.3 The Core Question You’re Answering

“How can an unattended BEAM device preserve trustworthy temperature evidence and recover from a bad update when both hardware and connectivity fail?”

5.4 Concepts You Must Understand First

  1. Nerves firmware lifecycle — Nerves Getting Started, “Creating and Burning Firmware”.
  2. OTP supervision — Designing for Scalability with Erlang/OTP, supervision and restart chapters.
  3. I2C transactions — sensor datasheet plus The Book of I2C, protocol and bus chapters.
  4. Durable delivery — Enterprise Integration Patterns, “Store and Forward” and “Idempotent Receiver”.
  5. Embedded reliability — Making Embedded Systems, 2nd Edition, watchdog, state, and fault chapters.

5.5 Questions to Guide Your Design

  1. What evidence makes one reading trustworthy or explicitly degraded?
  2. How much data may be lost in sudden power loss, and how is that measured?
  3. Which alarms require local action even with no network?
  4. Which health probes are strong enough to validate firmware?
  5. How will signing-key rotation avoid stranding old devices?

5.6 Thinking Exercise

Draw two timelines: a 14-day network outage and a bad tentative firmware boot. Track sampling, storage pressure, alarms, publisher state, firmware slots, watchdog resets, and what a remote operator learns after connectivity returns.

5.7 The Interview Questions They’ll Ask

  1. Why use monotonic and wall time together on an embedded gateway?
  2. How do hysteresis and dwell time reduce alarm noise?
  3. How do you make store-and-forward upload idempotent?
  4. What does a firmware signature prove, and what does it not prove?
  5. Why should firmware validation wait for health evidence?
  6. How does an A/B layout survive power loss during update?

5.8 Hints in Layers

Hint 1: Pure domain first — Test reading quality and alarm transition tables on the host.

Hint 2: One hardware owner — Let one supervised process own the bus and expose structured results.

Hint 3: Prove offline before cloud — Persist, reboot, and replay into a local receiver before adding update logic.

Hint 4: Trial means untrusted — Create a deliberately non-validating signed test image and prove rollback physically.

5.9 Books That Will Help

Topic Book Chapter
Embedded architecture Making Embedded Systems, 2nd Edition State machines, watchdogs, fault handling
I2C The Book of I2C Protocol and transaction chapters
OTP recovery Designing for Scalability with Erlang/OTP Supervision chapters
Durable messaging Enterprise Integration Patterns Store and Forward; Idempotent Receiver

5.10 Implementation Phases

Phase 1: Physical Sampling and Alarm Domain (6-9 hours)

  • Wire/read real sensor, classify quality, persist sequences, and exercise hysteresis/stale alarms.

Phase 2: Offline Delivery and Device Operations (6-10 hours)

  • Add durable queue, network observer, idempotent batch receiver, storage pressure, reboot/power drills.

Phase 3: Authenticated Firmware and Rollback (8-13 hours)

  • Establish signed artifacts, inactive-slot install, tentative health gate, validation, watchdog, rollback, and runbook.

5.11 Key Implementation Decisions

Decision Options Recommendation Rationale
Temperature units Float, integer hundredths Integer hundredths Exact threshold decisions
Buffer Memory, durable bounded log Durable bounded log Survive offline reboot
Upload identity Request UUID, sequence range + digest Sequence range + digest Stable retry semantics
Alarm Single threshold, hysteresis+dwell Hysteresis+dwell Avoid chatter
Firmware In-place, A/B tentative A/B tentative Preserve known-good recovery
Validation Immediate, health-gated Health-gated Catch early failure

6. Testing Strategy

6.1 Test Categories

Category Purpose Examples
Pure domain Deterministic reading/alarm behavior dwell, hysteresis, stale quality
Storage Power/reboot continuity append, acknowledge, torn write
Network Ambiguous delivery and outage accepted-then-timeout, long offline period
Hardware Real bus/sensor behavior unplug/replug, invalid sample
Firmware security Reject unauthenticated artifacts tamper, wrong target, old version
Rollback Prove tentative safety health pass/fail, repeated reset

6.2 Critical Test Cases

  1. Oscillation around threshold does not chatter; sustained excursion enters and clears according to policy.
  2. Sensor unplug raises stale alarm without stopping storage/network/update supervision.
  3. Network outage plus reboot preserves a contiguous pending sequence.
  4. Server acceptance followed by client timeout does not duplicate records after retry.
  5. Storage high-water and critical-water paths raise alarms and apply documented retention.
  6. Tampered or wrong-target firmware never becomes boot candidate.
  7. Healthy tentative image validates only after all local probes pass.
  8. Signed non-validating image resets and returns to previous known-good version.
  9. Power interruption during inactive-slot installation leaves current active firmware bootable.

6.3 Test Data

Use fixed temperature traces for normal, short spike, sustained high, recovery, impossible value, and sensor loss. Use sequence batches with duplicate/gap responses. Build non-production signed good, tampered, wrong-target, and rollback-fixture firmware artifacts.

6.4 Definition of Done

  • Real target reads a physically connected temperature sensor.
  • Readings include exact units, sequence, calibration, time, and quality metadata.
  • Hysteresis/dwell and stale-sensor alarms pass deterministic traces.
  • Offline readings survive reboot and upload once in contiguous order.
  • Storage pressure and sudden-power-loss behavior are measured and documented.
  • Firmware authenticity and target/version checks occur before installation.
  • Healthy tentative firmware validates after local health gates.
  • Deliberately non-validating signed firmware rolls back to prior known-good image.
  • Full physical drill ends with DRILL PASS and retained local evidence.

7. Common Pitfalls & Debugging

7.1 Frequent Mistakes

Problem: “Temperature alarm chatters.”

  • Why: Entry and exit use one threshold with no dwell.
  • Fix: Use separate thresholds, consecutive/duration evidence, and explicit state.
  • Quick test: Replay a trace oscillating by one hundredth around the threshold.

Problem: “Offline data disappears after reboot.”

  • Why: Queue or acknowledgement state exists only in process memory.
  • Fix: Persist before acceptance and delete only confirmed sequence ranges.
  • Quick test: Power cycle with pending records, then inspect range continuity.

Problem: “Bad firmware never rolls back.”

  • Why: It was validated immediately or the health deadline/watchdog is absent.
  • Fix: Keep trial tentative until device-local probes pass; withhold validation on the fixture.
  • Quick test: Boot signed non-validating image and observe slot/version after reset.

7.2 Debugging Strategies

  • Inspect I2C bus/address separately from decoded sensor values.
  • Show sensor quality, buffer range, alarm state, slot, trial deadline, and validation in one local status command.
  • Preserve a bounded boot/update journal across rollback.
  • Compare server receipt ranges and device pending ranges when diagnosing duplicates/gaps.

7.3 Performance Traps

  • Writing flash for every noisy intermediate state without a loss/endurance budget.
  • Spawning upload work per sample during an outage.
  • Retaining unbounded logs or acknowledged readings.
  • Making firmware validation depend on a slow or unavailable cloud endpoint.

8. Extensions & Challenges

8.1 Beginner Extensions

  • Add local LED/buzzer patterns for warning, critical, stale, and update trial.
  • Add calibration metadata import and validation.

8.2 Intermediate Extensions

  • Add a second sensor and disagreement-quality policy.
  • Add secure-element-backed device identity with NervesKey.
  • Add adaptive sampling under stable temperature and storage pressure.

8.3 Advanced Extensions

  • Stage a signing trust-root rotation across two firmware releases.
  • Add fleet rollout rings and automatic pause on rollback-rate threshold.
  • Perform environmental chamber testing and quantify sensor/thermal lag.

9. Real-World Connections

9.1 Industry Applications

Cold-chain gateways support pharmaceutical, food, and laboratory logistics where missing evidence can invalidate a shipment. The same store-and-forward and tentative-update model applies to remote pumps, energy controllers, and agricultural gateways.

  • Nerves for reproducible embedded Linux firmware and Elixir runtime.
  • Circuits.I2C for hardware bus ownership.
  • VintageNet for network lifecycle.
  • NervesHubLink and fwup-based mechanisms for firmware delivery and slot operations.
  • CubDB or another embedded-appropriate durable store for bounded records.

9.3 Interview Relevance and Scope Boundary

P26 is a complete embedded system: physical sensor, offline evidence, local alarms, firmware artifact authentication, partition trial, and rollback. It is distinct from Projects 1–13 because hardware and boot safety are first-class. It is also distinct from P27: P27 loads a Rust NIF into a running BEAM VM and studies scheduler/crash risk at a native function boundary; P26 validates and rolls back the entire device firmware image and should not require a custom NIF.


10. Resources

10.1 Essential Reading

  • Nerves Getting Started: https://hexdocs.pm/nerves/getting-started.html
  • Nerves.Runtime: https://hexdocs.pm/nerves_runtime/Nerves.Runtime.html
  • Circuits.I2C: https://hexdocs.pm/circuits_i2c/Circuits.I2C.html
  • VintageNet: https://hexdocs.pm/vintage_net/VintageNet.html
  • NervesHubLink: https://hexdocs.pm/nerves_hub_link/readme.html
  • NervesKey: https://hexdocs.pm/nerves_key/NervesKey.html

10.2 Standards and Further Study

  • The selected board and sensor manufacturer datasheets are primary hardware specifications.
  • The Update Framework specification: https://theupdateframework.io/
  • fwup: https://github.com/fwup-home/fwup
  • Making Embedded Systems, 2nd Edition, state-machine, watchdog, and reliability chapters.
  • The Book of I2C, electrical and protocol chapters.