Reefer Trailer Cold-Chain & Door-Event Monitor

This reference application is here to give you ideas and help you get started quickly. It's built on specific hardware that may not match yours, so focus on the sections that fit your use case. If you'd like to talk through your project and whether it's a good fit for Blues, reach out.
Building with an AI assistant? Point it at this accelerator's firmware in firmware/reefer_cold_chain_monitor/ so it can learn how the device works and help you adapt it to your hardware.
This project is a loss prevention reference design that keeps continuous watch over refrigerated (reefer) trailers. It catches temperature excursions before a load spoils and logs every door event before a pallet walks out the back. Two DS18B20 temperature probes and a magnetic door reed switch feed a Blues Notecarrier CX, which packages the sensor events and hands them to a Notecard for Skylo. The Notecard delivers them over whichever network it can reach, cellular when a tower is in range and Skylo satellite when it isn't.
What you'll have when you're done: a weatherproof, trailer-powered monitor that samples cargo temperature every minute. Each sample queues locally and goes to the Blues Notehub cloud service on the next outbound session, so samples reach the cloud/Notehub in batches (once per hour by default). When a door opens or a temperature threshold is breached, the monitor fires an alert on the next sample cycle (up to ~60 seconds after the event, plus network-establishment time). It also ships hourly compliance summaries to Notehub over cellular or WiFi. Per-sample logs and hourly summaries go in cellular/WiFi-only Notefiles, and the Notecard discards their queued Notes when it connects over NTN, so they never eat into the bundled satellite data budget. NTN sessions only happen when terrestrial coverage is unavailable, and on those periodic hub sessions only pending alert Notes carry payload data over the satellite link.
Energy footprint: steady-state draw from a 12 V trailer supply is roughly 30–60 mAh per 24 h, and most of that is one hourly cellular session of ~15–45 seconds at ~250 mA average. NTN satellite sessions use more energy per event because the link takes longer to establish, but sending only alerts over satellite keeps the 10 KB data budget intact. During commissioning you confirm the real consumption with Blues Mojo (see §9).
1. Project Overview
The problem. A refrigerated trailer hauling fresh produce, pharmaceuticals, or frozen food is one of the more valuable assets in ground transportation. A typical load runs $40,000 to $80,000 before you even count the trailer itself. Most of the loss comes from two failure modes:
- Temperature excursion. A reefer unit that loses refrigerant, trips a setpoint, or runs out of fuel can let a frozen or fresh cargo hold warm up into the danger zone. Often nobody discovers the spoilage until delivery, when it's too late to re-route the load. If you catch the excursion within the first hour, while there are still options, you can save the load instead of writing it off.
- Door theft. Drop-stop cargo theft follows a familiar pattern. A thief follows a trailer to a fuel stop or rest area, opens the rear doors, and removes full pallets in minutes. The most reliable early warning is a door opening at a place or time nobody expected.
Both failure modes have the same root cause, which is that nobody can see inside the trailer while it's moving.
Scope note. This design only uses direct inputs from temperature probes and a door switch. There's no OEM reefer unit or tractor integration, no J1939 CAN, and no proprietary reefer telemetry (Carrier DataLink, Thermo King DSR, etc.). It's a point-instrumentation retrofit for fleets with a mix of reefer makes and models that want a universal, non-invasive monitor.
Why Notecard for Skylo. A trailer is always on the move, so its connectivity keeps changing. Urban routes have dense cellular coverage, while rural interstates can have big gaps and cross-border hauls hand off between carrier networks. Port staging areas that face the ocean can sit outside terrestrial coverage entirely. A fixed cellular SIM handles the urban case but quietly drops offline everywhere else, which is exactly when unsupervised, high-value loads are most exposed.
New to Blues?
Learn what we do in this 3-minute video that walks you through how we simplify wireless connectivity.
Learn what we do in this 3-minute video that walks you through how we simplify wireless connectivity.
Notecard for Skylo
(NOTE-NBGLWX)
(see the datasheet) handles all
of this in a single 30 × 42 mm module. It has LTE-M, NB-IoT, and GPRS for
terrestrial cellular, Skylo NTN (non-terrestrial network) satellite as an
automatic fallback, and WiFi through an onboard antenna. The firmware turns on
this multi-RAT behavior with a single
card.transport
request (method:"wifi-cell-ntn") that it sends once. After that the Notecard
handles network selection itself, and the application never needs to know which
radio carried a given message. If a door opens at a rural rest stop, the alert
goes out over cellular when a tower is in range and over satellite when it
isn't, with no radio-selection logic in the application. A mobile asset can't
choose its coverage, so this automatic multi-RAT (radio access technology)
failover is the main reason to use Notecard here.
Deployment scenario. The electronics go in a weatherproof NEMA 4X enclosure that's strapped or screwed to the inside trailer wall or an exterior chassis rail. It runs off the trailer's 12 V DC supply (or the nose-box connector, if there is one). Two stainless steel DS18B20 probes thread through grommets into the cargo compartment, one near the front (forward air) and one near the rear return-air panel. The reed switch mounts on the door frame, with its matching magnet on the door itself.
2. System Architecture
Device-side responsibilities. While the trailer rolls down the highway at
65 mph, the STM32 host on the
Notecarrier CX sleeps
in STM32 STOP2 (a few µA) until the Notecard's ATTN pin wakes it through
card.attn. Every 60
seconds it wakes just long enough to read both DS18B20 probes and the door pin,
check the threshold rules locally, and hand any resulting Notes to the Notecard
over I²C. The host resumes in place with its RAM intact, so the running state
(temperature averages, the door open/close timestamp, the alert deduplication
epoch) just stays in the AppState struct across sleep cycles. Nothing gets
serialized to the Notecard or lost, and the trailer's 12 V supply only sees a
few seconds of MCU draw per minute.
Notecard responsibilities. Notecard for Skylo takes care of everything
involved in getting the message off the trailer. It queues
Notes locally and runs the WiFi → cellular →
NTN fallback policy that the firmware sets up once at first boot with
card.transport
(method:"wifi-cell-ntn"). It opens a session on the configured
hub.set outbound
cadence (60 minutes by default), and when the host calls
hub.sync it sends
pending alerts right away over whatever network is available. It also pulls
environment
variables
down from Notehub, so dispatch can retune thresholds without a reflash. Notecard
for Skylo has an integrated GNSS radio too, so the firmware turns on
card.location.mode
periodic (600-second interval, motion-gated per the API docs). Every
trailer_alert.qo then carries the last known latitude and longitude, which
tells you where the trailer was when the alert fired without any extra
hardware.
Notehub responsibilities. Everything the Notecard sends, whether over
cellular, satellite, or WiFi, lands in Notehub, which
ingests and stores it and applies the project's
routes. Alerts,
per-sample logs, and hourly summaries live in separate
Notefiles. Dispatch routes can send the
alerts straight to the TMS or on-call channel while the per-sample and summary
streams flow into a long-term cold-chain compliance store. The
trailer_alert.qo payload is compact+port-encoded, so it works the same way
over cellular and NTN, and Notehub session metadata records which transport
carried each event. Smart
Fleets group trailers by
lane, customer, or cargo type and push fleet-specific threshold overrides
without touching any individual device's config.
Routing to the cloud (high level). Notehub routes to HTTP, MQTT, AWS, Azure, GCP, Snowflake, and other destinations. Setup varies by project, and this one doesn't include a specific downstream endpoint, so start with the Notehub routing docs.
3. Technical Summary
- Notehub: create a Notehub project and copy its ProductUID.
- Wire the bench rig: Notecarrier CX + Notecard for Skylo + two DS18B20 probes (VDD to +3V3, GND to GND, data to A0 with 4.7 kΩ pull-up to +3V3) + reed switch on A1 + a jumper from the Notecarrier CX's ATTN pin to D5 so the Notecard can wake the host. The full pinout is in §5.
- Edit one line of
firmware/reefer_cold_chain_monitor/reefer_cold_chain_monitor_helpers.hto setPRODUCT_UIDto your project's value. - Flash with arduino-cli:
The
arduino-cli core install STMicroelectronics:stm32 arduino-cli lib install "Blues Wireless Notecard" "STM32duino Low Power" "STM32duino RTC" OneWire DallasTemperature # Compile and upload (substitute your port) arduino-cli compile -b STMicroelectronics:stm32:Blues:pnum=CYGNET,usb=none firmware/reefer_cold_chain_monitor/ arduino-cli upload -b STMicroelectronics:stm32:Blues:pnum=CYGNET,usb=none -p /dev/cu.usbmodem* firmware/reefer_cold_chain_monitor/usb=noneboard option turns off the USB CDC stack, which has to be off for the host to sleep. §7.1 has the full dependency list and build settings. - Watch: open Notehub → your project → Events. You should see
_session.qowithin 1 minute. On the next outbound window (default ~60 minutes) you'll get a batch oftrailer_log_cell.qoNotes (one per 60-second sample) andtrailer_summary_cell.qo(hourly), and any alerts show up astrailer_alert.qo(immediate sync).
Here's a sample Note this device emits:
{ "t1_c": -1.4, "t2_c": -0.8, "door_open": false }4. Hardware Requirements
| Part | Qty | Rationale |
|---|---|---|
| Notecarrier CX | 1 | Integrated carrier with an onboard STM32L433 host, so there's no separate MCU board to buy. The host sleeps in STOP2 between samples, and the Notecard's ATTN pin wakes it. See the datasheet. |
| Notecard for Skylo (NOTE-NBGLWX) | 1 | Cellular (LTE-M / NB-IoT / GPRS) + Skylo NTN satellite module with built-in GNSS and an onboard WiFi antenna (Quectel BG95-S5 cellular/satellite modem + Silicon Labs WFM200S WiFi). Network selection is automatic and needs no firmware changes. It bundles 500 MB of cellular data over 10 years plus 10 KB of satellite data, with no activation fees. See the datasheet. |
| Blues Mojo | 1 | Coulomb counter on the power rail. You need it during commissioning and validation for ground-truth energy measurement (see §9). Remove Mojo and the Qwiic cable before production deployment, because the field doesn't need their inline current draw and quiescent load. See the datasheet. |
| Adafruit DS18B20 Waterproof Temperature Sensor (#381) | 2 | 1-Wire stainless-steel probe with a −55 °C to +125 °C range and ±0.5 °C from −10 °C to +85 °C. Includes a 4.7 kΩ pull-up resistor. Put one probe near the front evaporator and one near the rear return-air panel. |
| Adafruit Magnetic Contact Switch (#375) | 1 | Normally-open reed switch in a plastic housing that activates within 15 mm of its matching magnet. The switch mounts on the door frame and the magnet on the door leaf. |
| Pololu D24V22F5 5 V Step-Down Regulator (#2858) | 1 | 5.3–36 V input, 5 V / 2.5 A output. It converts the trailer's 12 V DC (or nose-box 12 V) to 5 V for the Notecarrier CX, and it's compact and efficient (85–95%). |
| NEMA 4X enclosure, ~6 × 4 × 2 in | 1 | Weatherproof housing rated for wash-down and condensation, which suits the inside of a reefer trailer. |
| Skylo-certified cellular / NTN flexible antenna (included with NOTE-NBGLWX) | 1 | Ships in the NOTE-NBGLWX kit and connects to the Notecard MAIN u.FL port. Covers LTE-M / NB-IoT / GPRS cellular and Skylo S-Band / L-Band NTN (B23, B255, B256). Must not be substituted. Any other antenna on the MAIN port decertifies the device on Skylo's network, and the device may get blocked. If a production design needs a different antenna, you'll need a delta EIRP test report from a CTIA/OTA-authorized lab. The Blues Antenna Guide has the full certification policy. Route the included flexible antenna cable through a sealed IP68 cable gland in the NEMA 4X enclosure wall so the antenna sits outside the metal structure. |
| Passive GPS/GNSS flexible antenna (included with NOTE-NBGLWX) | 1 | Ships in the NOTE-NBGLWX kit and connects to the Notecard GPS u.FL port. Covers GPS/GNSS L1 (1559–1610 MHz). Route the cable through a sealed cable gland to the outside of the NEMA 4X enclosure, where it has a clear, unobstructed view of the sky, and mount the antenna on the trailer roof or an exterior wall, away from the refrigeration unit. If the included antenna gets damaged, a replacement u.FL passive GNSS antenna works too (e.g., the Blues accessories Quectel YCA001BA, covering 1560–1620 MHz, u.FL). |
| Sealed IP68 cable gland, M16, for the antenna cables | 2 | One per antenna cable (MAIN and GPS), through the NEMA 4X enclosure wall. They keep the IP/NEMA rating intact once the antenna cables pass through. Use a gland sized for the cable diameter of the included antenna pigtails (typically ~3–4 mm). |
| 2-conductor stranded wire, 22 AWG, ~3 m | 1 | Extends the door sensor cable from the door frame to the enclosure. Ordinary alarm wire works. |
| Inline ATO/ATC blade fuse holder, 12–16 AWG leads | 1 | Goes inline on the trailer's 12 V positive feed, between the trailer source and the Pololu D24V22F5 VIN, so overcurrent protection sits as close to the source as practical (see the field installation caution in §5). Any automotive-grade in-line ATO/ATC holder with a lead wire gauge matched to the supply run will do. |
| 3 A ATO/ATC automotive blade fuse | 1 | Fits the inline fuse holder above and is sized for the Pololu D24V22F5's 2.5 A rated output with some wiring headroom. Use only a 3 A fuse, and never swap in a higher rating. A blown fuse points to a wiring fault, so fix the fault before you replace the fuse. |
All Blues hardware ships with an active SIM that includes 500 MB of data and 10 years of service, with no activation fees and no monthly commitment, so there's no separate SIM to buy or activate.
5. Wiring and Assembly
All host I/O lands on the dual 16-pin header of the Notecarrier
CX. Notecard for Skylo
seats into the M.2 slot, and u.FL leads run from it to the externally mounted
cellular, satellite, and GNSS antennas. For bench validation, the Mojo sits
inline between the 5 V supply and the Notecarrier's +VBAT pad and reports
cumulative mAh to the Notecard over Qwiic (I²C).
Power (production, no Mojo):
- Pololu D24V22F5
VIN→ inline fuse holder (3 A ATO blade fuse fitted; see BOM) → trailer 12 V DC positive. Mount the fuse holder on the positive lead as close to the 12 V source as practical (see the field installation caution below). - Pololu
GND→ trailer chassis GND (circuit ground reference for the entire system) - Pololu
VOUT(+5 V) → Notecarrier CX+VBAT - Pololu
GND→ Notecarrier CXGND(power-return path; must share the same chassis ground as the input side)
Power (bench validation, with Mojo inline):
- Pololu
VIN→ bench 12 V supply positive - Pololu
GND→ bench supply GND (circuit ground reference) - Pololu
VOUT(+5 V) → MojoBAT - Mojo
LOAD→ Notecarrier CX+VBAT - Pololu
GND→ MojoGND→ Notecarrier CXGND(shared ground throughout the power chain) - Mojo Qwiic → Notecarrier CX Qwiic (I²C telemetry connection for bench energy measurement only; remove Mojo and the Qwiic cable in production)
DS18B20 temperature probes (1-Wire bus on A0):
- Both probe
VDD(red) wires → Notecarrier CX+3V3 - Both probe
GND(black) wires → Notecarrier CXGND - Both probe data (yellow) wires → Notecarrier CX
A0 - Notecarrier CX
+3V3→ 4.7 kΩ resistor (included in Adafruit #381 bag) → Notecarrier CXA0(pull-up)
Probe order.
getTempCByIndex(0)returns whichever probe the library enumerates first at startup. To match each index to a physical location, power up with only one probe connected, note its reported temperature, label it, and then connect the second. You only have to do this once, on the bench.
Door reed switch (A1):
- Adafruit #375 lead 1 → Notecarrier CX
A1 - Adafruit #375 lead 2 → Notecarrier CX
GND - The firmware uses
INPUT_PULLUPon A1, so no external resistor is needed.
Polarity: The Adafruit #375 is a normally-open (NO) switch, so the circuit is open when no magnet is nearby. Mount the plastic housing on the stationary door frame and the magnet on the moving door. As the door closes and the magnet approaches, the circuit closes and pulls
A1LOW. When the door opens, the circuit opens and the pull-up takesA1HIGH. The firmware readsHIGH = door open.
ATTN-to-D5 jumper (host sleep):
- Notecarrier CX
ATTN→ Notecarrier CXD5with a short jumper. Both are on the same 16-pin header (ATTNat position 3,D5at position 14). - Leave the Notecarrier CX
ENpin unconnected.
The Notecard pulls ATTN low while the host sleeps and raises it when the next
sample is due, and the rising edge on D5 wakes the host from STOP2. EN has
to stay unconnected. On the CX it enables the shared 3.3 V VIO rail (host,
Qwiic, 3V3_OUT, and Notecard I/O), so driving it from ATTN would brown out
the whole board instead of putting the host to sleep. Putting a Host to Sleep
Between Sensor
Readings
covers the full pattern.
Door sensor cable extension: The #375 comes with a 29 cm (≈ 11 in) pigtail. To reach from the door frame to the enclosure, splice 22 AWG 2-conductor wire on at the sensor housing terminals, and keep the join inside a weatherproof connector (e.g., a gel-filled butt splice).
Antennas:
- Notecard
MAINu.FL → included Skylo-certified flexible antenna cable → through sealed IP68 cable gland in enclosure wall → antenna positioned outside with clear sky view - Notecard
GPSu.FL → included passive GPS flexible antenna cable → through sealed IP68 cable gland in enclosure wall → antenna positioned outside with clear sky view (trailer roof or exterior wall, away from the refrigeration unit)
Antenna placement. Both antennas must sit outside the metal enclosure and any steel trailer structure, because steel badly attenuates cellular, NTN, and GNSS signals. Keep the GNSS antenna at least 20 cm from the cellular/NTN antenna and from reefer unit motors or inverters so it doesn't get desensitized. Seat every u.FL connector firmly before you route the cables, since these push-on connectors are fragile. Run the antenna cables through the IP68 cable glands before sealing, avoid sharp bends, and keep at least 10 mm (ideally 20 mm) of clearance between each antenna and nearby metal.
Skylo antenna substitution. The
MAINport antenna must be the Skylo-certified flexible antenna that comes with the NOTE-NBGLWX. Connecting any substitute, even a cellular/NTN whip that looks equivalent, decertifies the device on Skylo's network, and Skylo may block it from the NTN service. If a custom enclosure or mounting setup needs a different antenna, contact Blues about the delta EIRP certification process before you deploy.
Field installation caution. Tap the 12 V feed from a dedicated trailer accessory circuit and fit a 3 A automotive blade fuse as close to the source as practical. Don't wire straight to the battery without overcurrent protection. Route all sensor and power cables away from refrigerant lines, the reefer unit wiring harness, and OEM trailer wiring, and don't share conduit with high-current circuits. Avoid penetrating insulated trailer panels where you can, and run cables through existing grommets or approved bulkhead fittings instead to preserve the trailer's thermal envelope. Seal any non-OEM penetrations with closed-cell foam tape and record them in the trailer maintenance log.
6. Notehub Setup
-
Create a project. Sign up at notehub.io and create a project. Copy the ProductUID, which looks like
com.your-company.your-name:reefer-monitor. -
Set the ProductUID in firmware. Open
reefer_cold_chain_monitor_helpers.hand replace the empty string on the#define PRODUCT_UID ""line with your value. -
Claim the Notecard. Power up the assembled unit. The Notecard associates itself with your Notehub project on its first cellular (or satellite) connection, and the device shows up in your project's Devices tab within a minute or two.
-
Create a Fleet per lane or customer. Fleets group devices for shared configuration and routing. A natural split here is one fleet per cargo type (fresh, frozen, pharmaceutical), because the temperature thresholds are very different for each. Smart Fleets can auto-assign trailers based on properties already in Notehub.
-
Set environment variables. In the Notehub web console, select your Fleet (or an individual Device) → Settings → Environment tab, and enter each variable name and value below. Changes take effect on the next inbound sync over cellular or WiFi (default 120 minutes), with no reflash and no truck roll.
Variable Default Valid range Purpose temp_max_c7.0−30.0 to 50.0 °C Warm excursion threshold in °C. An alert with alert:"temp_excursion"fires when either probe goes above it. 7 °C (45 °F) is a common fresh-produce limit, and frozen loads usually use −15 °C (5 °F). Values outside the range are silently ignored, and the current value stays.temp_min_c-25.0−60.0 to 20.0 °C Cold / freeze protection threshold. An alert with alert:"temp_cold"fires when either probe drops below it. Useful for loads with a minimum-temperature requirement (e.g., live plants, some vaccines). Values outside the range, or a value ≥temp_max_c, are silently ignored.door_alert_sec60030–86400 seconds Seconds the door can stay open before a door_open_longreminder fires. The default is 10 minutes, which is typical for a legitimate delivery stop.sample_interval_sec6010–3600 seconds Seconds between sensor samples and host sleep cycles. summary_interval_min601–1440 minutes Minutes between trailer_summary_cell.qoNotes. Changing it also re-applieshub.set outbound, so the Notecard's sync cadence follows the new value.alert_cooldown_sec180060–86400 seconds Global minimum seconds between any two temperature alerts, whatever the probe or excursion type. The firmware keeps a single last_temp_alert_epochshared by all probes and alert types, so a warm-excursion alert from probe 1 resets the same timer that would suppress a later cold-excursion alert from probe 2. This prevents alert storms during slow-drift excursions, and 30 minutes is a reasonable floor for operational response time. -
Configure routes. Add routes for these Notefiles:
trailer_alert.qo: alert events (door state change, temperature excursion). They go out over the first available transport, with the Notecard picking cellular when it's in range and NTN satellite as a fallback. Route them to your TMS, dispatch system, or on-call channel for real-time delivery. Notehub session metadata shows the transport (cellular vs. satellite) for each event.trailer_log_cell.qo: a per-sample record written everysample_interval_sec(default 60 seconds) with both probe temperatures and the door state. It's cellular/WiFi only and never goes over NTN. Route it to a cold-chain compliance archive or long-term data store. Expect gaps in this file during NTN-only coverage (see §10).trailer_summary_cell.qo: an hourly compliance summary (mean/min/max per probe, door event count, current door state). Also cellular/WiFi only. Route it to the same cold-chain compliance destination astrailer_log_cell.qo.
With alerts kept apart from the log and summary data, you can set up routes at different urgency levels without filter logic on either end.
What you should see in Notehub
_session.qo: Notecard housekeeping, one per cellular or satellite session. Seeing these events confirms the radio is reaching Notehub.trailer_log_cell.qo: one Note queued persample_interval_sec(default every 60 seconds), cellular/WiFi only. The Notes reach Notehub in batches on each outbound session (default 60 minutes, so expect up to 60 Notes per delivery). Sample body:A temperature field showing{ "t1_c": -1.4, "t2_c": -0.8, "door_open": false }−127means that probe wasn't responding when the sample was taken. Expect gaps in this file during long stretches of NTN-only coverage. Per-sample records pick up again once terrestrial connectivity returns.trailer_summary_cell.qo: one persummary_interval_min(hourly by default), cellular/WiFi only. Expect gaps here too during long NTN-only stretches. Summary Notes queued while NTN is the active transport get discarded at sync time (the samedelete:truemechanism astrailer_log_cell.qo), and they aren't kept for a later terrestrial upload. Sample body:A temperature field showing{ "t1_c": -1.4, "t2_c": -0.8, "t1_min_c": -2.1, "t1_max_c": -0.9, "t2_min_c": -1.6, "t2_max_c": -0.3, "door_events": 2, "door_open": false }−127means the probe wasn't responding. In a summary Note, it means no valid samples were recorded across the whole window. Alert Notes use the same−127sentinel when a probe fails to respond at the moment the alert fires. In both Note types, treat−127as a sensor-fault flag and never as a temperature near absolute zero.trailer_alert.qo: generated on the next sample cycle after a threshold trip or door event, then sent immediately withhub.syncso the Notecard doesn't wait for the next outbound window. It goes out over the first available transport, cellular when in range and NTN satellite as a fallback. Sample bodies:{ "alert": "temp_excursion", "t1_c": 9.2, "t2_c": 8.7, "door_open": true, "door_open_sec": 0, "lat": 41.8781, "lon": -87.6298 } { "alert": "door_open", "t1_c": -1.4, "t2_c": -0.8, "door_open": true, "door_open_sec": 0, "lat": 41.8781, "lon": -87.6298 } { "alert": "door_close", "t1_c": -1.4, "t2_c": -0.8, "door_open": false, "door_open_sec": 387, "lat": 41.8781, "lon": -87.6298 }latandloncarry the last known GNSS fix at the moment the alert fired. Both are0.0when no fix has been acquired yet (e.g., on first power-on, before the GNSS antenna has had a clear view of the sky). Treatlat == 0.0 && lon == 0.0as a no-fix sentinel in downstream analytics.
7. Firmware Design
The firmware is split across four files.
reefer_cold_chain_monitor.ino
has setup(), loop(), and the top-level sample-cycle driver.
reefer_cold_chain_monitor_helpers.h
defines the constants, AppState, clamp helpers, and function prototypes.
reefer_cold_chain_monitor_helpers.cpp
implements the sensor drivers, the door and temperature state machines, and all
of the Notecard communication. Finally,
cx_sleep.h
holds the host-sleep helpers (the card.attn request, STOP2 entry, and ATTN
wake).
7.1 Installing and flashing
Dependencies:
- Arduino core for STM32:
stm32duino/Arduino_Core_STM32. Add the index URLhttps://github.com/stm32duino/BoardManagerFiles/raw/main/package_stmicroelectronics_index.jsonunder File → Preferences → Additional Boards Manager URLs, then install it from the Boards Manager (search "STM32 MCU based boards"). The Notecarrier CX's host uses the Cygnet board definition (see the build settings below). Blues Wireless Notecardlibrary:note-arduino. Install it through the Arduino Library Manager witharduino-cli lib install "Blues Wireless Notecard", or search forBlues Wireless Notecardin the IDE Library Manager and install the latest stable version. The note-arduino releases page lists the current stable release.OneWire:PaulStoffregen/OneWire. Install it from the Library Manager.DallasTemperature:milesburton/Arduino-Temperature-Control-Library. Install it from the Library Manager.STM32duino Low Power(and its dependencySTM32duino RTC):stm32duino/STM32LowPower. Install it from the Library Manager. It provides the STOP2 entry and ATTN-pin wake thatcx_sleep.huses.
Build settings: The Notecarrier CX's host is the same STM32L433 as the Blues
Cygnet, so select Blues boards → Cygnet (pnum=CYGNET) and set USB
support (if available) to None (usb=none). If the USB CDC stack is
enabled and no USB host is attached (the trailer supply, or the Notecarrier CX
USB-C switch in NC), the STM32's USB wakeup interrupt fires continuously and
the host never stays in STOP2. Debug output (when DEBUG_MODE is defined in the
.ino) goes to the LPUART on the Notecarrier CX debug jack instead, which an
ST-LINK V3 exposes as a virtual COM port (see Serial Logging With
STLINK).
Flashing with the Arduino IDE: Open reefer_cold_chain_monitor.ino, apply
the build settings above, and hit Upload. With the Notecarrier CX's USB-C
switch in HST, you can reach the host's DFU bootloader over the USB-C port
(hold BOOT, tap RST). You can also upload over SWD with an ST-LINK on the
debug jack.
Flashing with arduino-cli:
# Compile and upload — usb=none disables the USB CDC stack, which must be
# off for the host to sleep.
arduino-cli compile -b STMicroelectronics:stm32:Blues:pnum=CYGNET,usb=none \
firmware/reefer_cold_chain_monitor/
arduino-cli upload -b STMicroelectronics:stm32:Blues:pnum=CYGNET,usb=none \
-p /dev/cu.usbmodem* firmware/reefer_cold_chain_monitor/firmware/reefer_cold_chain_monitor/sketch.yaml sets the same board options, so
arduino-cli compile picks them up too when you run it in that directory
without -b. Replace /dev/cu.usbmodem* with your actual port (COMx on
Windows, /dev/ttyACM* on Linux). With DEBUG_MODE defined, open a serial
monitor on the ST-LINK virtual COM port at 115200 baud to watch the
[boot], [sensor], [door], [temp], [summary], and [alert] log lines.
7.2 Template format codes (compact encoding)
The alert Notefile uses compact
format,
with fixed-width binary-encoded fields that keep satellite data use to a
minimum. The note.template request names the field types: TFLOAT32 (4-byte
IEEE-754), TINT16 (2-byte signed), TUINT32 (4-byte unsigned), and TBOOL
(boolean). These are note-c constants for the numeric type hints the
Notecard reads: TFLOAT32 = 14.1, TINT16 = 12, TUINT32 = 24, and
TBOOL = true. The alert Notefile's compact+port encoding
drops the standard JSON envelope and delivers only the declared body fields as
binary records, which is critical with a 10 KB satellite data budget. Cellular
fully supports the same format, so the firmware doesn't need any special
handling.
7.3 Modules
| Responsibility | Function |
|---|---|
First-boot Notecard config (hub.set, card.transport, card.location.mode) | hubConfigure |
Re-sync hub.set outbound when summary interval changes | applyHubSetIfChanged |
| Compact Note template definition | defineTemplates |
| Environment-variable refresh (each wake) | fetchEnvOverrides |
| DS18B20 temperature reading (1-Wire) | readTemperatures |
| Door reed switch reading | readDoorState |
| Door open/close/long-open state machine | checkDoorEvents |
| Temperature excursion detection + dedup | checkTemperatureExcursion |
| Rolling window accumulation | accumulateSummary |
| Hourly summary Note | sendSummary |
| Immediate-sync alert Note | sendAlert |
| Epoch time from Notecard RTC | getEpochTime |
Host sleep (STOP2 until the Notecard raises ATTN) | cx_sleep.h: cxSleepBegin, cxSleepUntilAttn |
| Application state (held in RAM across sleep cycles) | AppState |
7.4 Sensor reading strategy
DS18B20 probes. Both sensors share the single 1-Wire bus on A0.
probes.requestTemperatures() starts a conversion on both sensors at once and
blocks for ~750 milliseconds at 12-bit resolution (0.0625 °C steps). Then
getTempCByIndex(0) and getTempCByIndex(1) return the two readings. The
DallasTemperature library returns DEVICE_DISCONNECTED_C (−127 °C) for any
probe that doesn't respond. The firmware treats readings below
TEMP_INVALID + 1.0 as invalid and leaves them out of the summary averages,
without affecting the other probe's accumulation.
Door reed switch. Each sample is a simple digitalRead(DOOR_PIN). The
Adafruit #375 is a normally-open switch, and with INPUT_PULLUP and the switch
between A1 and GND, a closed door reads LOW and an open door reads HIGH.
Instantaneous reads are fine here, because a real trailer door stays in each
state for seconds to minutes.
7.5 Event payload design
The firmware writes its data to three
template-backed
Notefiles. The alert Notefile (trailer_alert.qo) uses compact format with a
port assignment. Compact encoding drops the standard Note metadata envelope and
stores only the declared body fields as fixed-length binary records, which is
critical with a 10 KB satellite data budget, and NTN (satellite) mode requires a
port number. Cellular fully supports compact+port too, so the same Notefile gets
delivered correctly over either transport.
The cellular/WiFi-only Notefiles (trailer_log_cell.qo and
trailer_summary_cell.qo) are template-backed so their schema stays consistent,
but they use standard JSON encoding without a port, and they carry
delete:true. According to the Blues Satellite Best
Practices documentation, a
cellular/WiFi-only Notefile template (no format or port) with delete:true
makes the Notecard discard its queued Notes at sync time when NTN is the active
transport. These Notefiles are never transmitted over satellite, so they don't
consume the bundled satellite data budget. (The _cell suffix in their names
marks them as terrestrial-only, and they never go over NTN.)
trailer_log_cell.qo: per-sample, queued, cellular/WiFi only:
{ "t1_c": -1.4, "t2_c": -0.8, "door_open": false }trailer_summary_cell.qo: hourly, queued, cellular/WiFi only:
{
"t1_c": -1.4,
"t2_c": -0.8,
"t1_min_c": -2.1, "t1_max_c": -0.9,
"t2_min_c": -1.6, "t2_max_c": -0.3,
"door_events": 2,
"door_open": false
}trailer_alert.qo: immediate, enqueued then hub.sync, cellular or NTN
transport:
{
"alert": "temp_excursion",
"t1_c": 9.2, "t2_c": 8.7,
"door_open": true,
"door_open_sec": 0,
"lat": 41.8781, "lon": -87.6298
}For every alert, the firmware adds a Note to the alert Notefile (without
sync:true) and then sends a hub.sync request to wake the radio. Keeping the
sync trigger separate from the enqueue avoids a race where sync:true on the
note.add could start a sync before the Note is fully committed. The Notecard
picks cellular when it's in range and falls back to NTN satellite otherwise, so
the firmware doesn't need any radio-selection logic.
Alert type values: door_open (door just opened), door_close (door just
closed, and door_open_sec carries the open duration), door_open_long (door
still open past door_alert_sec), temp_excursion (probe above temp_max_c),
and temp_cold (probe below temp_min_c).
The door_open_sec field is a TUINT32 (4-byte unsigned integer) in the
template, so it can hold door-open durations up to ~136 years. A probe that
fails to respond shows up as −127 in the temperature fields of any Note type
(trailer_log_cell.qo, trailer_summary_cell.qo, and trailer_alert.qo alike).
Treat that as a sensor-fault flag, not a real temperature. Door-event alerts
(door_open, door_close, door_open_long) include live probe readings
sampled at the moment the alert fires, so a −127 in a door alert means the
probe really was unresponsive at that instant, not that the temperatures were
left out on purpose. lat and lon are TFLOAT32 fields that carry the last
GNSS fix. Both are 0.0 when no fix is available, so treat that pair as a
no-fix sentinel.
7.6 Power and sync strategy
The trailer has a 12 V supply, but keeping the host MCU asleep between samples
still cuts the heat in the enclosure dramatically, and it lets the firmware
structure port cleanly to a battery-backed variant (Scoop or otherwise).
setup() runs once at power-up, and each pass through loop() is one sample
cycle. After each cycle the host calls cxSleepUntilAttn() (in cx_sleep.h).
That sends a card.attn
sleep request for sample_interval_sec seconds, waits for the Notecard to
pull ATTN low, and enters STM32 STOP2, where the host draws a few microamps
and keeps its RAM intact. When the interval is up, the Notecard raises ATTN,
the rising edge on D5 wakes the host, and execution picks up on the next line
of loop(). The host resumes in place instead of cold-booting, so the
AppState struct just lives in RAM and nothing gets serialized to the Notecard.
If the sleep request fails or ATTN never goes low (a missing jumper), loop()
waits out the interval awake and tries again on the next cycle, so the sample
cadence holds either way.
The Notecard itself idles at ~8–18 µA between sessions. Per-sample log Notes and
hourly summary Notes pile up in its on-device queue and go out together in a
single outbound session. Door and temperature alerts get enqueued and then
trigger a hub.sync request. That skips the outbound timer and wakes the radio
right away on whatever network is available, cellular if it's in range and NTN
satellite otherwise.
Sampling and transmission are deliberately decoupled. The sensors sample every
60 seconds, but the radio only connects once per hour (plus on demand for
alerts). Fresh environment variables come down on the inbound cadence (default
120 minutes). When cellular is unavailable and the Notecard uses NTN for a
periodic session, trailer_log_cell.qo and
trailer_summary_cell.qo Notes get discarded at sync time. Their
cellular/WiFi-only delete:true templates make the Notecard clear those queues
instead of sending them over satellite, so only pending alert Notes carry
payload data over the satellite link. Every NTN session still has
session-establishment overhead (Skylo protocol + housekeeping), whatever the
alert payload. Inbound checks cost satellite data too, about 50 bytes each per
Satellite Best
Practices,
so the 120-minute inbound cadence spends roughly 600 bytes a day while a
trailer is on NTN. Environment variables aren't templated, so they only arrive
over cellular or WiFi anyway. For trailers that spend long stretches on
satellite, set the _sync_inbound_mins_ntn reserved environment
variable
to a much longer interval.
7.7 Retry and error handling
- If
hubConfigureor template registration fails at power-up,loop()retries the full first-boot configuration on every wake and skips the sample cycle until it succeeds. readTemperaturesexcludes any probe returningDEVICE_DISCONNECTED_Cfrom all accumulation. If both probes fail for the whole summary window, all six temperature fields in the summary carry the−127sentinel instead of a misleading mean of zero.fetchEnvOverridessilently skips missing variables and keeps the current firmware default, so a Notehub project with no environment variables set is always valid.- Alert deduplication: temperature alerts are rate-limited by
alert_cooldown_sec(default 30 minutes) so a slow-drifting probe doesn't flood the downstream on-call channel. - The door open-long reminder fires once per open event (the
door_long_alert_sentflag inAppState). A door that stays open across several sleep cycles produces exactly one reminder, not one per sample.
7.8 Key code snippet 1: compact template definition
The templates registered at first boot make each Note a fixed-length binary
record built for satellite transport. The "compact" format and port
assignment on the alert Notefile meet the NTN requirements, and cellular fully
supports them too, so the same file gets delivered correctly over either
transport. The cellular/WiFi-only Notefiles skip compact format and the port,
and they carry delete:true. When NTN is the active transport at sync time, the
Notecard discards queued Notes from those cellular/WiFi-only Notefiles (see
Blues Satellite Best Practices).
defineTemplates() uses constants from reefer_cold_chain_monitor_helpers.h:
NOTEFILE_ALERT is trailer_alert.qo on port 51 (TEMPLATE_PORT_ALERT),
NOTEFILE_LOG is trailer_log_cell.qo, and NOTEFILE_SUMMARY is
trailer_summary_cell.qo. Each request's response is checked, and any failure
makes defineTemplates() return false so the next wake retries.
// ── Alert template (NTN-compatible, delivered over any transport) ─────────
{
J *req = notecard.newRequest("note.template");
if (req == NULL) return false;
JAddStringToObject(req, "file", NOTEFILE_ALERT);
JAddNumberToObject(req, "port", TEMPLATE_PORT_ALERT);
JAddStringToObject(req, "format", "compact");
// No delete:true — alert notes are never purged; they are delivered over
// whichever transport (cellular or NTN) is available when hub.sync fires.
J *body = JAddObjectToObject(req, "body");
// String field: exemplar length = longest expected value
// ("temp_excursion", 14 chars — ties "door_open_long" at 14 chars).
JAddStringToObject(body, "alert", "temp_excursion");
JAddNumberToObject(body, "t1_c", TFLOAT32);
JAddNumberToObject(body, "t2_c", TFLOAT32);
JAddBoolToObject(body, "door_open", TBOOL);
JAddNumberToObject(body, "door_open_sec", TUINT32);
JAddNumberToObject(body, "lat", TFLOAT32);
JAddNumberToObject(body, "lon", TFLOAT32);
J *rsp = notecard.requestAndResponse(req);
// ...
}
// ── Per-sample log template (cellular/WiFi only) ──────────────────────────
{
J *req = notecard.newRequest("note.template");
if (req == NULL) return false;
JAddStringToObject(req, "file", NOTEFILE_LOG);
JAddBoolToObject(req, "delete", true);
J *body = JAddObjectToObject(req, "body");
JAddNumberToObject(body, "t1_c", TFLOAT32);
JAddNumberToObject(body, "t2_c", TFLOAT32);
JAddBoolToObject(body, "door_open", TBOOL);
// ...
}
// ── Hourly summary template (cellular/WiFi only) ──────────────────────────
{
J *req = notecard.newRequest("note.template");
if (req == NULL) return false;
JAddStringToObject(req, "file", NOTEFILE_SUMMARY);
JAddBoolToObject(req, "delete", true);
J *body = JAddObjectToObject(req, "body");
JAddNumberToObject(body, "t1_c", TFLOAT32);
JAddNumberToObject(body, "t2_c", TFLOAT32);
JAddNumberToObject(body, "t1_min_c", TFLOAT32);
JAddNumberToObject(body, "t1_max_c", TFLOAT32);
JAddNumberToObject(body, "t2_min_c", TFLOAT32);
JAddNumberToObject(body, "t2_max_c", TFLOAT32);
JAddNumberToObject(body, "door_events", TINT16);
JAddBoolToObject(body, "door_open", TBOOL);
// ...
}7.9 Key code snippet 2: immediate-sync alert
The alert Note is enqueued without sync:true, and once the enqueue succeeds, a
hub.sync call wakes the radio right away. On the NOTE-NBGLWX, the Notecard
picks cellular when it has coverage and falls back to the Skylo satellite radio
otherwise, and the firmware never needs to know which. The detection code
(checkDoorEvents() or checkTemperatureExcursion()) reads the last GNSS fix
with getLocation() when it latches the event and passes lat and lon in, so
every alert is geo-stamped where the event happened, even on a retry after the
trailer has moved.
The firmware treats hub.sync as part of the critical alert path. sendAlert()
checks the response for NULL and responseError(), and it only returns true
once the sync itself succeeds. If the sync fails, the pending-alert latch in
AppState stays set and the next wake retries the sync, using the done flag
to skip the note.add call that already queued the Note.
// ── Enqueue alert note ────────────────────────────────────────────────────
// Skip if already successfully enqueued on a prior wake (retry path).
if (!done) {
J *req = notecard.newRequest("note.add");
if (req != NULL) {
JAddStringToObject(req, "file", NOTEFILE_ALERT);
J *body = JAddObjectToObject(req, "body");
JAddStringToObject(body, "alert", type);
JAddNumberToObject(body, "t1_c", (double)t1);
JAddNumberToObject(body, "t2_c", (double)t2);
JAddBoolToObject(body, "door_open", doorOpen);
JAddNumberToObject(body, "door_open_sec", (double)openSec);
JAddNumberToObject(body, "lat", (double)lat);
JAddNumberToObject(body, "lon", (double)lon);
J *rsp = notecard.requestAndResponse(req);
if (rsp != NULL) {
if (!notecard.responseError(rsp)) { done = true; }
notecard.deleteResponse(rsp);
}
}
}
// ── Immediate sync ────────────────────────────────────────────────────────
// ...
bool sync_ok = false;
if (done) {
J *req = notecard.newRequest("hub.sync");
if (req != NULL) {
J *rsp = notecard.requestAndResponse(req);
if (rsp != NULL) {
if (!notecard.responseError(rsp)) {
sync_ok = true;
} else {
DEBUG_PRINTLN(F("[alert] hub.sync returned error — will retry"));
}
notecard.deleteResponse(rsp);
}
// ...
}
}
bool ok = done && sync_ok;
// ...
return ok;7.10 Key code snippet 3: sleeping until the Notecard raises ATTN
cxSleepUntilAttn() asks the Notecard to hold ATTN low for exactly
g_sampleIntervalSec seconds, confirms the pin has gone low, and puts the STM32
into STOP2. The host stays alive, so the firmware checks the result of the
card.attn request instead of firing it off and forgetting about it. The
Notecard's ATTN pin brings the host back on schedule, and loop() carries on
with the next sample, with the door timestamps, accumulator sums, and alert
epochs all still in AppState.
void setup() {
// ...
// ── Arm the ATTN wake: the Notecard raises ATTN (D5) to end each sleep ────
cxSleepBegin();
}
void loop() {
// ...
if (g_state.configured) {
runSampleCycle();
}
// ...
if (!cxSleepUntilAttn(notecard, g_sampleIntervalSec, NULL, log)) {
// The Notecard didn't take the sleep request, or ATTN never went low
// (check the ATTN -> D5 jumper). Keep the sample cadence and try again.
DEBUG_PRINTLN(F("[warn] ATTN sleep failed — waiting out the interval awake"));
delay(g_sampleIntervalSec * 1000UL);
}
}8. Data Flow
Every 60 seconds the firmware wakes up, reads both temperature probes and the door pin, and runs two independent checks (the door state machine and temperature excursion). Then it logs the raw sample, adds the data to the rolling hourly window, and goes back to sleep. Because the checks are independent, a door event and a temperature excursion at the same moment each produce their own alert.
Measured each sample cycle (every 60 seconds): forward-air temperature °C
(probe 1), return-air temperature °C (probe 2), and door state (open/closed).
Each sample gets its own entry in trailer_log_cell.qo (cellular/WiFi only, and
discarded when the Notecard connects over NTN). The readings also feed a rolling
hourly summary window (mean, min, max per probe). The Notecard tracks GNSS
position on its own in the background (card.location.mode periodic,
600-second interval, motion-gated). The host MCU doesn't query location on every
sample cycle. It only reads the last known fix when it detects an alert event,
so every alert is geo-stamped at event-detection time without a per-sample GNSS
query.
Transmitted:
trailer_alert.qo: evaluated and queued during the next sample cycle (up tosample_interval_secafter the event, default 60 seconds), then sent immediately withhub.sync(skipping the outbound timer) when a door opens, a door closes, a door stays open pastdoor_alert_sec, a probe exceedstemp_max_c, or a probe drops belowtemp_min_c. It goes out over the first available transport (cellular preferred, NTN satellite as the fallback). Temperature alerts are rate-limited byalert_cooldown_sec.trailer_log_cell.qo: one Note per sample cycle (default every 60 seconds), cellular/WiFi only, queued and shipped on the next outbound sync. Expect gaps during NTN-only coverage (see §10).trailer_summary_cell.qo: one Note persummary_interval_min(default 60 minutes), cellular/WiFi only, queued and shipped on the next outbound sync. It holds the mean, min, and max for each probe plus the door-open count and the current door state.
Location. hubConfigure calls
card.location.mode
with mode:"periodic" and seconds:600, and from then on the Notecard tracks
location by itself in the background. In periodic mode it only samples at the
configured interval when it detects motion, so the GNSS radio comes on roughly
every 10 minutes while the trailer is moving and stays off while it's
stationary. The host MCU doesn't poll card.location on each 60-second sample
cycle. Instead, checkDoorEvents and checkTemperatureExcursion make a single
card.location request when they latch an alert event, and sendAlert embeds
that fix as the lat/lon fields (a retry reuses the latched fix). Every alert
gets geo-stamped with the trailer's position at
event-detection time, and the routine sample cycle never has to make a GNSS
query. When no fix has been acquired yet (first power-on, or an obstructed GNSS
antenna), both fields are 0.0. The GNSS antenna needs an unobstructed view of
the sky, so mount it on the trailer roof or an exterior wall, away from the
refrigeration unit, as described in §5.
Routing. All three Notefiles land in Notehub, and routes send them on to
downstream systems from there. A typical setup sends trailer_alert.qo to a TMS
or messaging gateway (real-time dispatch), and sends trailer_log_cell.qo and
trailer_summary_cell.qo to a cold-chain compliance database.
Alert triggers (all in trailer_alert.qo, delivered over cellular or NTN
satellite):
door_open: the door went from closed to open. Syncs immediately.door_close: the door went from open to closed, anddoor_open_seccarries how long it was open.door_open_long: the door is still open afterdoor_alert_secseconds (default 10 minutes). Fires once per open event.temp_excursion: either probe is abovetemp_max_c. Subject toalert_cooldown_secdedup.temp_cold: either probe is belowtemp_min_c. Subject toalert_cooldown_secdedup.
9. Validation and Testing
Expected steady-state cadence. A healthy, closed, in-range trailer produces
trailer_log_cell.qo Notes every 60 seconds (queued locally and arriving in
Notehub on the hourly outbound session, up to 60 per batch), one
trailer_summary_cell.qo per hour, and zero alert events. The first batch of
log Notes reaches Notehub at the first outbound session (default 60 minutes).
Alerts show up in trailer_alert.qo within one sample cycle
(sample_interval_sec, default 60 seconds) of the event, plus
network-establishment time.
Functional validation. You can confirm the alert flow without waiting for a real excursion:
- Temperature (warm-excursion path): On a room-temperature bench the probes
already read well above the 7 °C default, so the first sample after power-up
fires a
temp_excursionalert without anyone touching the probes. While they stay warm, another follows each timealert_cooldown_sec(default 30 minutes) runs out. The[sensor]lines in the serial output show the readings that tripped it. - Temperature (cold-excursion path): Put one probe in an ice-water bath
(~0 °C) and temporarily raise
temp_min_cto5.0in the Fleet environment variables. Also raisetemp_max_cabove room temperature (e.g.,30.0), because the firmware checks the warm threshold first and the room-temperature probe would otherwise keep winning. Once the next inbound sync pulls the new values and the cooldown has run out, the next sample with the iced probe triggers atemp_coldalert. After you've confirmed the alert, settemp_min_cback to-25.0andtemp_max_cback to your operational value. - Door: Pull the door sensor magnet away from the housing, and a
door_openalert should appear within the next sample cycle. Bring the magnet back and check thatdoor_closearrives with a non-zerodoor_open_sec. - Threshold tuning: Lower
temp_max_cto0.0in the Fleet's environment variables. The next inbound sync pulls the new value, and the next sample cycle with any probe above 0 °C fires the alert. - Location: Make sure the GNSS antenna has a clear view of the sky outdoors.
After at least one full
card.location.modefix interval (≤ 10 minutes on a moving or recently moved unit), trigger a door or temperature alert and check thatlatandlonare non-zero in the resultingtrailer_alert.qoNote body in Notehub. A(0.0, 0.0)pair means no fix has been acquired yet, so give it more time or check that the GPS antenna cable and connector are seated.
Power validation with Mojo. The published figures for Notecard for Skylo are ~8–18 µA idle (radio off between syncs) and ~250 mA average during cellular transmit, with millisecond spikes approaching 2 A in regions that need GSM. Satellite sessions have a similar current envelope but last longer.
Put the Mojo inline between the 5 V supply and
the Notecarrier CX +VBAT pad. In steady state, expect a trace like this:
| Phase | Expected current |
|---|---|
| Host in STOP2 + Notecard idle between samples | ~20–30 µA (Notecard ~8 µA + host ~1–2 µA, plus Notecarrier CX regulator quiescent) |
| Host awake (sensor reads + Notecard I²C) | ~5–15 mA for ~1–2 seconds per sample |
| Notecard cellular session (hourly) | ~250 mA average, 15–45 seconds |
| Notecard satellite session (NTN) | ~200–400 mA average, usually longer than LTE-M |
With the defaults (60-second samples, 60-minute summaries, no alerts), a useful bench target is roughly 30–60 mAh per 24 h on cellular, most of it from the once-an-hour radio burst. Satellite-only operation uses more per session because the link takes longer to establish.
Measurement scope. Mojo sits on the 5 V output side of the Pololu regulator, so it only measures the downstream Notecarrier + Notecard draw at 5 V. The real draw from the 12 V trailer battery will be higher, because the Pololu adds its own quiescent current and conversion losses (the D24V22F5 is rated 85–95 % efficient under load, and it draws quiescent current even with no load). If parked-trailer battery drain is a concern, pair Mojo's 5 V measurement with an upstream current measurement on the 12 V feed. For a unit that will sit parked for long periods, consider a step-down regulator with lower quiescent current.
Two anomaly patterns jump out on a Mojo trace:
- Host never sleeping: A flat, continuous 10–30 mA baseline means the host
isn't entering STOP2. Check the
ATTN→D5jumper (without it the firmware waits out each interval awake and logs[warn] ATTN sleep failed), and confirm the firmware was built with USB support set to None (usb=none). With the USB CDC stack enabled, the host leaves STOP2 within a millisecond. - Excessive hourly bursts: Each session runs 2–3 minutes instead of 15–45 seconds. That's usually weak signal or frequent satellite fallback, so check the antenna routing and site coverage.
Mojo is a bench and commissioning tool, and production units don't need it.
A Notecarrier CX and a Notecard for Skylo team up with two DS18B20 probes and a
magnetic reed switch to watch for the two failure modes behind most reefer
trailer cargo loss, temperature excursion and unauthorized door access. The
firmware samples every minute and queues each raw sample locally for Notehub
delivery on the next outbound session (trailer_log_cell.qo). When any
threshold trips, it fires an alert on the next sample cycle (up to ~60 seconds)
over whatever radio is available (trailer_alert.qo, delivered automatically
over cellular or NTN satellite). It also ships a compact hourly summary
(trailer_summary_cell.qo) for compliance and trending. Thanks to the
cellular + satellite multi-RAT design, the monitor stays connected as the
trailer moves from an urban distribution center through rural interstates to a
port staging yard, and nobody has to configure networks, swap SIMs, or
negotiate with carriers.
The same firmware structure makes a good starting point for pharmaceutical cold chain, cross-border intermodal containers, or any mobile insulated enclosure where a temperature log and a door audit trail carry regulatory or financial weight.
10. Troubleshooting
| Symptom | Likely Cause | What to Check |
|---|---|---|
| Device never appears in Notehub | PRODUCT_UID is still the empty default, or the unit hasn't established a session on any radio yet. | Confirm that PRODUCT_UID in reefer_cold_chain_monitor_helpers.h matches the value shown in your Notehub project. Watch the serial monitor at 115200 baud for [boot] and _session.qo lines. If any radio is in range, a session usually completes within a few minutes of first power-on. If only NTN coverage is available, expect the first session to take longer, and confirm the GNSS and MAIN antennas have an unobstructed view of the sky. |
One or both DS18B20 probes report -127 | Probe wiring fault, missing 4.7 kOhm pull-up on the 1-Wire bus, or a damaged probe lead. | Check that VDD goes to +3V3, GND to GND, and data to A0 on every probe. Confirm the 4.7 kOhm pull-up (included in the Adafruit #381 bag) sits between +3V3 and A0. Power-cycle with one probe at a time to find the failing lead. The firmware treats -127 as a sensor-fault sentinel, so one failed probe doesn't corrupt the other probe's summary average. |
| Implausible temperature readings (e.g. +85 then drifting) | The DS18B20 reads +85 at power-on until the first conversion completes, and long unshielded cable runs can also pick up noise. | Wait at least one full sample cycle before trusting a probe reading. Keep probe leads away from reefer wiring harnesses and inverter motors. If the problem continues, shorten the cable run or switch to a shielded extension. |
Door state stuck false (closed) when the door is open | The magnet is still close to the reed switch, or the housing and magnet are mounted in reversed positions. | The Adafruit #375 is normally-open, so a closed door (magnet near switch) reads LOW and an open door reads HIGH. Mount the housing on the stationary frame and the magnet on the moving leaf, not the other way around. Check that the gap closes to within 15 mm when the door shuts. |
Door state stuck true (open) | Cut or pinched 22 AWG extension cable, oxidized splice, or a failed reed switch. | Continuity-test the extension wire end to end with the magnet held against the housing. Re-seat any butt splices, and reseal them with a gel-filled connector if water has reached the joint. |
| Cellular sessions succeed but no NTN sessions ever fire | Terrestrial coverage satisfies the default selection, so NTN never gets exercised. | That's expected with the wifi-cell-ntn policy, because NTN is a fallback and not a parallel path. To confirm NTN is healthy, take the unit to a known cellular dead zone or temporarily disable the cellular and WiFi paths with card.transport on the Notecard. Then watch for _session.qo entries that report an ntn transport in their session metadata. |
| Temperature alerts firing on a healthy load | The threshold values don't match the actual cargo class, or one probe sits against a cold or warm spot inside the trailer. | Open the Fleet's environment variables and confirm temp_max_c and temp_min_c match the cargo (fresh produce, frozen, or pharmaceutical). Compare both probe values in trailer_log_cell.qo. If one consistently disagrees with the other by several degrees, move it away from the evaporator outlet, the door seal, or direct return-air flow. |
| Hourly summaries and per-sample logs stop arriving for hours | The trailer is parked in NTN-only coverage, so trailer_log_cell.qo and trailer_summary_cell.qo Notes are discarded at sync time and only alerts ride the satellite link. | This is the documented behavior that protects the satellite budget. Once terrestrial coverage returns, per-sample logging and summaries resume on the next outbound session. The Notes discarded during the gap don't come back. |
| Battery / power loss after long parked dwell | The host isn't sleeping (missing ATTN→D5 jumper, or firmware built with USB support enabled), the Pololu D24V22F5's quiescent draw is on the 12 V trailer battery, or the regulator is wired ahead of an always-on circuit instead of an accessory feed. | Use a Mojo trace to confirm the host enters STOP2 between samples. A flat 10–30 mA baseline means it doesn't, so check the ATTN→D5 jumper (§5) and the usb=none build option (§7.1). Tap the 12 V supply from a switched accessory circuit so the unit drops out when the trailer is fully parked. For installations that have to run continuously, budget for the regulator's own quiescent current on top of the Notecard's idle draw, and consider a lower-quiescent step-down regulator (see the measurement-scope note in §9). |
GNSS fields stuck at 0.0, 0.0 | The GNSS antenna doesn't have a clear view of the sky, the u.FL connector isn't seated, or the trailer hasn't moved long enough to satisfy card.location.mode's motion-gated periodic sample. | Reseat both u.FL connectors on the Notecard and reroute the GNSS antenna to the trailer roof or exterior wall, away from the refrigeration unit. Drive the trailer for at least one full 600-second interval, then confirm the next alert carries a non-zero fix. |
| Antenna or sky-view problems on Skylo | Antenna substituted with a non-Skylo-certified part, or MAIN antenna mounted inside a metal enclosure. | Confirm that the included Skylo-certified flexible antenna is on the MAIN port, since swapping in a different cellular or NTN whip decertifies the device on Skylo's network. Route the antenna outside the NEMA 4X enclosure and any steel trailer structure through a sealed IP68 cable gland. See §5 and the Blues Antenna Guide. |
If your problem isn't covered above, search or post on the Blues community forum.
11. Limitations and Next Steps
The design covers the two failure modes behind most refrigerated cargo loss (excursion and unauthorized door access) with direct sensor input and no OEM reefer integration, so it works across mixed fleets. Humidity, multi-zone mapping, J1939 telemetry, and geofence logic were left out on purpose, to be added where a deployment calls for them.
Simplified for the POC
The POC keeps things simple in the places below, and that's deliberate. Once the basic excursion-and-door monitor has proven itself in the field, a real fleet deployment will want to add another sensor, finer placement guidance, or an OEM integration at each of them.
Two probes only. A 53 foot trailer can stratify by several degrees from front to rear and from floor to ceiling. This design puts one DS18B20 near the forward evaporator and one near the rear return-air panel. That's enough to catch a reefer failure or a gross excursion, but it's not a full thermal map of the cargo zone.
No humidity or dew-point sensor. Some loads, like leafy greens, certain pharmaceuticals, and paper goods, are sensitive to humidity as well as temperature, but the current Notes only carry temperature.
Coarse probe placement guidance. Section 4 specifies forward and rear positions, but it doesn't lay out an evaporator-vs-cargo-zone placement scheme calibrated to a particular load type or trailer geometry. Operators are expected to refine probe placement during their first commissioning runs.
Reed switch is binary, not multi-state. The Adafruit #375 only reports open or closed. It can't tell a door cracked an inch from one swung wide open, and on a split-rear configuration it has no way of knowing which door (left or right) was actuated.
No driver or dispatcher app integration. Notehub routes alerts to whatever downstream destination you configure, and this project doesn't ship a mobile app for the driver or a dashboard for dispatch.
No reefer ECU integration. The design is intentionally OEM-agnostic, so it can't read setpoint, defrost cycle, refrigerant pressure, fuel level, or alarm codes from the reefer controller itself. Operators see the cargo-zone temperature but not the reefer's health.
Single 12 V feed assumption. The wiring assumes a standard trailer accessory circuit. Battery-only installations, or ones that have to survive a fully parked trailer for days, would do better with a different power architecture, including an upstream regulator with lower quiescent draw.
Production Next Steps
After the basic monitor is running across a real fleet, these extensions are the natural next moves. They're listed roughly from most immediately useful to most dependent on outside integration.
Add humidity (and dew point) sensing by wiring an
SHT41 to the Qwiic bus and adding
rh_pct and dewpoint_c fields to the per-sample, summary, and alert Notes.
The compact alert template has room for the extra fields without breaking the
satellite data budget.
Multi-zone temperature mapping goes from two probes to four or six DS18B20s on the same 1-Wire bus (the library and template can scale) and reports per-zone min, mean, and max in the hourly summary. That helps with high-value loads, where a hot spot in one corner of the trailer is the early warning.
Reefer ECU integration adds a J1939 or CAN tap for fleets with homogeneous reefer units, so setpoint, defrost, fuel level, and alarm codes ride along with the cargo-zone telemetry. Add it alongside the existing Notes instead of replacing them.
Geofence-aware alerting uses the lat and lon fields already on every
alert to quiet door-open events at known authorized stops (yard, customer DC,
fuel network) and escalate door events at unexpected locations. That logic is
best run in Notehub or downstream, not on the device.
Notecard Outboard DFU means turning on Outboard DFU, which the Notecarrier CX supports with no extra wiring, so threshold logic updates and bug fixes can ship over the air whenever the trailer has cellular or WiFi coverage. Firmware updates don't go over NTN, which is far too low-bandwidth for a firmware image.
Compliance reporting for FSMA-Cold and HACCP adds a Notehub route into a compliance archive that rolls the per-sample logs and hourly summaries up into per-shipment PDF reports. Those reports work for FSMA Sanitary Transportation rule audits and HACCP records at food shippers, or for pharma cold-chain release decisions.
12. Summary
The fleet dispatcher now has the two signals that matter most. One is a geo-stamped alert within roughly a minute when a door opens at an unexpected location, and the other is a temperature excursion caught while the load can still be re-routed. Notecard for Skylo picks cellular, NTN satellite, or WiFi at every moment of a haul without any firmware logic. It keeps the bundled satellite data budget intact by discarding the per-sample and hourly summary Notefiles at sync time when NTN is the active transport, and alerts ride the satellite link regardless. For shippers under FSMA Sanitary Transportation rules or HACCP records, and for pharmaceutical cold-chain operators on GDP terms, those same per-sample logs and hourly summaries double as the independent condition record that audits and freight claims demand.
The same skeleton carries over to pharma cold chain, cross-border intermodal containers, frozen-food long-haul, or any mobile insulated enclosure where a temperature log and a door audit trail have regulatory or financial weight.
On This Page
- 1. Project Overview
- 2. System Architecture
- 3. Technical Summary
- 4. Hardware Requirements
- 5. Wiring and Assembly
- 6. Notehub Setup
- 7. Firmware Design
- 7.1 Installing and flashing
- 7.2 Template format codes (compact encoding)
- 7.3 Modules
- 7.4 Sensor reading strategy
- 7.5 Event payload design
- 7.6 Power and sync strategy
- 7.7 Retry and error handling
- 7.8 Key code snippet 1: compact template definition
- 7.9 Key code snippet 2: immediate-sync alert
- 7.10 Key code snippet 3: sleeping until the Notecard raises ATTN
- 8. Data Flow
- 9. Validation and Testing
- 10. Troubleshooting
- 11. Limitations and Next Steps
- 12. Summary