Use Your AI Agent to Talk to Your Products with Notehub IQ

Blues Developers
What’s New
Resources
API Reference
Notecard and Notehub API documentation
Blog
Technical articles for developers
Connected Product Guidebook
In-depth guides for connected product development
Developer Certification
Get certified on wireless connectivity with Blues
Newsletter
The monthly Blues developer newsletter
Terminal
Connect to a Notecard in your browser
Webinars
Listing of Blues technical webinars
Blues.comNotehub
Shop
Docs
Help
Support DocsNotehub StatusSecurity AdvisoriesVisit our Forum
Sign In
Docs Home
What’s New
Resources
API Reference
Notecard and Notehub API documentation
Blog
Technical articles for developers
Connected Product Guidebook
In-depth guides for connected product development
Developer Certification
Get certified on wireless connectivity with Blues
Newsletter
The monthly Blues developer newsletter
Terminal
Connect to a Notecard in your browser
Webinars
Listing of Blues technical webinars
Blues.comNotehub
Shop
Docs
Example Apps
Samples
Building a Simple Asset Tracker
Building Edge AI Applications
Continuous Asset Tracking with External GPS and Immediate Location Sync
Controlling Device State and Receiving Acknowledgment
Managing a Notecard's WiFi Network Remotely
Putting a Host to Sleep Between Sensor Readings
Routing Data from Notehub to a Custom Cloud Endpoint
Using LEDs and NeoPixels to Monitor Notecard Status
Accelerators
Asset Location Tracking
Heavy Equipment Hours-of-Use & Utilization Tracker
Returnable Container / Tote Pool Tracker
Untethered Trailer and Chassis Fleet Tracker
Asset Performance Optimization
Legacy Diesel Generator Fleet Performance Uplift
Solar Array String-Level Performance Dashboard
Battery Management Systems
Aerial Lift / Rental Equipment Battery Health Monitor
Off-Grid Solar Battery Site Controller
Remote Cabinet Backup Battery Sentinel
Downtime Prevention
Municipal Wastewater Lift Station Monitor
Rooftop HVAC Predictive Maintenance
VFD Pump Predictive Maintenance
Energy Monitoring Solutions
Commercial Tenant Sub-Metering Bridge
EV Charger Session & Utilization Monitor
Utility Distribution Transformer Load Monitor
Energy Savings
Commercial Plug-Load & After-Hours Waste Dashboard
Demand-Response Solar + Battery Dispatcher
Multi-Site Walk-In Cooler Energy & Setpoint Monitor
Industrial Equipment Monitoring
CNC Machine Spindle Load and Cycle Time Tracker
Injection Molding Shot-to-Shot Process Monitor
Trailer Manufacturer Connected Trailer Platform
Loss Prevention
Construction Equipment Anti-Theft Tracker with Immobilizer
Reefer Trailer Cold-Chain & Door-Event Monitor
Remote Patient Monitoring
Cellular Medication Adherence Pillbox
Post-Discharge Vitals Relay Hub
Remotely Monitored Assets
Off-Grid Livestock Water Tank Monitor
Remote Apiary Hive Health Monitor
Safety Assurance
Construction Site Environmental & Noise Exposure Monitor
Lone Worker Panic and Fall Detection Beacon
Regulatory-Grade Pharmacy and Lab Cold Storage Audit Monitor
Supply Chain Tracking
Pallet-Attached Cold Chain Logger
Rail Car Condition & Interchange Tracker
Truck Roll Reduction
Commercial Grease Interceptor Level Monitor
Propane / LPG Tank Fill Telemetry
homechevron_rightExample Appschevron_rightAcceleratorschevron_rightReturnable Container / Tote Pool Tracker

Returnable Container / Tote Pool Tracker

Returnable Container / Tote Pool Tracker banner
note

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/tote_pool_tracker/ so it can learn how the device works and help you adapt it to your hardware.

This project is an asset location tracking solution for pools of reusable containers like plastic totes, pressurized kegs, and gas cylinders. It reports location on motion events and daily heartbeats, using cellular connectivity and the Notecard's built-in accelerometer. Each container sends a motion event when it starts or stops moving and a daily check-in while it sits idle, all over LTE Cat-1 bis. The hardware is a Notecarrier CX with a Notecard Cell+WiFi (§4 has the BOM). Operators tune the heartbeat interval and motion sensitivity from the Blues Notehub cloud service, with no reflashing.

1. Project Overview

The problem. Supply chains run on reusable containers. Plastic totes carry produce from the farm to the distribution center, pressurized cylinders shuttle gas between filling plants and customer sites, and stainless kegs make the brewery-to-bar loop thousands of times before they're retired. The economics only work if the containers keep moving through the loop, and they leak out of their pools all the time. One gets left on a loading dock past its pickup window. Another ends up in a back corner of a customer's warehouse, on the wrong carrier, or forgotten at a rail interchange for six months. Industry estimates put pool shrinkage anywhere from 5% to 20% a year per container type. It's a quiet, spread-out cost that rarely shows up as one dramatic incident, but it steadily eats into the pool's working capacity and the replacement budget.

Underneath it all is a visibility problem, and the usual fixes each break down somewhere in the supply chain. Passive RFID tags and barcodes need scanner infrastructure at every gate, and active RFID needs per-site readers the container owner doesn't control. Manual cycle counts are expensive, slow, and never quite in sync with reality. GPS plus cellular looks like the obvious answer, but pure GPS is a poor fit for an asset that spends 90% of its time sitting still in a warehouse. The receiver draws tens of milliamps waiting for a fix that adds very little over just knowing the container is "at the Memphis DC."

In this project, the Notecard's built-in accelerometer watches for motion while the device sleeps at microamp levels. When it sees the container move (a forklift picks it up, it gets loaded onto a truck, or it's shunted across a rail yard), it wakes the host MCU. The host queues a motion event, and the Notecard delivers it over cellular, using cell-tower and WiFi AP triangulation for location. Triangulation adds no real power cost beyond the cellular session that has to happen anyway to deliver the Note. It's accurate enough at the site level to tell you whether a tote is at Supplier A or Customer B, and it doesn't need GPS hardware or wait on a GPS cold start. A daily heartbeat confirms the device is alive even when the container sits untouched for a week.

Why Notecard. Containers crisscross supplier warehouses, customer sites, truck yards, and rail interchanges, and most of those places aren't on any WiFi network the container owner controls. You can't realistically ask every customer to share their wireless network, and there's no consistent AP infrastructure across a national supplier base. Cellular removes all of those dependencies and works the same way at a Chicago cold-storage facility as it does at a rural agri-distribution depot. Location comes from cell-tower and WiFi AP triangulation (mode:"wifi,cell"), so there's no GPS hardware and no GPS cold-start latency. On each Notehub session the Notecard scans the surrounding cell towers, plus any WiFi APs it can see, and Notehub resolves both into a latitude/longitude that it appends to every event automatically. In AP-dense places like warehouses and distribution centers, the WiFi data can tighten accuracy from the kilometer scale to tens of meters. In rural or outdoor areas with no APs, the device falls back to cell-only triangulation automatically. The WiFi scan adds a little time to the start of each session (usually a few seconds, depending on AP density and radio conditions), which is negligible next to the cellular session itself. Where APs are around, that's an accuracy boost that costs you next to nothing. This is asset location tracking built around the supply chain's real geography instead of lab conditions.

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.

Bench and limited field-trial assembly. This reference build puts a Notecarrier CX, a Notecard, and a rechargeable 2 Ah LiPo battery in a polycarbonate IP67 housing (Hammond 1554C2GY or equivalent, see §4). The housing attaches directly to the container with non-penetrating fasteners, such as stainless zip ties, band clamps, cradle brackets, or industrial-grade adhesive pads, depending on the container type. Nothing is wired to the container, and the sites don't need any infrastructure. Expect to swap batteries every 12–24 months in a low-motion deployment, which makes this hardware a fit for bench validation and short field trials rather than a multi-year production rollout. The device configures itself on first power-on and runs on its own, and fleet operators manage every device and tune per-fleet settings from Notehub. Production deployments that need 3–5+ years between swaps replace the LiPo path with a custom carrier board and a Li-SOCl₂ primary cell (see §11).

warning

Safety: not rated for classified or explosive atmospheres. This build uses standard commercial electronics and a LiPo battery. It is not ATEX, IECEx, Class I Division 2, or intrinsically safe certified, and it must not be deployed in classified or explosive atmospheres, including anywhere flammable gas or vapor may be present, unless a recognized testing body has certified the complete assembly (enclosure, battery, antenna, and all electronics) for that environment.

2. System Architecture

System architecture: Notecard MBGLW built-in accelerometer and cellular → Notecarrier CX STM32 host → cellular → Notehub → routes

Event Format in Notehub

The firmware emits two Note types. Notehub appends location to each one automatically and routes them separately:

Motion event (tote_event.qo), which appears within ~30 seconds of the motion threshold being crossed:

{
  "file": "tote_event.qo",
  "body": {
    "event":      "departed",
    "moving":     true,
    "battery_mv": 3851.0,
    "cycle":      14
  },
  "where_lat":   35.9728,
  "where_lon":  -86.7653,
  "where_location": "Nashville TN USA",
  "where_country": "US"
}

Daily heartbeat (tote_heartbeat.qo), which fires once per heartbeat_hours (default 24 h):

{
  "file": "tote_heartbeat.qo",
  "body": {
    "battery_mv":  3920.0,
    "moving":      false,
    "cycle_count": 42,
    "reason":      1
  },
  "where_lat":   35.9728,
  "where_lon":  -86.7653,
  "where_location": "Nashville TN USA"
}

The reason field is 0 (boot), 1 (heartbeat), or 2 (low battery). Notehub appends the where_* fields after cell-tower triangulation, so the firmware doesn't need any code to produce location data.

Device-side responsibilities. The Notecarrier CX's STM32 host is designed to sleep through most of every day. Its only jobs are managing the duty cycle and making quick decisions when something does happen. On each wake (from either a motion-state change or the daily heartbeat timer), the host works out why it woke, formats the right Note, hands it to the Notecard over I²C, and goes back to sleep within seconds. For the 23+ hours a typical container sits still each day, the host MCU stays in STM32 STOP2, drawing a few microamps, until the Notecard's ATTN pin wakes it. The Notecard's own low-power IMU does all the accelerometer monitoring, so the host is only involved for the few seconds it takes to queue a Note.

Notecard responsibilities. The Notecard does the patient work. Its built-in accelerometer watches for motion-state changes around the clock and fires the ATTN pin the instant the configured motion threshold is crossed. On every Notehub session it scans the surrounding cell towers and nearby WiFi APs for cell-tower and WiFi triangulation. Notehub turns that scan into a latitude/longitude and appends it to every event automatically, without any per-event firmware code. Notes queue locally through outages and go out in order when connectivity comes back. Environment variables pushed from Notehub arrive on each inbound sync, so a pool manager can retune motion sensitivity or heartbeat cadence without a firmware update or a truck roll.

Notehub responsibilities. The Notecard manages its own cellular session on supported carrier networks worldwide through its embedded global SIM and delivers data to Notehub over the Internet. From there, Notehub ingests and stores every event, resolves the cell-tower data into a human-readable location, and applies your project-level routes. The two streams are kept apart at the source on purpose (tote_event.qo for motion events and tote_heartbeat.qo for daily heartbeats), so each can go to a different destination with a different urgency, and the routes don't need filter logic. Fleets and Smart Fleets are the natural way to organize a mixed pool. A pool manager can set different motion thresholds for totes, kegs, and cylinders without ever forking the firmware.

Routing to the cloud (high level only). Notehub can route to HTTP, MQTT, AWS, Azure, GCP, Snowflake, and several other destinations, and route setup depends on your project. This project doesn't ship a specific downstream endpoint, so see the Notehub routing docs for details. A typical fleet integration sends tote_event.qo to a warehouse management system (WMS) or custom fleet portal in real time, and tote_heartbeat.qo to long-term storage for pool health analytics.

3. Technical Summary

  1. Clone this repo and open /firmware/tote_pool_tracker/tote_pool_tracker.ino.
  2. Define PRODUCT_UID as your Notehub project's ProductUID. Until you do, the #ifndef PRODUCT_UID guard near the top of the sketch stops the build with an error (see §6).
  3. Install the Arduino Core for STM32, Blues Wireless Notecard, and STM32duino Low Power libraries (see §7.1).
  4. Jumper the Notecarrier CX's ATTN pin to D5 (see §5).
  5. Build for the Cygnet board with USB support set to None, and upload (see §7.1).
  6. Power the assembly (USB or battery). The first time it connects over cellular, the device associates itself with your Notehub project, and it shows up in the Devices tab within 1–2 minutes.
  7. Tap or shake the assembly firmly for 3–4 seconds, and a tote_event.qo with "event":"departed" should arrive in Notehub within ~30 seconds. Set it down, and after 30 seconds of stillness an "event":"arrived" follows.

The sections below cover assembly, firmware, and Notehub configuration in detail.

Here's a sample Note from this device:

{
  "file": "tote_event.qo",
  "body": {
    "event":      "departed",
    "moving":     true,
    "battery_mv": 3851.0,
    "cycle":      14
  },
  "where_lat":   35.9728,
  "where_lon":  -86.7653,
  "where_location": "Nashville TN USA",
  "where_country": "US"
}

4. Hardware Requirements

note

Bench/POC BOM. These components build the bench prototype documented here. The Notecarrier CX and its LiPo charge path are the right platform for bench validation and limited field trials. A production deployment aiming for 3–5+ years between swaps needs a custom carrier board with a direct +VBAT input and a Li-SOCl₂ primary cell instead of the LiPo. §11 covers that path.

PartQtyRationale
Notecarrier CX1Integrated carrier with an onboard STM32L433 host MCU, so you don't need a separate host board. The host sleeps in STOP2 between wakes until the Notecard's ATTN pin wakes it.
Notecard Cell+WiFi (MBGLW)1Cellular connectivity plus a built-in accelerometer for motion detection. The Cell+WiFi variant turns on both cell-tower and WiFi AP triangulation (mode:"wifi,cell"), which improves location accuracy in AP-dense environments and falls back to cell-only automatically where there are no APs. Per the NOTE-MBGLW datasheet antenna requirements, the MBGLW includes a PCB antenna for its WiFi function, so no external WiFi hardware is needed in plastic or polycarbonate enclosures. The on-board PCB antenna scans nearby WiFi APs through non-metallic walls as is. The MBGLW also has a dedicated WiFi U.FL connector for swapping in an external 2.4 GHz antenna. Other Blues reference builds that put the same Notecard inside a metal enclosure (e.g., a commercial HVAC RTU sidecar) do connect an external lead to this connector. They do that because the metal enclosure blocks the on-board PCB antenna's 2.4 GHz signal, and the Notecard hardware itself is the same. In this plastic-tote deployment, the on-board PCB antenna is the right choice and all you need. In a metal enclosure or a metal-bodied container, the metal shields the 2.4 GHz signal no matter which antenna is connected inside. The on-board PCB antenna and any antenna sitting entirely inside the metal box are blocked equally, and WiFi AP triangulation will suffer unless the antenna's radiating element is mounted outside the metal shell or behind a verified non-metallic RF window. See the optional WiFi antenna row below.
Blues Mojo (commissioning/validation)1Coulomb counter spliced inline on the battery rail to measure real energy use during bring-up. It doesn't ship with the field tracker. It stays on the bench as commissioning and regression-testing equipment.
SparkFun Lithium Ion Battery, 2 Ah (PRT-13855)13.7 V / 2000 mAh LiPo with a JST PH connector that matches the Notecarrier CX battery port. It's the POC power source, with a realistic service life of 12–24 months. §11 covers the Li-SOCl₂ production path.
Hammond Manufacturing 1554C2GY (field deployment enclosure, IP67/NEMA 4X)1120 × 65 × 40 mm UV-stabilized polycarbonate enclosure, NEMA 4X / IP67–IP68, with a silicone gasket and flat lid. The internal cavity (~113 × 58 × 34 mm) fits the Notecarrier CX (76 × 38 mm board) lengthwise, with the LiPo pack lying flat on top of the board. It's rated for outdoor service at truck yards, rail interchanges, port terminals, and keg/cylinder washdown environments. A clear-lid variant, 1554C2GYCL, is available if you want to check the LEDs through the lid. You can get it from Mouser, DigiKey, and RS Components.
Hammond Manufacturing 1591XXBSFLBK (bench bring-up only, IP54, not suitable for outdoor or field deployment)1113 × 63 × 28 mm ABS enclosure, IP54, with a flanged gasketed lid and integral PCB standoffs. It's only adequate for bench bring-up and sheltered indoor environments. Do not use for outdoor field deployment. Truck yards, rail interchanges, port terminals, and container washdown workflows need at least an IP67 enclosure, so use the Hammond 1554C2GY above. Available from Hammond, Mouser, and RS Components.
SparkFun SMA to U.FL Cable, 150 mm (WRL-18568) (optional, external antenna path)1Carries the cellular signal from the Notecard's u.FL socket to an SMA female connector seated in the enclosure wall. You only need it for enclosed or stacked-container deployments where the bundled patch antenna can't see enough sky.
SparkFun LTE Hinged External Antenna, 698 MHz–2.7 GHz, SMA Male (CEL-16432) (optional)1Stub antenna (SMA male, 2.3 dBi) that covers all LTE Cat-1 bis bands globally. Thread the pigtail's SMA connector through a ¼″ panel hole in the enclosure wall, and this antenna screws straight onto the pigtail's SMA female end. Alternate: Taoglas TG.30.8113 (3 dBi, 698–2700 MHz, hinged SMA male) for ~0.7 dB more gain where coverage is challenging.
Blues Flexible Cellular/WiFi Antenna (SKU 233-00006) (optional, only useful if the radiating element can be placed outside any metal shielding)185 mm × 14.5 mm flexible FPC antenna (Molex), 698 MHz–4.0 GHz, with a 180 mm u.FL coaxial tail. Connect it to the Notecard's WiFi U.FL connector. In metal-enclosure or metal-bodied installations, this antenna only helps if the radiating element is routed outside the metal, either through a cable gland or behind a dedicated non-metallic RF window in the enclosure wall. Placed entirely inside a metal enclosure, it's still shielded by the metal and does no better than the on-board PCB antenna. Not required for plastic or polycarbonate enclosures, where the MBGLW's on-board PCB WiFi antenna scans nearby APs through the non-metallic walls as is.
Essentra Components CG-M16-1-BK Cable Gland, M16, IP68 (optional, cable-through variant only)1Only needed if you swap the stub antenna above for a remote-mount antenna with an integrated SMA cable (e.g., a magnetic-mount antenna on the outside of the shipping container). It seals the cable exit through the enclosure wall and accepts 3–10 mm cable diameters.

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.

5. Wiring and Assembly

Wiring and assembly: Notecard MBGLW in M.2 slot — cellular u.FL connected, WiFi PCB antenna built-in; LiPo on JST LIPO port; ATTN → D5 jumper so the Notecard can wake the host from STOP2; Mojo inline on battery rail (bench only)

There isn't much wiring in this design. The Notecard's built-in accelerometer handles all the motion detection on its own, and in a plastic or polycarbonate enclosure the MBGLW's on-board PCB WiFi antenna takes care of AP scanning without any external hardware. That leaves six short steps between an empty enclosure and a tracker ready to clip onto a container. You seat the Notecard, connect the antennas, fit the ATTN-to-D5 jumper, connect the battery, splice in the Mojo for bench validation if you want it, and close the enclosure.

1. Notecard. Slide the Notecard Cell+WiFi (MBGLW) into the Notecarrier CX's M.2 slot, label-side up, and press until it seats. The Notecarrier CX's STM32 host talks to the Notecard over the I²C bus routed through the M.2 interface, so that part needs no wiring.

2. Antennas. The MBGLW has two antenna connections.

Cellular: Connect the bundled cellular patch antenna to the Notecard's cellular u.FL connector. In sealed polycarbonate enclosures or stacked-container deployments where the patch antenna can't see much sky, run a u.FL-to-SMA pigtail to an SMA connector seated in a ¼″ hole in the enclosure wall, and screw an external stub antenna onto it (see the optional BOM items). The bundled patch antenna is fine for bench testing and most open-top plastic housings.

WiFi: Per the NOTE-MBGLW datasheet antenna requirements, the MBGLW includes a PCB antenna for its WiFi function, so no connection or external hardware is needed in plastic or polycarbonate enclosures. The on-board PCB antenna scans nearby APs through the non-metallic walls at each Notehub session, and Notehub resolves that data to a location automatically. The MBGLW also has a WiFi U.FL connector, where you can substitute an external 2.4 GHz antenna when the enclosure material calls for it. Reference builds that put the same Notecard inside a fully metal enclosure do connect an external lead to this WiFi U.FL connector. They need it because the metal enclosure blocks the on-board PCB antenna's 2.4 GHz signal, and every MBGLW unit has the connector either way. In this plastic-tote deployment the on-board PCB antenna is the right choice, and no WiFi U.FL connection is required. If the device will be housed in a metal enclosure or mounted inside a metal-bodied container, note that metal shields the 2.4 GHz signal. Both the on-board PCB antenna and any antenna left entirely inside the metal are equally blocked, so connecting a flex antenna to the WiFi U.FL connector won't help unless you route it outside the metal. To keep WiFi AP scanning in a shielded installation, the antenna's radiating element has to sit outside the metal shell. Route a 2.4 GHz u.FL antenna (e.g., Blues Flexible Cellular/WiFi Antenna, SKU 233-00006) through a cable gland or behind a non-metallic RF window in the enclosure wall, then connect the u.FL tail to the Notecard's WiFi U.FL connector. Where you can live without WiFi scanning, the device falls back to cell-only triangulation automatically.

3. ATTN-to-D5 jumper (host sleep). Fit a short jumper between the ATTN and D5 pins on the Notecarrier CX. Both are on the same 16-pin header (ATTN at position 3 and D5 at position 14). The Notecard pulls ATTN low while the host sleeps and raises it when a motion-state change or the heartbeat timer should wake the host. The rising edge on D5 then wakes the STM32 from STOP2. Leave the Notecarrier CX EN pin 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 walks through the full pattern.

4. Battery. Connect the SparkFun 2 Ah LiPo to the Notecarrier CX's 2-pin JST PH battery connector (labeled LIPO). The keyed connector enforces polarity, so don't force a reversed pack. The Notecarrier CX includes a LiPo charge circuit. The battery charges whenever USB power is present, and the tracker runs from the battery when USB is removed.

5. Mojo (bench bring-up only). For power validation during development, splice the Mojo inline between the LiPo JST connector and the Notecarrier CX LIPO input. Mojo's BAT+ / BAT- connect to the battery, and Mojo's LOAD+ / LOAD- connect to the Notecarrier CX. The Mojo Qwiic connector attaches to the Notecarrier CX's Qwiic port, and it reports cumulative mAh to the Notecard over I²C. This measures the full assembly, host plus Notecard, on battery power. Note: this firmware doesn't read Mojo data over I²C or publish any Mojo telemetry in Notes. Mojo serves only as a bench commissioning and regression-testing instrument, and it isn't deployed with the field tracker.

6. Enclosure. Mount the board and battery in the polycarbonate enclosure (Hammond 1554C2GY for field deployment, 1591XXBSFLBK for bench only), with any external cellular antenna cable routed through a cable gland. Attach the housing to the container with non-penetrating fasteners only: stainless zip ties or hose-clamp style straps on plastic totes, band clamps or collar brackets on kegs and cylinders, and industrial-grade VHB adhesive pads on flat polymer surfaces.

warning

Safety: pressurized vessels must not be drilled or tapped. Kegs, gas cylinders, and any other pressurized container must never be drilled, tapped, welded, or structurally modified in any way to mount a tracker. Use only external band clamps, collar brackets, or strap-style mounts that grip the vessel's exterior surface without compromising its structural integrity.

warning

Safety: not rated for classified or explosive atmospheres. This assembly is not ATEX, IECEx, Class I Division 2, or intrinsically safe certified. Unless the enclosure, battery, antenna, and complete assembly are fully certified, do not deploy it in any classified or explosive atmosphere, including the flammable-gas environments that come with certain gas-cylinder applications.

warning

Important: the ATTN jumper is the whole sleep mechanism. cxSleepUntilAttn() puts the host into STOP2 and counts on the ATTN→D5 rising edge to wake it. Without the jumper, the firmware sees that ATTN never went low, stays awake, and polls motion every 15 minutes instead. The tracker still works, but battery life takes a big hit. If you adapt this firmware to a different carrier board, wire ATTN to any interrupt-capable host GPIO and update CX_ATTN_PIN in cx_sleep.h. The Attention Pin Guide has background on the pin.

6. Notehub Setup

1. Create a project. Sign up at notehub.io and create a project. Copy the ProductUID, which looks like com.your-company.your-name:tote-tracker.

2. Set the ProductUID in firmware. Open tote_pool_tracker.ino and add #define PRODUCT_UID "com.your-company.your-name:tote-tracker" (with your value) above the #ifndef PRODUCT_UID guard near the top. You can also pass it as a build flag: -DPRODUCT_UID=\"com.your-company.your-name:tote-tracker\". Without one of those, the guard's #error stops the build, so an unconfigured binary can't be flashed by accident.

3. Claim the device. Power up the assembly. The first time it connects over cellular, the Notecard associates itself with your Notehub project, so there's no manual claim step. The device shows up in the Devices tab within a minute or two.

4. Organize into Fleets. Fleets group devices that share configuration. For a mixed container pool, one fleet per container type (totes, kegs, cylinders) or per customer region works well, and each fleet can carry its own motion_threshold tuned to how those containers get handled. Smart Fleets can auto-assign new devices to the right fleet based on a device name or serial number prefix, which saves a lot of manual setup once the pool gets big.

5. Set environment variables. In Notehub, select the fleet you created above and open Environment in the left sidebar. Every variable is optional, and the table shows the firmware defaults. The device fetches them on its next inbound sync, and changes take effect without reflashing.

VariableDefaultPurpose
heartbeat_hours24Hours between heartbeat Notes while the container is stationary. Range: 1–168. Longer intervals stretch battery life, and shorter ones keep "last-seen" timestamps fresh. Heartbeat Notes go out with sync:true, so each one opens its own cellular session right away instead of waiting on the outbound periodic timer. Changing this value reapplies hub.set outbound/inbound in the same wake cycle, which keeps the Notecard's inbound polling cadence (environment variable pulls) in step with the heartbeat schedule.
low_battery_mv3300Battery rail voltage (mV) below which the heartbeat Note's reason code becomes 2 (low battery). Use it as your cue to schedule a battery swap before the tracker goes dark.
motion_threshold5Accelerometer events per time bucket needed before the Notecard calls the container "moving." Raise it if vibrating dock floors cause false departures, and lower it for gentle handling like roller conveyors. Range: 1–20.
motion_bucket_sec30Length of each accelerometer counting bucket (seconds). Use it with motion_threshold to trade sensitivity against response latency. Range: 5–300.

6. Configure routes. Add one route for tote_event.qo (immediate motion events to a WMS, fleet portal, or alert webhook) and a second for tote_heartbeat.qo (daily pool health for a long-term analytics store). Because the Notefiles are separate at the source, each route can target its own destination and urgency without any filtering logic.

What to expect in Notehub

The Notecard starts a session early on first boot, so events usually show up within a few minutes. The exact timing depends on network registration and the initial sync, and a weak-signal site or busy carrier can stretch that to 5–10 minutes. Once the device is registered, the Events tab should show:

  • _session.qo: automatic Notecard housekeeping on each cellular session. Seeing it confirms the radio is reaching Notehub.
  • tote_heartbeat.qo: one per heartbeat_hours (default 24 h), sent with sync:true so each one reaches Notehub as soon as a session comes up after the on-device timer fires. The reason field is 0 on first power-on (boot), 1 on later timer wakes (heartbeat), and 2 when the battery is below low_battery_mv. Notehub appends where_lat, where_lon, and where_location once it has resolved the cell-tower data.
  • tote_event.qo: emitted with sync:true when the container crosses the configured motion threshold (it starts or stops moving). The event field is "departed" (stopped → moving) or "arrived" (moving → stopped).

7. Firmware Design

The firmware spans four files in the firmware/ directory. Keep them together, because the main sketch references the helpers:

FileRole
tote_pool_tracker.inoMain sketch: setup(), loop(), and global definitions
tote_pool_tracker_helpers.hShared types, constants, and extern declarations
tote_pool_tracker_helpers.cppAll helper-function implementations
cx_sleep.hHost sleep: card.attn request, STOP2 entry, ATTN wake

When you open or build the sketch directory, the Arduino toolchain compiles the headers and .cpp along with the .ino automatically.

7.1 Installing and flashing

Dependencies:

  • Arduino core for STM32: stm32duino/Arduino_Core_STM32. Add the index URL https://github.com/stm32duino/BoardManagerFiles/raw/main/package_stmicroelectronics_index.json under File → Preferences → Additional Boards Manager URLs. The Notecarrier CX's host uses the Cygnet board definition (see the build settings below).
  • Blues Wireless Notecard library: note-arduino. Install it from the Arduino Library Manager (search for "Blues Wireless Notecard") or with arduino-cli lib install "Blues Wireless Notecard". The note-arduino releases page shows which version is current when you install.
  • STM32duino Low Power library (and its dependency STM32duino RTC): stm32duino/STM32LowPower. It provides the STOP2 entry and ATTN-pin wake that cx_sleep.h uses.

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 (battery power, 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 is defined in tote_pool_tracker_helpers.h) 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 firmware/tote_pool_tracker/tote_pool_tracker.ino, apply the build settings above, and click 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 — point at the sketch *directory* so arduino-cli
# picks up the helper .cpp and headers automatically. 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/tote_pool_tracker
arduino-cli upload  -b STMicroelectronics:stm32:Blues:pnum=CYGNET,usb=none -p /dev/cu.usbmodem* firmware/tote_pool_tracker

firmware/tote_pool_tracker/sketch.yaml sets the same board options, so if you run arduino-cli compile in that directory without -b, it picks them up too.

Replace /dev/cu.usbmodem* with your actual port, which is COMx on Windows or /dev/ttyACM* on Linux.

After upload, serial output is disabled by default. Release builds leave out Serial calls on purpose to avoid the ~1 mA UART wake-time penalty on a battery-powered device. To turn logging on, uncomment #define DEBUG in firmware/tote_pool_tracker/tote_pool_tracker_helpers.h and reflash, then open the serial monitor at 115200 baud to watch each wake cycle. The host prints a few lines on first boot and on each wake after that, then goes quiet while it sleeps. A few seconds of activity per cycle is normal.

7.2 Modules

ResponsibilityFunction
Notecard one-time configuration (hub.set, card.voltage, card.triangulate, card.motion.mode)notecardConfigure()
Heartbeat Notefile template registrationdefineTemplates()
Environment variable fetch and clamped overridefetchEnvOverrides()
Motion configuration reapplication when env vars changeinline in loop() after fetchEnvOverrides()
hub.set outbound/inbound reapplication when heartbeat_hours changesinline in loop() after fetchEnvOverrides()
Motion state read from Notecard accelerometerreadMotionMoving()
Battery voltage read from Notecard rail monitorreadBatteryMv()
Immediate motion event emission (sync:true)sendMotionEvent()
Immediate daily heartbeat emission (sync:true, template-backed)sendHeartbeat()
Sleep timing, ATTN arming, and STOP2 entryenterSleep()

7.3 Motion and location strategy

Motion detection runs entirely on the Notecard's built-in accelerometer, which you configure with card.motion.mode. The motion parameter sets the threshold, meaning how many accelerometer events have to happen within a configurable time bucket before the Notecard flips the motion status from "stopped" to "moving" (or back). When the status changes, the Notecard fires the ATTN pin to wake the host. Reading the current status takes a simple card.motion request, and the Notecard answers with "mode":"moving" or "mode":"stopped".

Location comes from card.triangulate set to mode:"wifi,cell", which turns on both cell-tower and WiFi AP scanning. On each Notehub session the Notecard scans the surrounding cell towers and nearby WiFi APs. Notehub resolves the combined data to a latitude/longitude and appends the position (where_lat, where_lon, where_location, where_country) to every event automatically, without GPS hardware or any per-event firmware code. In AP-dense places like warehouses and distribution centers, the WiFi data tightens accuracy from the kilometer scale to tens of meters. Where the Notecard can't detect any APs, it falls back to cell-only triangulation automatically. The WiFi scan adds a little time to session startup (usually a few seconds, depending on AP density and radio conditions). Next to the LTE Cat-1 bis cellular session that's negligible, so in AP-rich environments the extra accuracy comes almost free. This use case only needs to know which site or city a container is at, and cell-plus-WiFi triangulation gives that precision at far lower power than GPS. WiFi AP scanning uses the MBGLW's on-board PCB WiFi antenna and works through plastic and polycarbonate enclosure walls without any external hardware. In metal-enclosure or metal-shielded installations, the metal blocks the 2.4 GHz signal, and WiFi AP triangulation will suffer unless the antenna's radiating element sits outside the metal shell (see §5).

7.4 Event payload design

tote_event.qo (motion events, untemplated, sync:true):

{
  "file": "tote_event.qo",
  "body": {
    "event":      "arrived",
    "moving":     false,
    "battery_mv": 3851.0,
    "cycle":      14
  },
  "sync": true
}

Motion events are untemplated. There are only a handful per container per day at most, and a flexible JSON schema is handy while the design is still changing. sync:true tells the Notecard to skip the outbound cadence timer and start a cellular session as soon as the Note is queued. The event usually reaches Notehub 15–60 seconds after the motion threshold is crossed, depending on network conditions and the configured bucket duration. Notehub appends the triangulated where_* fields before passing the event to any configured routes.

tote_heartbeat.qo (daily, sync:true, template-backed):

{
  "file": "tote_heartbeat.qo",
  "sync": true,
  "body": {
    "battery_mv":  3920.0,
    "moving":      false,
    "cycle_count": 42,
    "reason":      1
  }
}

sync:true makes the Notecard open a cellular session as soon as the Note is queued. That guarantees each daily heartbeat reaches Notehub as soon as a session comes up after the on-device timer fires. It doesn't wait for the next periodic outbound sync, and it doesn't depend on a motion-triggered session happening to come first. Heartbeats use a Note template registered at cold boot. All four fields are fixed-width (battery_mv is a 4-byte float, moving a 1-byte bool, cycle_count a 4-byte uint, and reason a 1-byte int), so every record is stored on the Notecard as a compact, fixed-length binary entry instead of a variable-length JSON blob. At one heartbeat a day over a three-year deployment, the bandwidth you save over raw JSON adds up on a constrained SIM data plan. The reason field is a numeric code: 0 = boot (first power-on), 1 = heartbeat (normal timer wake), and 2 = heartbeat_low_battery (battery below low_battery_mv). Using a number keeps the record fully fixed-width.

7.5 Low-power strategy

setup() runs once at power-up to configure the Notecard, and each pass through loop() is one wake cycle. After queuing the right Note, enterSleep() calls cxSleepUntilAttn() (in cx_sleep.h). That sends a card.attn request with mode:"sleep,arm,motionchange" and a seconds value equal to the time left until the absolute next_heartbeat_epoch deadline, so a motion wake re-arms only the time still owed instead of starting a fresh full interval. If the epoch isn't available yet (before the first cellular sync), the function falls back to the full configured interval. When failed_send_count is non-zero, sleep is also capped at RETRY_WAKE_SEC (15 minutes) so a pending Note gets retried promptly. The Notecard pulls the ATTN pin low, and the host enters STM32 STOP2, where it draws a few microamps and keeps its RAM. When the Notecard raises ATTN, the rising edge on D5 wakes the host and loop() runs again. Since the host resumes in place, the ToteState struct just stays in RAM, and nothing gets serialized to the Notecard.

Adding "arm,motionchange" to the sleep mode means the Notecard wakes the host on whichever comes first, a motion-state transition (the accelerometer threshold being crossed) or the heartbeat timer running out. motionchange only fires ATTN during the sleep window if arm is there too. Without it, the mode string accepts the keyword but never arms the wake source (per the asset tracking guide). Sleep current stays very low because the host is in STOP2, but total battery energy depends mostly on the number of cellular sessions. Each daily heartbeat and each "departed"/"arrived" motion event opens its own cellular session through sync:true. So a container that moves several times a day burns a lot more energy than one that sits still for a week.

Both Note types are queued with sync:true, so the Notecard opens a cellular session as soon as each Note is ready and skips the outbound cadence timer entirely. That guarantees each daily heartbeat reaches Notehub as soon as a session comes up after the on-device timer fires. It isn't held for the next periodic outbound sync, and motion-triggered sessions in between don't affect it. The firmware still sets hub.set outbound/inbound to match heartbeat_hours and reapplies it at runtime whenever that value changes. That keeps the Notecard's inbound polling cadence, which is what pulls environment variable updates from Notehub, in step with the heartbeat schedule.

7.6 Retry and error handling

  • If card.motion fails three times in a row (no response, an error, or an unrecognized mode), readMotionMoving() returns the previously saved was_moving state. That way a transient I²C failure can't be mistaken for a real motion-state change and set off a spurious event.
  • readBatteryMv() returns 0.0 on a NULL or error response. A battery_mv: 0 sentinel in a Note stands out from any normal in-range reading and flags that the voltage wasn't available, instead of quietly reporting a misleading non-zero value.
  • fetchEnvOverrides() writes resolved environment values to both the runtime g_* globals and the g_state.desired_* fields. If it fails (a NULL response or a Notecard error), it leaves both alone, so a brief connectivity outage can't quietly revert fleet tuning to the compile-time defaults.
  • After fetchEnvOverrides() returns, loop() compares the desired g_motion_threshold / g_motion_bucket_sec values with g_state.last_applied_motion_threshold / g_state.last_applied_motion_bucket_sec. If they differ, the firmware reissues card.motion.mode and only updates the last_applied_* fields once that succeeds, so a failed reissue gets retried automatically on the next wake.
  • heartbeat_hours works the same way through g_state.last_applied_heartbeat_hours. A desired value that differs from the last applied one triggers a hub.set reissue with the updated outbound/inbound cadence in the same wake cycle. The firmware also re-anchors the absolute heartbeat deadline to the new interval right away (when it has a valid epoch), so the device starts sleeping toward the new schedule without running out the old one first.
  • On a retry wake where the pending Note finally goes through, Branch 2 compares the current motion state with g_state.pending_moving (the motion state captured when the pending record was first created) and emits a follow-up event if they differ. For example, say a "departed" Note fails and the container stops before the retry succeeds. pending_moving is true (the container was moving when the event was queued), now_moving is false, and the firmware emits an "arrived" event on that same retry wake. The baseline is pending_moving and not g_state.was_moving, because was_moving gets updated at the end of every wake, failed retry wakes included. Across several retries it drifts toward the current motion state and would hide the difference. pending_moving is written exactly once per pending record and never changes during retries. Design limitation: the firmware holds at most one pending Note at a time. If the container changes state more than once during a long retry period, only the final motion state gets compared with the original baseline. This single-pending-Note model can't recover intermediate transitions that reverse and then reverse again.

7.7 Key code snippet 1: combined motion + timer sleep

After queuing the wake-cycle Note, the firmware arms a sleep with two wake conditions. It computes the sleep duration from the time left until the absolute heartbeat deadline, so motion wakes don't reset the daily schedule, and caps it at RETRY_WAKE_SEC when a Note needs retrying. Before the first cellular sync, when there's no epoch yet, it sleeps the full interval. cxSleepUntilAttn() sends the card.attn request (and checks the result, since the host stays alive to see the response), waits for ATTN to go low, and enters STOP2. Passing "arm,motionchange" adds the second wake source, so ATTN also rises if the motion status changes before the timer runs out.

void enterSleep(uint32_t now_epoch) {
    // Derive sleep duration from the remaining time to the absolute deadline.
    uint32_t sleep_sec = g_heartbeat_hours * 3600UL;
    if (now_epoch > 0 && g_state.next_heartbeat_epoch > now_epoch) {
        sleep_sec = g_state.next_heartbeat_epoch - now_epoch;
    }

    // Cap at the retry interval when a note.add failed this wake so the
    // dropped note is retried promptly rather than waiting a full heartbeat
    // interval.
    if (g_state.failed_send_count > 0 && RETRY_WAKE_SEC < sleep_sec) {
        sleep_sec = RETRY_WAKE_SEC;
    }

#ifdef DEBUG
    Stream *log = &debugSerial;
#else
    Stream *log = NULL;
#endif
    if (!cxSleepUntilAttn(notecard, sleep_sec, "arm,motionchange", log)) {
        // The Notecard didn't take the sleep request, or ATTN never went low
        // (check the ATTN -> D5 jumper). Stay awake for at most one retry
        // interval so motion is still polled, then run the cycle again.
        // ...
        uint32_t wait_sec = sleep_sec < RETRY_WAKE_SEC ? sleep_sec : RETRY_WAKE_SEC;
        delay(wait_sec * 1000UL);
    }
}

7.8 Key code snippet 2: cell-tower and WiFi AP triangulation configuration

The firmware sends this once at cold boot in notecardConfigure(), and loop() resends it on each wake until the Notecard confirms it. On every Notehub session after that, the Notecard scans cell towers and nearby WiFi APs automatically, and Notehub resolves the combined data and appends the position to each event.

if (!g_state.triangulate_confirmed) {
    J *req = notecard.newRequest("card.triangulate");
    JAddStringToObject(req, "mode", "wifi,cell");
    JAddBoolToObject(req, "on",  true);
    JAddBoolToObject(req, "set", true);
    J *rsp = notecard.requestAndResponse(req);
    if (rsp) {
        if (!notecard.responseError(rsp)) g_state.triangulate_confirmed = true;
        notecard.deleteResponse(rsp);
    }
}

7.9 Key code snippet 3: motion event with immediate sync

sync:true skips the outbound cadence timer and starts a cellular session as soon as the Note is queued. The event usually reaches Notehub 15–60 seconds after the motion threshold is crossed. sendMotionEvent() first copies the event ("departed" or "arrived"), motion state, battery reading, and cycle count into the g_state.pending_* record. doSendMotionNote() then builds the Note from that record, making up to three attempts, and clears it only once the Notecard accepts the Note. If every attempt fails, the record is still there for the next wake to replay:

J *req = notecard.newRequest("note.add");
JAddStringToObject(req, "file", FILE_EVENTS);
JAddBoolToObject(req,   "sync", true); // immediate delivery

J *body = JAddObjectToObject(req, "body");
JAddStringToObject(body, "event",      g_state.pending_event);
JAddBoolToObject(body,   "moving",     g_state.pending_moving);
JAddNumberToObject(body, "battery_mv", g_state.pending_battery_mv);
JAddNumberToObject(body, "cycle",      (int)g_state.pending_cycle);

J *rsp = notecard.requestAndResponse(req);
if (!rsp) continue;
bool ok = !notecard.responseError(rsp);
notecard.deleteResponse(rsp);
if (ok) {
    g_state.pending_note_type = PENDING_NONE;
    return true;
}

7.10 Key code snippet 4: heartbeat Note template

All four fields are fixed-width, so every heartbeat record encodes as a compact, fixed-length binary entry. TFLOAT32, TBOOL, and TUINT32 are type-hint macros from note-c, and 11 is the Note template system's 1-byte integer placeholder (the same one used throughout the reference accelerators). The REASON_* codes are 0 (boot), 1 (heartbeat), and 2 (low battery). template_confirmed is set only when the Notecard accepts the template, and loop() re-registers it on each wake until then.

J *req = notecard.newRequest("note.template");
JAddStringToObject(req, "file", FILE_HEARTBEAT);
JAddNumberToObject(req, "port", 10);

J *body = JAddObjectToObject(req, "body");
JAddNumberToObject(body, "battery_mv",  TFLOAT32); // 4-byte float
JAddBoolToObject(body,   "moving",      TBOOL);    // 1-byte boolean
JAddNumberToObject(body, "cycle_count", TUINT32);  // 4-byte unsigned int
JAddNumberToObject(body, "reason",      11);       // 1-byte int: see REASON_*

J *rsp = notecard.requestAndResponse(req);
if (rsp) {
    if (!notecard.responseError(rsp)) g_state.template_confirmed = true;
    notecard.deleteResponse(rsp);
}

8. Data Flow

Data flow: accelerometer or timer wake → host reads motion state + battery → tote_event.qo (sync:true, immediate) or tote_heartbeat.qo (sync:true, templated) → Notehub appends location + routes

Collected. On each wake cycle: the current motion state ("moving" or "stopped" from the Notecard's internal accelerometer), the battery voltage from the Notecard's rail monitor, and the wake-cycle counter. There are no external sensors to read, and the host is only awake for a few seconds per cycle.

Transmitted.

  • tote_event.qo: queued with sync:true on every motion-state transition, so the Notecard starts a cellular session as soon as the Note is ready instead of waiting on the outbound cadence timer. A normally active container sends a "departed" event when it starts moving and an "arrived" event when it stops, which gives you a matched pair for each transit leg. The Notecard delivers each Note to Notehub, which appends the triangulated location before passing it to any configured routes.
  • tote_heartbeat.qo: emitted once per heartbeat_hours (default 24 h) whether or not there's been motion, queued with sync:true, and delivered right away in its own cellular session. It guarantees a daily "last seen alive" confirmation in Notehub, even for containers that sit idle for days or weeks, and no matter what motion-triggered sessions happen in between.

Routed. Both Notefiles pass through Notehub, which resolves and appends where_lat, where_lon, where_location, where_country, and where_timezone to each event as it arrives. From there, routes can send the events to any supported destination. That might be an HTTP/HTTPS webhook into a WMS (warehouse management system) or TMS (transportation management system), MQTT to a real-time fleet portal, or Snowflake/cloud storage for historical pool analytics. Route setup depends on your project, and this reference design doesn't ship a downstream endpoint.

In-band alerts. The reason code 2 (low battery) in tote_heartbeat.qo is the main in-band signal for scheduling battery maintenance. Other alerting, like spotting containers outside their expected site boundaries, belongs downstream instead of in firmware (see §11).

9. Validation and Testing

Expected steady-state. A healthy tracker on an idle container sends one tote_heartbeat.qo a day and no tote_event.qo events. Both Note types use sync:true, so each opens its own cellular session and reaches Notehub as soon as that session comes up after the on-device event. A tracker on an active container sends anywhere from 0 to a dozen tote_event.qo events a day (one "departed" and one "arrived" per transit leg), plus one tote_heartbeat.qo. If neither file shows up in the Notehub Events tab after 5 minutes, the likeliest causes are a missing or wrong PRODUCT_UID, a disconnected antenna, or firmware built with USB support enabled (see §7.1).

Triggering a motion event on the bench. With the assembly powered (and the ST-LINK serial port open at 115200 baud, if DEBUG is enabled), tap or shake the Notecarrier CX firmly a few times. The Notecard's internal accelerometer samples slowly in motion-detect mode, so it takes a few seconds of steady motion to rack up enough events to cross the bucket threshold. A firm 3–4 second burst of taps is usually enough to clear the default threshold of 5. A tote_event.qo with "event":"departed" should appear in Notehub within ~30 seconds. Set the device down, wait out the bucket duration (30 seconds of inactivity by default), and "event":"arrived" follows.

Using Mojo to validate power behavior. Notecard's published idle current in low-power mode is about 8 µA at 3.7 V between Notehub sessions. The Notecard low-power design guide has the authoritative measured figures across SKUs and operating conditions. A transmit burst for a single queued Note usually lasts from a few seconds to tens of seconds at 50–250 mA average on LTE Cat-1 bis, and most of that is radio warm-up and network registration. The STM32L433 host in STOP2 adds roughly 1–2 µA.

Here's the expected 24-hour energy at one heartbeat a day, with no motion events and good signal (bench prototype, LiPo supply):

PhaseDurationApprox. currentApprox. energy
Deep sleep (Notecard idle, host in STOP2)~23.98 h~10 µA~0.24 mAh
Heartbeat wake and Note queue (~5 seconds host + I²C)~5 seconds~15–20 mA~0.03 mAh
Heartbeat sync:true cellular session (~20 seconds at ~150 mA avg LTE Cat-1 bis)~20 seconds~150 mA~0.83 mAh
Total active drain~1.1 mAh/day

On paper, the 2000 mAh LiPo would cover that active drain for ~5 years. That ignores LiPo self-discharge, though, and at this duty cycle self-discharge dominates. A typical 2000 mAh LiPo self-discharges at 1–2% per month (~480 mAh/year), which eats roughly half the pack's capacity over 2 years even while the tracker sits still. Once you account for self-discharge and cell aging, a realistic LiPo service life is 12–24 months in a low-motion, good-signal deployment. Weak-signal retries, motion events, and cold temperatures each shorten that further. That's why this build is documented as bench/POC and not production. Multi-year deployments need the Li-SOCl₂ primary-cell path described in §11.

Getting 3 to 5+ years between swaps takes a Li-SOCl₂ primary cell (under 1% annual self-discharge and much higher volumetric capacity). That in turn needs a different carrier with no charging circuitry (see §11). Whatever the chemistry, the energy figures in the table above still help you size the active-drain budget.

For a useful Mojo bench check, leave the assembly running for 24 h and confirm the Mojo tally lands in the 1–5 mAh range. If it's off by more than ~5×, the cause is usually one of these:

  • Host never sleeping: a flat, continuous 10–80 mA baseline. The usual causes are a missing ATTN→D5 jumper (the firmware then waits awake and retries every 15 minutes) or firmware built with USB support enabled (the host leaves STOP2 within a millisecond). Check the jumper and the usb=none build option (see §7.1).
  • Excessive cellular retries: sync bursts run longer than expected or fire more than once a day. Check signal strength with card.wireless or hub.status in the blues.dev In-Browser Terminal.
  • Motion false triggers: tote_event.qo Notes pile up from dock-floor vibration or transport. Raise motion_threshold with the Fleet environment variable and watch the event rate drop, with no reflash needed.

10. Troubleshooting

Device doesn't appear in Notehub Devices tab after 3–5 minutes.

  • Make sure the PRODUCT_UID in your sketch matches exactly (copy and paste it from Notehub instead of retyping it). A mismatch is the most common cause.
  • Check the serial monitor at 115200 baud (uncomment #define DEBUG in tote_pool_tracker_helpers.h and reflash to see Notecard I²C trace output).
  • Confirm USB power is connected or the LiPo battery is seated properly in the JST connector (the keyed connector enforces polarity, so don't force it).
  • Check that the Notecard is fully seated in the M.2 slot.
  • Check signal strength. The Notecard may be registered but slow to sync, so give it up to 10 minutes in a weak-signal area.

Device appears but never sends an event.

  • Shake or tap the assembly firmly for 3–4 seconds. The accelerometer samples slowly in motion-detect mode, so it needs sustained motion to accumulate events. Gentle handling or one quick tap won't cross the motion threshold.
  • Confirm the motion threshold suits your environment. If it's set high (e.g., motion_threshold: 15), normal dock handling may not register. Lower the value in the Fleet → Environment panel and wait for the next inbound sync (~24 hours by default, or sooner, on the next cellular session, if you set off a motion event).
  • Make sure tote_heartbeat.qo shows up at least once a day in the Events tab. If heartbeats never arrive, motion events won't either, since both come down to the same connectivity or configuration problem.

Event arrives once per minute instead of at motion state changes.

  • The motion threshold is too sensitive. Raise motion_threshold from 5 to 10–15 in Fleet → Environment to cut false triggers from dock vibration, and increase motion_bucket_sec to 60 or higher so motion has to last over a longer window.

Battery drains much faster than expected (>5 mAh/day consumed).

  • Confirm the host is really entering STOP2, either with the Mojo coulomb counter or by measuring the battery rail directly. If the host draws a continuous 10–80 mA, check the ATTN→D5 jumper and confirm the firmware was built with USB support set to None (see §7.1). If you're adapting this design to a different carrier, wire ATTN to an interrupt-capable host GPIO and set CX_ATTN_PIN in cx_sleep.h (see the Attention Pin Guide).
  • Check signal strength, because weak-signal retries add cellular sessions and burn more energy. Run card.wireless in the blues.dev In-Browser Terminal to see RSSI and bars.
  • If motion events are piling up more than once a day, raise motion_threshold and increase motion_bucket_sec to cut down on false triggers.

Event payload is missing a field (e.g., cycle is absent).

  • Check the firmware version, since early versions may have had different payloads. Make sure you've uploaded the latest sketch and that the installed Blues Wireless Notecard library is up to date.

11. Limitations and Next Steps

This reference design is scoped to bench validation and limited field trials. It's meant to be the fastest way from "we should track these containers" to a live cellular uplink with site-level location. A few scope choices keep that path short on purpose. Each one is listed below with the production hardening that turns a 12–24 month LiPo prototype into a fleet that stays deployed for years.

Simplified for the POC

Each choice below keeps the prototype quick to build, and each comes with the work it needs before the trackers go out on a real container pool.

LiPo battery vs. Li-SOCl₂ is the primary scope constraint. This POC build runs a rechargeable 2 Ah LiPo on the Notecarrier CX's charge-enabled power path. As §9 explains, LiPo self-discharge caps practical service life at 12–24 months no matter how low the active drain is. That's the main reason this build is documented as a bench prototype and not a production deployment. For real multi-year deployments, with 3 to 5+ years between swaps, the right chemistry is Li-SOCl₂ (e.g., a D-size Tadiran TL-5930 at 3.6 V / 14.5 Ah, under 1% annual self-discharge). The catch is that the Notecarrier CX's onboard charge IC is designed for LiPo chemistry. Connecting a Li-SOCl₂ primary cell directly to the JST LIPO port would run the charge circuit against a non-rechargeable cell, which is unsafe. A production deployment on Li-SOCl₂ needs a purpose-built carrier board with no charging circuitry and a direct +VBAT input matched to the primary cell chemistry. That power path has to be designed from scratch instead of adapted from a charge-enabled design. The Notecarrier CX documented here is the right platform for prototyping and limited field trials.

Not rated for classified or explosive atmospheres. This build uses standard commercial electronics and a LiPo battery, and none of them carry ATEX, IECEx, Class I Division 2, or intrinsically safe certification. Deployments in classified or explosive atmospheres (including certain gas-cylinder and industrial-gas environments where flammable gases or vapors may be present) need a purpose-certified assembly, where the enclosure, battery, antenna, and all the electronics carry the right hazardous-location rating. That certification work is far beyond the scope of this POC.

Enclosure fit and gland rating need checking when you adapt the design. The main BOM specifies the Hammond 1554C2GY (NEMA 4X / IP67, polycarbonate) for field deployment and lists the Hammond 1591XXBSFLBK (IP54, ABS) only for bench bring-up. If you switch to a different enclosure, check the interior cavity against the Notecarrier CX footprint (76 × 38 mm board) and the flat LiPo pack before you commit. Inner dimensions tighter than ~95 × 50 mm mean stacking the board and battery instead of laying them flat. Any cable gland where a cellular antenna pigtail leaves the enclosure has to be rated to the same IP level as the enclosure body. The Essentra M16 IP68 gland in the BOM covers that for both the 1554C2GY and any IP68-rated replacement.

Triangulation accuracy depends on the surroundings. This build defaults to cell-tower and WiFi AP triangulation (mode:"wifi,cell"). In AP-dense places like warehouses, distribution centers, and port terminals, the combined mode can get accuracy down to 15–200 m, which is plenty to tell individual customer sites apart. In rural or outdoor areas with few APs, the device falls back to cell-only, which gives roughly 300 m to 5 km accuracy depending on tower density. That's still good enough for site-level identification in most cases. If you need sub-50 m precision (a specific dock door rather than a facility), the upgrade path is a GPS fix through card.location.mode periodic. It costs you a GPS antenna, up to a few minutes of cold-start latency, and extra power for every fix.

No server-side geofencing. The POC reports locations but doesn't evaluate them. If you want an alert when a container leaves an expected zone or turns up at an unexpected site, build it in the downstream fleet application or as a Notehub JSONata route transform. The transform would compare where_lat/where_lon against fleet-level environment variables that define site polygons, and fire a webhook when a container shows up outside every known site. Either way, the site boundary data can change without a firmware update.

No transit-leg pairing. The firmware reports "departed" and "arrived" events on their own. Stitching them into origin-destination legs (Container #4712 left Supplier A at 08:15 and arrived at Customer B at 14:42) takes logic in the fleet application, which has the context to make the match.

Single wake reason. The firmware never asks the Notecard which event raised ATTN. It works the reason out from state instead. A motion-state change means a motion wake, a pending Note means a retry wake, and anything else is checked against the heartbeat deadline using card.time. Until the Notecard has valid time, a wake with nothing else to explain it is treated as a heartbeat. Asking the Notecard what fired would make that explicit, at the cost of another I²C round trip on every wake.

No tamper or shock detection. The accelerometer could pick up sustained unusual orientations or sharp impact signatures. Both are workable firmware extensions, but this POC doesn't implement them.

Production Next Steps

Getting from this prototype to a fleet that runs for years starts with redesigning the power path, then adding the cloud-side logic a real tracking operation depends on.

A custom carrier board with a direct +VBAT input (and no charging circuitry) can run a D-cell Li-SOCl₂ primary, leaving the Notecarrier CX form factor for prototyping only.

Voltage-variable sync through hub.set voutbound/vinbound lets the Notecard cut back on cellular sessions by itself as the battery drains, once you configure the voutbound/vinbound voltage thresholds. The shipped firmware only sends hub.set with fixed outbound/inbound. To turn on voltage-variable sync, you'd need either (a) a small firmware change that adds voutbound/vinbound fields to the hub.set call, plus a check that the hub.set reissue that fires when heartbeat_hours changes doesn't overwrite them, or (b) a one-time commissioning step in the blues.dev In-Browser Terminal that sets the fields directly on each device.

Server-side geofencing in Notehub JSONata would compare where_lat/where_lon against fleet-level environment variables that define site polygons, and fire a route to a webhook when a container shows up outside every known site.

Notecard Outboard DFU delivers over-the-air host firmware updates to the whole fleet. That matters a lot for a tracker that may stay on a container for 5+ years without a hands-on service visit.

Per-container asset metadata (container ID, type, tare weight, inspection due date) can live in Notehub device-level environment variables, and a JSONata route transform can append it to events. The fleet portal then shows full container profiles without the tracker storing or sending any of that data itself.

12. Summary

The pool manager used to find out about container shrinkage at quarter-end, when the replacement budget finally caught up with reality. Now every container reports back on its own schedule. A tote_event.qo Note arrives within ~30 seconds of a container starting or stopping, and a tote_heartbeat.qo comes in once a day even when it sits perfectly still. Each one carries battery state plus a location that Notehub resolves from cell-tower and WiFi AP triangulation and appends automatically. Assets that spend most of their time sitting still need a tracker that clips on, wakes only when something interesting happens, and costs next to nothing to run between wakes. The Notecard's built-in accelerometer does the patient work of watching for motion at microamp power levels, and its ATTN pin keeps the host MCU in STOP2, drawing a few microamps, until there's something to report. Because the cellular service is prepaid, the same hardware and firmware deploy the same way at an urban cold-storage facility and a rural distribution depot, with no IT coordination at either site. The data that comes back tells you where each container last checked in, whether it moved today, and whether its battery is healthy. That's enough to run weekly reconciliations without manual cycle counts, flag containers that haven't moved in 30 days for recovery calls, and catch customers holding containers past their agreed dwell time before it snowballs into a shortage emergency.

On This Page

  • 1. Project Overview
  • 2. System Architecture
    • Event Format in Notehub
  • 3. Technical Summary
  • 4. Hardware Requirements
  • 5. Wiring and Assembly
  • 6. Notehub Setup
    • What to expect in Notehub
  • 7. Firmware Design
    • 7.1 Installing and flashing
    • 7.2 Modules
    • 7.3 Motion and location strategy
    • 7.4 Event payload design
    • 7.5 Low-power strategy
    • 7.6 Retry and error handling
    • 7.7 Key code snippet 1: combined motion + timer sleep
    • 7.8 Key code snippet 2: cell-tower and WiFi AP triangulation configuration
    • 7.9 Key code snippet 3: motion event with immediate sync
    • 7.10 Key code snippet 4: heartbeat Note template
  • 8. Data Flow
  • 9. Validation and Testing
  • 10. Troubleshooting
  • 11. Limitations and Next Steps
    • Simplified for the POC
    • Production Next Steps
  • 12. Summary

Resources

  • GitHub
  • Request More Info
Can we improve this page? Send us feedback
© 2026 Blues Inc.
© 2026 Blues Inc.
AboutDocsAPI ReferenceTermsPrivacy