Propane / LPG Tank Fill Telemetry

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/propane_tank_telemetry/ so it can learn how the device works and help you adapt it to your hardware.
This project is a truck roll reduction reference design that gives propane dealers fill telemetry for every tank in their delivery territory. It swaps fixed-schedule routes for demand-driven dispatch and projects days-until-empty for each tank in the fleet. A level sensor at the tank's existing gauge port and a temperature probe on the tank shell let each tank report on itself. A single Notecard for Skylo (one module with cellular, Skylo satellite, and WiFi radios that fails over between them automatically) gets the data out from a tank three minutes outside town just as easily as from a remote mountain cabin beyond any cell tower. One SKU and one firmware image cover every site type in the territory.
1. Project Overview
The problem. Most propane dealers still run delivery routes on a fixed calendar, where every tank gets a truck every six weeks. That schedule doesn't track demand. It's just the only option when dealers can't tell which tanks actually need filling. So trucks roll to tanks sitting at 60%, while tanks on remote farm properties run dry between scheduled visits. Neither failure is exotic. Both follow directly from not having fill-level data.
The root cause is infrastructure. A propane tank sits in a field, on a farm, at a cabin, or at a rural business. It's almost never near a WiFi access point the dealer can use, and it's often somewhere cellular is marginal or missing entirely. The tank itself is a sealed pressure vessel with no way to communicate. Retrofitting telemetry means solving three problems at once: connectivity that varies by site, sensing that's specific to LP (liquefied petroleum) gas vessels, and turning raw fill readings into data a dispatcher can act on.
This project handles all three. A weatherproof electronics enclosure mounts on a post or bracket outside the AHJ-defined classified area. Field wiring connects it to a 4-20 mA LP gauge-port float transmitter at the tank's existing dip-tube gauge port and to a DS18B20 temperature probe strapped to the tank shell. Every 15 minutes the device wakes, reads the transmitter current and tank temperature, converts the transmitter's linear 4–20 mA output directly to fill percentage, updates a smoothed daily consumption rate, and reports a daily summary to the Blues Notehub cloud service. When fill drops below a configurable low-fill threshold, an alert goes out right away. The dealer's dispatch system sees fill %, gallons remaining, and projected days-until-empty for every tank in the fleet, which is enough to replace the calendar with a route that only rolls a truck when a tank actually needs it.
Why Notecard for Skylo. Propane tanks sit at farms, cabins, rural
businesses, and homes. Most have no customer WiFi the dealer can use, and many
are in areas where cellular coverage is spotty or missing. A dealer network
covers all of these site types and can't afford a different hardware setup for
each one. Notecard for
Skylo
(NOTE-NBGLWX) puts three radios on one M.2 module and picks among them
automatically: cellular (LTE-M / NB-IoT / GPRS), satellite over the
Skylo non-terrestrial network (NTN), and WiFi. The
firmware sets a single
card.transport
preference of wifi-cell-ntn. That tells the Notecard to use WiFi where a
provisioned AP happens to be in range (the rare residential tank), fall back to
cellular (the de facto primary across most of the territory), and fall back
again to Skylo satellite at remote sites. The Notecard handles failover, so the
host firmware never branches on which network is live.
This used to be a two-device decision, with a cellular Notecard for in-coverage tanks and a separate satellite add-on for the rest. Now it's a single part number and antenna kit, plus one firmware image that deploys unchanged across the whole fleet. Notecard for Skylo ships with an active global SIM that includes 500 MB of cellular data and 10 years of service, plus 10 KB of bundled Skylo satellite data, with no activation fees and no monthly per-SIM commitment. When a site turns out to have weaker coverage than the survey suggested, there's nothing to swap. The same board that runs on cellular near town reaches the Skylo network at the cabin on its own.
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.
Deployment scenario. A weatherproof NEMA 4X enclosure mounts on a separate post, wall, or bracket outside the AHJ-defined classified area (see the safety notice in §4) and runs off a solar-charged 12 V sealed lead-acid battery. A 4-20 mA LP gauge-port float transmitter (Rochester Sensors M6300-LP Magnetel® gauge + R6315-12 transmitter, or equivalent) connects at the tank's existing 1¼″ NPT dip-tube gauge port, and field wiring runs from the transmitter to the electronics enclosure outside the hazardous boundary. A waterproof DS18B20 temperature probe clamps to the tank shell, and its readings go into the daily summary Notes for seasonal demand analytics. Notecard for Skylo's antenna cables leave the enclosure for outdoor-mounted antennas with a clear sky view. One Skylo-certified antenna carries both cellular and satellite, so where there's no cellular coverage, the board falls back to the Skylo NTN satellite network on its own. It stays on the same Notehub project, with no antenna swap and no firmware changes. The tank itself isn't modified, and the install needs neither on-site internet infrastructure nor the OEM's cooperation.
2. System Architecture
Device-side responsibilities. Inside the post-mounted enclosure, the
Notecarrier CX's STM32 host wakes every sample_interval_min (default 15
minutes), reads the 4-20 mA float transmitter and the DS18B20 shell probe,
converts the linear current straight to fill percentage and gallons remaining,
and updates a smoothed consumption-rate estimate. If a threshold trips, it
queues an alert Note on the spot. Otherwise it goes straight back to sleep. Once
per report_interval_hr (24 hours by default) it sends a templated summary
with the current fill %, fill gallons, the minimum fill seen in the window,
window-averaged temperature, daily consumption rate, and projected
days-until-empty. Between wakes the host sleeps in STM32 STOP2 (a few µA) until
the Notecard's ATTN pin wakes it through
card.attn, while
Notecard for Skylo idles at ~8 µA. Every queued
Note travels over I²C, and the host never
hand-rolls JSON or sends an AT command.
Notecard responsibilities. Notecard for Skylo does the same job no matter
which radio is live. It holds Notes in its on-device queue, opens a session on
the hub.set outbound
cadence, and pushes any sync:true alert through right away. The
card.transport wifi-cell-ntn preference set at boot picks the path
automatically, with no firmware branching. That's WiFi where a provisioned AP
is reachable, cellular at most sites, and Skylo satellite at remote ones.
Whatever the path, the module pulls environment
variables
from Notehub on every inbound sync, so a dispatcher can retune transmitter
calibration or alert thresholds for any tank in the fleet without anyone driving
out to it. Over satellite that depends on an environment variable template,
which the firmware registers at boot (see §7).
Notehub responsibilities. Events arrive in Notehub over whichever transport carried them, and Notehub ingests and stores them and passes them on through project-level routes. Per-fleet environment variables let a single firmware image handle tanks of every capacity, because the tank size and sensor calibration live in Notehub instead of in compiled constants. Smart Fleets can group tanks by territory, capacity class, or customer type so the right calibration values reach the right tanks automatically.
Routing to the cloud (high level). Notehub can route to HTTP, MQTT, AWS, Azure, GCP, Snowflake, and several other destinations, and route setup depends on your project. This reference design doesn't ship a specific downstream endpoint, so see the Notehub routing docs for details. For a propane dealer, the obvious targets are a route-optimization or ERP system (through an HTTP webhook) and a time-series database for trend analysis (in whatever cloud data store the dealer prefers).
3. Technical Summary
Clone this repository, build the firmware, and deploy it:
-
Get the firmware onto your Notecarrier CX:
arduino-cli core install STMicroelectronics:stm32 arduino-cli lib install "Blues Wireless Notecard" "STM32duino Low Power" "STM32duino RTC" OneWire DallasTemperature arduino-cli compile -b STMicroelectronics:stm32:Blues:pnum=CYGNET,usb=none firmware/propane_tank_telemetry/ arduino-cli upload -b STMicroelectronics:stm32:Blues:pnum=CYGNET,usb=none -p /dev/ttyACM0 firmware/propane_tank_telemetry/Change the port (
/dev/ttyACM0on Linux/Mac,COM*on Windows) if you need to. Theusb=noneboard option disables the USB CDC stack, which has to be off for the host to sleep (see §7).firmware/propane_tank_telemetry/sketch.yamlsets the same board options, so runningarduino-cli compilein that directory without-bpicks them up too. -
Claim your Notecard to Notehub: Sign up at notehub.io and create a project. Copy the ProductUID and paste it into the firmware as
PRODUCT_UID. -
Configure fleet variables in Notehub: Set these in Projects → Environment (tab) at the Fleet level:
tank_capacity_gal: your tank's usable capacity (e.g., 500)fill_alert_pct: alert threshold (default 20)- Everything else has a sensible default. See §6 for the full list.
-
See your first event: After a few minutes of network registration, check Notehub → Devices → [Your device] → Events. If your tank is below the alert threshold, a
tank_alert.qoshows up within minutes. The firsttank_status.qodaily summary Note arrives onereport_interval_hr(24 hours by default) after the Notecard first syncs its clock, since the firmware waits out a full reporting window before sending it. That Note's body carriesfill_pct,fill_gal,temp_c,gal_per_day, anddays_until_empty.
§4–§9 cover full assembly and commissioning.
Here's a sample Note from this device:
{
"file": "tank_status.qo",
"body": {
"fill_pct": 42.3,
"fill_gal": 211.5,
"min_fill_pct": 38.1,
"temp_c": 14.8,
"gal_per_day": 8.2,
"days_until_empty": 25.8,
"transmitter_ma": 10.8
}
}4. Hardware Requirements
| Part | Qty | Rationale |
|---|---|---|
| Notecarrier CX | 1 | Integrated carrier with an onboard STM32L433 host. It handles the 12-bit ADC for the 4-20 mA loop and OneWire for the temperature probe, so you don't need an external MCU. |
| Notecard for Skylo (NOTE-NBGLWX) · datasheet | 1 | One M.2 module carrying cellular (LTE-M / NB-IoT / GPRS, Quectel BG95-S5 modem), Skylo satellite (NTN), and WiFi (Silicon Labs WFM200S) radios. The firmware's card.transport wifi-cell-ntn setting makes it prefer WiFi where a provisioned AP is in range, use cellular at in-coverage tanks, and fall back automatically to the Skylo satellite network at remote sites beyond cellular reach, so there's no second device or part number to decide on. Cellular removes the per-site IT dependency, and the embedded global SIM and bundled satellite allocation cover the whole territory from one SKU. Requires the antennas below. |
| Blues Mojo | 1 | Bench only. Coulomb counter on the 5 V supply rail for checking power behavior during commissioning (see §9). Remove and discard it for field deployment, since it isn't integrated into production units. |
| Rochester Sensors M6300-LP Magnetel® Rough Rider® LP gas gauge + R6315-12 4-20 mA transmitter: M6300-LP datasheet · R6315-12 datasheet · 6300 series installation guide. Available from John M. Ellsworth Co. and LP gas equipment distributors. | 1 | Float-type magnetel level transmitter designed specifically for LP gas service. A float rides the liquid propane surface inside the tank and drives a two-wire 4-20 mA current loop proportional to fill level: 4 mA at 0 % fill, 20 mA at 100 % fill. Representative orderable assembly: M6300-LP gauge body (1¼″ NPT, 4″ dial) with the R6315-12 transmitter module attached. The R6315-12 clips to the M6300 gauge body and provides the 4-20 mA output, proportional to the float position. See the R6315-12 datasheet for electrical specifications and wiring. Mounting: the M6300-LP installs at the tank's existing 1¼″ NPT dip-tube gauge port (the standard gauge opening on most horizontal residential and commercial LP tanks). Dip-tube length and float arm length vary by tank geometry, so check the correct M6300-LP model suffix against the Rochester Sensors product application table and the tank's nameplate before ordering. Some larger commercial tanks use a 2″ NPT gauge boss, and Rochester Sensors offers matching variants. Electrical: two-wire loop-powered, 12–28 V DC supply. Note: the float tracks the liquid surface directly, so the firmware applies no density or temperature correction. The 4-20 mA output is already proportional to fill level regardless of liquid temperature or density. Installation at a propane pressure vessel must be performed by a licensed LP gas technician per NFPA 58. |
| SparkFun Waterproof DS18B20 Temperature Sensor (SEN-11050) | 1 | OneWire temperature probe clamped to the tank shell. Its readings go into daily summary Notes so the cloud side can correlate consumption with ambient conditions. |
| 120 Ω 0.1 % precision resistor | 1 | Shunt across the 4-20 mA current loop. Converts 4–20 mA to 0.48–2.40 V DC at the host's A0 ADC pin. The 120 Ω value leaves safe electrical headroom for standard 4–20 mA transmitter fault currents. A 24 mA diagnostic output produces only 2.88 V at A0, well within the 3.3 V ADC absolute maximum. The firmware rejects currents outside the 3.5–21 mA valid window as NAN and emits a sensor_fault alert, and with this shunt the ADC is electrically safe up to ~27 mA (3.3 V ÷ 120 Ω). |
| 4.7 kΩ resistor | 1 | OneWire pull-up for the DS18B20 data line. |
| 12 V DC/DC step-down module, 5 V / 2.5 A output (e.g. Pololu D24V22F5) | 1 | Steps the 12 V battery rail down to 5 V for the Notecarrier CX VBAT input. This design needs a 2.5 A continuous output rating, because Notecard for Skylo's onboard modem (Quectel BG95-S5) can demand up to ~2 A in brief bursts and a 1 A regulator can't reliably source that peak. The D24V22F5 delivers 5 V at up to 2.5 A continuous from a 5.3–36 V input, which leaves comfortable headroom above the Notecard's 2 A burst demand plus the STM32 host's active-mode draw. |
| 24 V DC/DC boost converter module, input 8–16 V, output regulated 24 V, ≥ 100 mA (e.g. Pololu U3V50F24) | 1 | Provides a stable, regulated 24 V supply for the 4–20 mA transmitter current loop. With a regulated 24 V loop supply, the transmitter stays in compliance across the full SLA discharge cycle, any practical cable run, and an AHJ-required IS barrier. See the loop compliance calculation in §5 Step 7. Do not drive the transmitter loop from the 12 V system rail. When the battery sags (11–11.5 V under partial charge and load), the loop supply falls below the R6315-12's 12 V minimum specification, and any IS barrier in the loop cuts the margin further. The Pololu U3V50F24 accepts inputs from 2.9 V up to its 24 V output and delivers a regulated 24 V at up to 500 mA. It runs from the same 12 V system rail as the 5 V step-down module. |
| Skylo-certified LTE/satellite antenna included with Notecard for Skylo (u.FL) | 1 | Connects to the MAIN u.FL port and carries both the terrestrial cellular signal and the Skylo satellite link, so one antenna covers both networks. Use only the Skylo-certified antenna supplied with Notecard for Skylo, because an uncertified antenna risks regulatory non-compliance and link failure. Mount it outdoors with an unobstructed view of the sky (northern hemisphere: the southern sky) so the board can reach Skylo wherever it falls back from cellular, and route it through a cable gland. Don't rely on the bare antenna inside a polycarbonate enclosure at tank-side, since the signal margin is too variable. |
| u.FL-to-SMA-F bulkhead pigtail, ~15–20 cm (e.g. Taoglas CAB.0150.A.01 or equivalent) | 0–1 | Optional. You only need it to mount an external SMA-terminated antenna instead of routing the bare u.FL antenna through the enclosure wall. It adapts the MAIN u.FL port to an external SMA bulkhead, which then connects to the external antenna cable outside. Skip it if you route the included Skylo-certified antenna straight through a gland. |
| Passive GPS/GNSS antenna (u.FL) per the Notecard for Skylo datasheet | 1 | Connects to the GPS u.FL port for GNSS time/location. Mount it outdoors with a clear sky view next to the main antenna, and route it through a cable gland. |
| IP68-rated PG-11 nylon cable gland, 5–10 mm cable OD (e.g. Lapp SKINTOP® MS-M PG 11 or equivalent) | 5 | One gland per field cable entering the enclosure. This installation needs penetrations for: (1) the Skylo-certified MAIN antenna lead (or its SMA bulkhead); (2) the GPS/GNSS antenna lead; (3) the DS18B20 temperature probe cable; (4) the 4–20 mA transmitter loop wiring (two conductors from transmitter to enclosure); and (5) the solar panel cable (skip this one if you use an MC4 pass-through instead, as in §5 Step 2). Every penetration must be sealed to keep the NEMA 4X enclosure rating. Pick each gland's cable-OD range to match that cable's outer diameter. |
| 12 V sealed lead-acid battery, 7–10 Ah (e.g. Universal Power Group UB1280, 8 Ah) | 1 | Stores energy for cloudy stretches and overnight. At this device's duty cycle, 8 Ah gives ≥3 days of autonomy without solar input (see Limitations for the power budget). |
| Inline fuse holder + 5 A automotive blade fuse (e.g. Bussmann BP/HHM + ATC5) | 1 | Goes in the battery (+) lead as close to the battery positive terminal as possible, and protects the wiring from a short-circuit fault in the load circuit. The 5 A rating protects the ≤2 A maximum load while leaving headroom above the controller's worst-case startup surge. Use a weatherproof fuse holder if the battery sits outside the NEMA 4X enclosure. |
| Victron SmartSolar MPPT 75/10 solar charge controller | 1 | Regulates solar panel output to charge the 12 V SLA battery safely. The SmartSolar MPPT 75/10 has dedicated LOAD+ / LOAD− output terminals with built-in low-voltage disconnect (LVD) that automatically cuts load power to protect the SLA from deep discharge. The wiring in §5 depends on that feature. If you substitute a different controller, confirm it has equivalent LOAD terminals with LVD before following the §5 wiring steps. |
| Monocrystalline solar panel, 10–20 W, 12 V nominal (e.g. Renogy 10W Monocrystalline RNG-10D-SS) | 1 | 10 W is enough for mid-latitude sites (≥4 h/day usable sun). Go up to 20 W for northern latitudes, winter deployments, or sites with frequent cloud cover. Mount it with a clear sky view, not against the tank body. |
| NEMA 4X polycarbonate enclosure, ~8 × 6 × 3.5″ | 1 | Weatherproof housing rated for outdoor tank-side mounting. Polycarbonate is UV-resistant and non-sparking. |
Bundled connectivity (Notecard for Skylo): Notecard for Skylo ships with an active global SIM that includes 500 MB of cellular data and 10 years of service, plus 10 KB of bundled Skylo satellite data, with no activation fees, no monthly commitment, and no separate satellite provider subscription. Additional satellite data is billed per byte (see the Notecard for Skylo datasheet for current pricing). Keeping inbound syncs infrequent stretches the bundled satellite allocation.
Safety notice. LP gas (liquefied petroleum gas) is a flammable compressed gas. Any sensor connection to a propane tank, including at the gauge port, must be performed by a qualified LP gas technician following applicable codes (NFPA 58, CGA, and the local authority having jurisdiction). The level transmitter, fittings, and wiring must be rated for LP gas service.
Classified-area / hazardous-location requirement. Blues Notecard and Notecarrier CX electronics are not rated for hazardous locations (NEC Class I Division 1 or Division 2 / ATEX Zone equivalents) and must not be installed inside the classified area as defined by the installation's authority having jurisdiction (AHJ) and NFPA 58. The classified area boundary around a propane container usually extends several feet from fittings, relief valves, and regulators. Confirm the boundary with the AHJ and the installing LP gas technician before you position the enclosure, and mount the electronics enclosure outside it. If the 4–20 mA transmitter loop wiring crosses the classified-area boundary, the AHJ may require a listed intrinsic-safety (IS) barrier or other approved interface device in the loop circuit, between the tank-side transmitter and the enclosure-side electronics. Confirm IS barrier requirements with the installing LP gas technician and the AHJ before wiring.
5. Wiring and Assembly
All Notecarrier CX host I/O lands on its dual 16-pin header, and Notecard for Skylo seats in the M.2 slot. During bench validation only, the Mojo sits inline between the 5 V step-down and the Notecarrier +VBAT pad. It doesn't go out to the field.
Enclosure placement first. Blues Notecard and Notecarrier CX electronics must be installed outside the classified-area boundary defined by NFPA 58 and the authority having jurisdiction. Confirm that boundary with the installing LP gas technician before you drill cable-gland holes or position the enclosure.
Step 1: Enclosure and cable-gland assignment. Mount the NEMA 4X polycarbonate enclosure at the location you picked. Before pulling any cables, drill and install one cable gland for each field-cable penetration. Assign the glands like this:
| Gland | Cable / penetration |
|---|---|
| 1 | Skylo-certified MAIN antenna lead (or its SMA bulkhead), which carries cellular and satellite |
| 2 | GPS/GNSS antenna lead (GPS u.FL port) |
| 3 | DS18B20 temperature probe cable |
| 4 | 4–20 mA transmitter loop wiring (2-conductor, from transmitter to enclosure) |
The solar panel cable takes a fifth gland (Gland 5) unless you use an MC4 pass-through instead (see Step 2). Match each gland's cable-OD clamp range to the actual cable outer diameter. After routing cables, tighten every gland to the manufacturer's torque spec, and don't leave any unused penetration open.
Step 2: Solar panel to charge controller (PV input).
- Solar panel PV+ → charge controller PV+ terminal.
- Solar panel PV− → charge controller PV− terminal.
Mount the panel somewhere unobstructed with a good sky view, not against the tank body or in the tank's shadow. Set the tilt angle to catch the most winter sun for the deployment latitude. Run the panel cable into the enclosure through Gland 5 (or use an MC4 weatherproof pass-through if the controller has external PV terminals), and keep the run as short as practical to cut resistive loss.
Step 3: SLA battery to charge controller (battery terminals).
- Battery (+) → charge controller BATT+ terminal.
- Battery (−) → charge controller BATT− terminal.
Use wire rated for the battery's short-circuit current (14 AWG minimum for a 7–10 Ah SLA). Put the inline fuse holder + 5 A fuse from the BOM in the battery (+) lead, as close to the battery positive terminal as possible. If the battery sits outside the NEMA 4X enclosure, use a weatherproof fuse holder.
Step 4: Charge controller load output to step-down module.
- Charge controller LOAD+ → step-down module Vin+.
- Charge controller LOAD− → step-down module Vin− / GND.
The Victron SmartSolar MPPT 75/10 has dedicated LOAD+ / LOAD− terminals with built-in low-voltage disconnect (LVD). These terminals switch the 12 V supply on and off based on battery state to protect the SLA. Only use this wiring with a controller that has LOAD terminals and LVD. If you substitute a different controller, check for those features before following these steps. If the controller has no LOAD terminals, wire from the BATT terminals and use the controller's LVD relay or alarm output to interrupt the load circuit.
Step 5: 12 V step-down to Mojo and Notecarrier +VBAT (5 V rail).
Set the step-down module output to 5.0 V and check it with a meter before connecting any load.
Step-down Vout+ → Mojo BAT+
Step-down Vout− → Mojo BAT− / GND
Mojo LOAD+ → Notecarrier CX +VBAT
Mojo LOAD− → Notecarrier CX GNDDuring bench validation the Mojo sits in this path to measure 5 V rail current. For field deployment, remove the Mojo and wire the step-down Vout+ directly to Notecarrier CX +VBAT.
Step 6: Notecard installation and antenna connections.
Seat Notecard for Skylo (NOTE-NBGLWX) into the Notecarrier CX M.2 slot and secure the retaining screw.
Notecard for Skylo has two antenna u.FL ports, MAIN and GPS. The single Skylo-certified antenna on the MAIN port carries both the cellular and the satellite signal, so there's no separate satellite antenna to install. Connect the antennas like this:
- MAIN port: attach the included Skylo-certified antenna, routed through Gland 1 (connect it directly, or adapt it to an external SMA bulkhead with a u.FL-to-SMA-F pigtail). Mount the antenna outdoors with an unobstructed view of the sky (northern hemisphere: the southern sky) so the board can reach the Skylo satellite network wherever it falls back from cellular. Use only the Skylo-certified antenna, because an uncertified part risks regulatory non-compliance and link failure. Don't skip this connection. Both cellular and satellite operation need this external antenna.
- GPS port: attach the passive GPS/GNSS antenna, routed through Gland 2, and mount it outdoors with a clear sky view alongside the main antenna.
Pick antenna cable lengths that suit the routing distance before you order, because most pre-terminated assemblies shouldn't be cut or field-terminated. Secure any extra cable outside the enclosure in a gentle loop, and don't coil cable inside the enclosure.
Step 7: 4–20 mA current loop (gauge-port level transmitter).
The Rochester Sensors M6300-LP + R6315-12 is a two-wire loop-powered instrument. A single 2-conductor cable runs from the transmitter (at the tank gauge port, outside or at the hazardous boundary) to the electronics enclosure, through Gland 4. Wire the loop from the dedicated 24 V boost converter output, not from the 12 V system rail or the 5 V Notecarrier rail (see the compliance note below):
- 24 V boost converter Vin+ → charge controller LOAD+ (same 12 V rail that feeds the 5 V step-down). 24 V boost converter GND → system GND.
- 24 V boost converter Vout+ (regulated 24 V) → transmitter + terminal (loop supply in).
- Transmitter − terminal (loop signal out) → one leg of the 120 Ω precision shunt resistor; other shunt leg → system GND (same node as Notecarrier GND and battery −).
- A0 (analog input on the Notecarrier CX header) → the junction between the transmitter − terminal and the shunt resistor.
At 4 mA (float at the bottom, empty tank), V(A0) = 0.48 V. At 20 mA (float
at full scale, full tank), V(A0) = 2.40 V. Both are well within the host's
0–3.3 V ADC range. With the 120 Ω shunt, a 24 mA transmitter diagnostic output
produces only 2.88 V at A0, and the ADC is electrically safe up to ~27 mA
(3.3 V ÷ 120 Ω). The firmware's software fault window is 3.5–21 mA. Any current
outside that range asserts a sensor_fault alert and marks the reading NAN.
The shunt voltage and ADC range are the same as in a 12 V supply design, and
only the loop supply source changes.
Loop compliance calculation. The R6315-12 datasheet specifies a supply voltage range of 12–28 V DC. Here's the worst-case compliance budget at 20 mA full-scale:
| Element | Voltage drop at 20 mA |
|---|---|
| 120 Ω shunt (sense resistor, enclosure side) | 20 mA × 120 Ω = 2.40 V |
| 100 m of 22 AWG field wiring (both conductors combined) | 20 mA × 2 × 100 m × 0.053 Ω/m ≈ 0.21 V |
| IS barrier (Zener-diode shunt type, if required by AHJ) | ≤ 2.5 V at 20 mA |
| Total worst-case external drops | ≤ 5.11 V |
With the regulated 24 V boost converter output, V at the transmitter terminals = 24 V − 5.11 V = 18.89 V. That's 6.89 V above the 12 V lower compliance limit and well below the 28 V upper limit. The transmitter stays in regulation across the full battery charge/discharge cycle, on any cable run up to 100 m, with or without an IS barrier.
Why not the 12 V system rail? An SLA battery discharges from ≈ 12.8 V (fully charged) to the low-voltage disconnect threshold (≈ 10.8 V). At partial charge under load it commonly measures 11.0–11.5 V, which is already at or below the R6315-12's 12 V minimum supply before you account for cable resistance or an IS barrier. Either one makes the margin worse. The 24 V boost converter removes that dependency, since it holds a regulated 24 V output as long as the SLA stays above the converter's 2.9 V minimum input voltage, well below the charge controller's LVD threshold.
The transmitter's process connection is the tank's single 1¼″ NPT dip-tube gauge port, so you don't need a second tank connection. Internally, the float mechanism provides the level reference. The LP gas technician installs the transmitter at the gauge port and runs the 2-conductor loop cable from the transmitter to the enclosure. Confirm the correct dip-tube length for the tank geometry before installation.
Before applying power, confirm that system GND (battery −), charge controller LOAD−, step-down Vin−, step-down Vout−, boost converter GND, and Notecarrier GND all connect to the same node.
Step 8: DS18B20 temperature probe.
Route the DS18B20 cable through Gland 3. Mount the probe body against the tank shell, ideally under a layer of closed-cell foam tape for better thermal contact and less ambient-air influence. Secure the cable with a zip tie so it doesn't strain the gland. Inside the enclosure:
- DS18B20 red wire (VCC) → +3V3 on the Notecarrier CX header.
- DS18B20 black wire (GND) → GND on the Notecarrier CX header.
- DS18B20 yellow wire (data) → D2 on the Notecarrier CX header.
- 4.7 kΩ pull-up resistor between the data wire and +3V3. The Notecarrier CX has no onboard OneWire pull-up, so you need this resistor.
Step 9: ATTN-to-D5 jumper (host sleep).
Fit a short jumper between the ATTN and D5 pins on the Notecarrier CX
header. Both are on the same 16-pin header (ATTN at position 3, D5 at
position 14). 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. 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
covers the full pattern.
Step 10: Satellite operation (automatic, no added hardware).
Satellite operation needs no extra module or wiring. Notecard for Skylo's
satellite radio is on the same M.2 board as cellular and WiFi, and the single
Skylo-certified MAIN antenna (Step 6) carries the satellite link. The firmware
enables cellular→satellite fallback by setting card.transport to
wifi-cell-ntn at boot (see §7). From then on, the Notecard automatically
routes queued Notes over the Skylo NTN satellite network at any site where
cellular is unavailable. For satellite to work in the field, the MAIN antenna
has to be mounted outdoors with an unobstructed view of the sky (northern
hemisphere: the southern sky), so confirm a suitable mounting spot at remote
tank sites. The daily summary Note (~40 bytes) fits well within the NTN payload
budget. Also note that the board must complete at least one initial non-NTN
(cellular or WiFi) sync to associate with Notehub and register its Notefile and
environment variable templates before it can use satellite. Commission each
unit where it has terrestrial coverage, even if it will normally run over
satellite. Before deploying to a cellular-dark site, review the Satellite Best
Practices guide for duty-cycle and
data-budget considerations.
Final pre-power checklist.
- All cable glands tightened; no unused enclosure penetrations open.
- All GND nodes common: battery −, charge controller LOAD−, step-down Vin−, step-down Vout−, boost converter GND, Notecarrier GND.
- Step-down output confirmed at 5.0 V before connecting Notecarrier.
- Boost converter Vin+ wired to 12 V system rail (LOAD+); boost converter Vout+ confirmed at 24 V before connecting transmitter loop wiring.
- Skylo-certified MAIN antenna connected and routed outdoors with a clear sky view (carries both cellular and satellite); GPS/GNSS antenna connected to the GPS port and routed outdoors.
- 120 Ω shunt installed in the current loop with A0 tapped at the transmitter − / shunt junction; loop supply drawn from 24 V boost converter output (not the 12 V rail).
- Inline fuse installed in battery (+) lead, as close to the battery positive terminal as possible.
6. Notehub Setup
-
Create a project. Sign up at notehub.io and create a project. Copy the ProductUID and paste it into the firmware as
PRODUCT_UID. -
Claim the Notecard. Power up the unit, and the Notecard associates with the project on its first cellular session. Check that the device shows up in the Notehub device list with a recent session.
-
Optional: configure WiFi credentials. Notecard for Skylo supports WiFi as an optional secondary transport, and the device runs normally on cellular (or satellite) alone if no WiFi credentials are set. This hardware stack has two reliable ways to provision them:
-
Preferred: Notehub environment variables (works on deployed hardware). Set the
wifi_ssidandwifi_passwordenvironment variables in Notehub (see the env var table in step 5 below). Env vars reach the device on the next inbound sync.hubConfigure()sets inbound to 2× the outbound period, so at the default 24-hour report cadence, inbound syncs happen every 48 hours. WiFi credentials set in Notehub may take up to 48 hours to reach the device. Once the inbound sync delivers the vars, the firmware issuescard.wifito store the credentials on the Notecard, which then connects over WiFi first whenever it can reach that network, sincewifi-cell-ntnputs WiFi ahead of cellular. To roll out credentials faster, temporarily lowerreport_interval_hr(to 1 hour, for example), confirm connectivity, then restore the original value. Credentials persist on the Notecard even if the env vars are cleared later. -
Alternative: direct Notecard USB (bench setup before deployment). With the Notecarrier CX fully powered off, set its USB-C DIP switch to
NCso the USB-C port connects to the Notecard instead of the STM32 host, then power up and connect a USB cable. With that connection active, open Notecard Playground and send{"req":"card.wifi","ssid":"<SSID>","password":"<password>"}. It's easiest to do this at the bench before you seal the enclosure for field deployment.
Normal operation doesn't need WiFi, so you don't have to provision it on every device.
-
-
Create a Fleet per territory. Fleets let you group devices for shared configuration and routing. A natural split is one fleet per delivery territory or per tank capacity class, since a 250-gallon residential fleet and a 1000-gallon commercial fleet may need different alert thresholds and report cadences. Smart Fleets can auto-assign new devices by matching tags set at provisioning time (e.g.
tank_size:1000gal). -
Set environment variables. In Notehub, go to Projects → [Your Project] → Environment and configure a Fleet at the Fleet level or Device level. Every variable below is optional, and the table shows the firmware's compile-time defaults. Any value you set in Notehub takes effect on the device's next inbound sync, with no firmware re-flash. Set variables on the Fleet so one configuration applies to every tank in it, and override at the Device level for per-tank exceptions.
Variable Default Purpose tank_capacity_gal500Usable tank capacity in gallons at 100 % gauge reading. sensor_empty_ma4.0Transmitter output current in mA corresponding to 0 % fill (empty tank). Standard 4–20 mA transmitters output exactly 4.0 mA at empty, so adjust this only if the installed unit has a non-standard live-zero (rare). sensor_full_ma20.0Transmitter output current in mA corresponding to 100 % fill. Standard 4–20 mA transmitters output 20.0 mA at full. Adjust it if the installed unit doesn't reach exactly 20 mA at the full-tank position. fill_alert_pct20Fill percentage below which a low_fillalert fires.consumption_alert_gal_per_day100Daily consumption rate above which a high_consumptionalert fires. Helps catch leaks or sudden demand spikes.sample_interval_min15Minutes between sensor samples. report_interval_hr24Hours between summary Notes. alert_cooldown_hr4Hours between repeated alerts of the same type. Keeps a slowly draining tank from sending an alert every 15 minutes. consumption_alert_streak3Number of consecutive sample cycles above consumption_alert_gal_per_dayrequired before ahigh_consumptionalert fires. Raising it cuts false positives from transient ADC jitter or short-lived post-refill spikes. Set it to1to alert on the first above-threshold reading.wifi_ssid(unset) SSID of a WiFi network to use as a secondary transport. Set both wifi_ssidandwifi_password, and the firmware issuescard.wifion the next inbound sync (up to 48 hours at the default cadence; see step 3 above) so the Notecard stores the credentials and connects over WiFi when it's available. Credentials persist on the Notecard even if the env vars are cleared later. Env var values are visible to Notehub project collaborators, so use a dedicated IoT or guest network.wifi_password(unset) WPA2 passphrase for the network specified by wifi_ssid. -
Satellite deployments (cellular-dark sites). You don't need a hardware change. Notecard for Skylo's satellite radio is already on the board, and the firmware enables cellular→satellite fallback by setting
card.transporttowifi-cell-ntn(see §7). At sites with no cellular coverage, the board falls back to the Skylo NTN satellite network automatically, and every event travels through the same Notehub project whatever the transport. Before it can use satellite, the board must complete at least one initial non-NTN (cellular or WiFi) sync to associate with the project and register its Notefile and environment variable templates. Commission each unit where it has terrestrial coverage, even if it will normally run over satellite. The daily summary Note (~40 bytes per record) fits within typical NTN satellite payload budgets. Each inbound sync also uses some of the bundled 10 KB satellite allocation, so consider wideningreport_interval_hr(which widens the inbound cadence too) at satellite sites. Read the Satellite Best Practices guide before deploying for the satellite-specific duty-cycle and data-budget details. -
Configure routes. Add one route for
tank_alert.qo(real-time delivery to the dealer's dispatch or routing system) and a second fortank_status.qo(batched delivery to a time-series store for historical trend analysis and route-density modeling). Because the two Notefiles are separate at the source, each can go to a different destination at a different urgency. Alerts go somewhere that pages an operator, and daily summaries go somewhere that feeds a dashboard.
7. Firmware Design
The main sketch is
firmware/propane_tank_telemetry/propane_tank_telemetry.ino.
Sensor math, fill-level calculation, and consumption tracking live in
firmware/propane_tank_telemetry/propane_tank_telemetry_helpers.h.
Dependencies:
- Arduino core for STM32
(
stm32duino/Arduino_Core_STM32), installed from the Boards Manager. Blues Wireless Notecard(thenote-arduinolibrary). Install it from the Arduino Library Manager or witharduino-cli lib install "Blues Wireless Notecard". See the note-arduino releases for available versions.STM32duino Low Power(and its dependencySTM32duino RTC), also from the Library Manager. It provides the STOP2 entry and ATTN-pin wake thatcx_sleep.huses.OneWire, from the Library Manager.DallasTemperature, also from the Library Manager.
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 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).
Modules
| Responsibility | Where |
|---|---|
Notecard configuration (hub.set, card.transport wifi-cell-ntn for cellular→satellite fallback, Note and environment variable templates) | hubConfigure, defineTemplates |
| Environment-variable fetch per wake | fetchEnvOverrides |
| 4-20 mA loop read → mA current | readTransmitterMA |
| DS18B20 OneWire temperature read | readTemperatureC |
| Transmitter current → fill % (linear interpolation) | computeFillPct |
| Fill % → gallons (linear scale) | computeFillGal |
| Consumption rate (EWMA), days-until-empty projection | updateConsumption, daysUntilEmpty |
| Alert evaluation and emission | runSampleCycle, sendAlert |
| Daily summary | sendSummary |
Host sleep (STOP2 until the Notecard raises ATTN) | cx_sleep.h: cxSleepBegin, cxSleepUntilAttn |
| Application state (held in RAM across sleep cycles) | AppState |
Sensor reading strategy
Gauge-port level transmitter (4-20 mA). The host's 12-bit ADC reads the voltage across the 120 Ω shunt. A 16-sample average filters out switching noise, and then the firmware converts the mean voltage to current (mA):
current_ma = (adc_voltage / 120.0) × 1000.0The firmware's valid window is 3.5–21 mA. Below 3.5 mA means an open-loop
wiring fault, and above 21 mA means a short or an unexpected diagnostic output.
Either one sets the reading to NAN and emits a sensor_fault alert. With the
120 Ω shunt, a 21 mA signal produces 2.52 V at A0, well within ADC range. The
ADC is electrically safe up to ~27 mA, so a transmitter in a full fault-high
condition won't damage it.
Fill percentage and fill gallons. The Rochester Sensors M6300-LP + R6315-12
float transmitter outputs a current proportional to the float's position in the
liquid propane, so the output already represents fill level directly,
independent of liquid density or temperature. computeFillPct converts the
transmitter
current to fill percentage with a simple linear interpolation between the
configured empty and full calibration points:
fill_pct = (current_ma - sensor_empty_ma) / (sensor_full_ma - sensor_empty_ma) × 100.0computeFillGal then scales that to gallons:
fill_gal = (fill_pct / 100) × tank_capacity_galThe fill reading gets no density or temperature correction. The float tracks the liquid surface directly, and its current output already represents fill level accurately across the full operating temperature range.
Temperature (DS18B20 OneWire). The DallasTemperature library handles
conversion timing. The daily summary Notes include tank wall temperature so
cloud-side analytics can correlate demand patterns with ambient conditions,
since propane consumption is strongly seasonal. A missing or faulted DS18B20
puts the -9999 sentinel in the temp_c field of summary Notes but doesn't
affect the fill reading.
Sensor selection rationale
The initial project spec called for a low-power ultrasonic or pressure level sensor, with the DS18B20 temperature probe providing vapor-pressure compensation in the fill measurement path. After evaluating both sensing methods for LP gas tank duty, this design departs from that spec and uses a float-type 4–20 mA transmitter instead. The table below lays out why, and the firmware's top-of-file sensor model comment summarizes the same reasoning.
| Approach | Why evaluated | Why not selected for this design |
|---|---|---|
| Ultrasonic time-of-flight (transducer at tank top; measures vapor-space height by round-trip echo time) | Non-contact; no liquid exposure for the sensor electronics; well-established for above-ground tank gauging in other applications | Requires a dedicated transducer port at the tank top, which is an extra tank penetration beyond the standard 1¼″ NPT gauge port that the Rochester Sensors float gauge already occupies. The speed of sound in the LP vapor space varies with temperature and gas composition, so an accurate fill reading needs a temperature-compensated speed-of-sound correction. That puts the DS18B20 in the fill measurement path and adds a second independent failure mode. The LP vapor composition (propane/butane ratio) varies by supplier and season and isn't known to the firmware, so it adds a systematic bias that can't be corrected without a composition sensor. |
| Hydrostatic pressure (pressure sensor measuring liquid column head pressure) | Single-point measurement; sensor can be installed at an existing tank fitting | On a horizontal LP tank, the gauge port at the top of the tank dome opens into the vapor space, not the bottom of the liquid column, so a sensor at the gauge port reads vapor pressure instead of liquid head. Reaching the liquid bottom takes a second tank penetration (a service-valve tee or a dedicated bottom fitting), which adds installation complexity and more tank connections that need LP gas technician work. Even with a bottom connection, the liquid head for a typical 500-gallon horizontal tank is only ~5 kPa (~0.7 psi) riding on top of ~850 kPa (~120 psi) of vapor pressure. Resolving fill level to ±5 % from that signal takes a pressure transmitter with ≤ 0.01 % full-scale accuracy, well beyond standard industrial grade. A differential pressure sensor spanning a bottom port and a vapor-space port would cancel the vapor background and improve accuracy, but it adds a second tank connection and needs two separate field-wiring runs to the enclosure. Either pressure variant puts the DS18B20 in the fill calculation path (density correction), as the original brief intended, but at the cost of two tank penetrations instead of one. |
| Float-type 4–20 mA transmitter (Rochester Sensors M6300-LP + R6315-12, selected design) | Industry-standard LP gas gauging method; well-understood installation at the existing gauge port | n/a (selected) |
This design uses the Rochester Sensors M6300-LP Magnetel® float gauge + R6315-12 transmitter because it installs at the tank's single standard 1¼″ NPT dip-tube gauge port, needs no extra tank penetrations, and produces a 4–20 mA output that's directly proportional to fill level regardless of liquid temperature or density. The float tracks the physical liquid propane surface, so its current output doesn't depend on density at all, and the firmware needs no temperature correction (and applies none). The main trade-off against the original spec is a continuously powered 4–20 mA current loop, the biggest load in the power budget, instead of the switched-power or ultra-low-power sensing an I²C ultrasonic or differential-pressure sensor could offer. See §11 for the power-budget discussion.
The DS18B20 temperature probe stays in the design for cloud-side seasonal demand
analytics. Its reading goes into every tank_status.qo daily summary Note and
is not passed to the fill calculation. The top-of-file sensor model comment
in
propane_tank_telemetry.ino
records the same reasoning next to the implementation.
Event payload design
The device writes to two
template-backed
Notefiles. Templates store Notes as compact fixed-length records instead of
free-form JSON. At 365 summary Notes per tank per year across a fleet of
thousands of tanks, that saves real bandwidth on a prepaid SIM. Both templates
also set format to compact and give the Notefile a port, which is what
makes them NTN-compatible. That way the same Notes go out over Skylo satellite
when cellular and WiFi aren't available. Compact format drops a Note's
metadata, so each template adds a _time field to keep the time the Note was
created.
tank_status.qo (daily summary):
{
"file": "tank_status.qo",
"body": {
"fill_pct": 42.3,
"fill_gal": 211.5,
"min_fill_pct": 38.1,
"temp_c": 14.8,
"gal_per_day": 8.2,
"days_until_empty": 25.8,
"transmitter_ma": 10.8
}
}tank_alert.qo (immediate, sync:true):
{
"file": "tank_alert.qo",
"sync": true,
"body": {
"alert": "low_fill",
"fill_pct": 18.2,
"fill_gal": 91.0,
"gal_per_day": 8.2,
"days_until_empty": 11.1
}
}The firmware defines two alert types. low_fill means the fill percentage
dropped below fill_alert_pct. high_consumption means the smoothed daily
consumption rate has exceeded consumption_alert_gal_per_day for
consumption_alert_streak consecutive sample cycles (default 3), which
debounces transient spikes from ADC jitter or post-refill noise. A third,
synthetic type, sensor_fault, fires when the transmitter current falls outside
the valid 3.5–21 mA window because of an open circuit or a short on the current
loop. All three use the same tank_alert.qo Notefile with sync:true, so they
skip the daily outbound window and reach the dealer's dispatch system as soon as
the Notecard can open a session. That's usually well under 60 seconds in good
cellular coverage, and minutes when the unit is running over the Skylo
satellite link (see the NOTE-NBGLWX
datasheet for authoritative
figures).
Satellite alert latency. When Notecard for Skylo is running over the
satellite (NTN) link, sync:true doesn't remove latency. The alert has to wait
for an available satellite pass, and Skylo's network duty-cycle rules limit how
often sessions can open. Treat satellite alert latency as minutes, not
seconds. A satellite session also has a different energy profile from a
cellular one, so the cellular Mojo current figures in §9 don't directly apply.
For sites expected to run over satellite, validate alert timing and
solar/battery sizing separately with the Satellite Best Practices
guide before commissioning.
Low-power strategy
Between samples the Notecarrier CX's STM32 host sleeps in STOP2, drawing a few
microamps and keeping its RAM. loop() runs one sample cycle, then calls
cxSleepUntilAttn() (in cx_sleep.h). That sends a
card.attn sleep
request for sample_interval_min × 60 seconds, waits for the Notecard to pull
ATTN low, and enters STOP2. When the interval is up, the Notecard raises
ATTN, the rising edge on D5 wakes the host, and execution resumes on the
next line of loop(). Notecard for Skylo itself idles at ~8 µA between network
sessions (the NOTE-NBGLWX published idle figure), no matter which radio it used
last.
The host resumes in place instead of cold-booting, so all application state
(the smoothed consumption-rate estimate, alert cooldown timestamps, and the
summary window) just lives in the AppState struct in RAM. Nothing gets
serialized to the Notecard, and setup() runs only once per power-up. 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 runs in periodic mode with an outbound period that matches
report_interval_hr. Summary Notes pile up in its queue and sync in a single
cellular session once a day, while alert Notes set sync:true and go out within
minutes of triggering.
Retry and error handling
- The firmware rejects transmitter current readings outside 3.5–21 mA as
NANand leaves them out of summary averages. Asensor_faultalert fires on the first detection and then stays quiet foralert_cooldown_hr. That pages the dealer once without flooding them on every 15-minute cycle. - The DS18B20 returns
DEVICE_DISCONNECTED_C(−127.0) if no sensor is present. The firmware checks for this sentinel and leaves the reading out of the window temperature average, so a missing probe doesn't affect the fill reading. (When no valid temperature samples were collected, the summary Note'stemp_cfield carries the-9999sentinel.) - The firmware computes consumption rate only when it has two consecutive valid fill readings. A refill event (fill level up by more than 5 % between samples) resets the short-term accumulator without touching the EWMA, so a delivery doesn't corrupt the running consumption estimate.
- A change to the
report_interval_hrenv var re-applieshub.seton the next wake, so the Notecard's outbound cellular cadence stays in step with the local summary cadence.
Key code snippet 1: template definition
Templates compress daily summary Notes into fixed-length records. In Notecard
template format codes, the first digit is the type (1 for signed, 2 for
unsigned), the second digit is the byte size, and a .1 suffix marks a float:
14.1: 4-byte float (±3.4e38, ~7 significant digits, good for fill %, consumption rates, days-until-empty)14: 4-byte signed integer, used here for the_timetimestamp21and22: 1- and 2-byte unsigned integers, used for the interval variables in the environment variable template
defineTemplates() sends the two Note templates and then an
env.template for
every variable fetchEnvOverrides() reads. Notecard for Skylo needs that
template before env.get works in NTN mode, and over satellite Notehub only
delivers the variables it names. env.get still returns every value as a
string, so the hints only size the satellite encoding. Each request's result
is checked, and perWakeConfigure() retries all three on every wake until they
succeed.
J *req = notecard.newRequest("note.template");
JAddStringToObject(req, "file", "tank_status.qo");
JAddNumberToObject(req, "port", 50);
JAddStringToObject(req, "format", "compact");
J *body = JAddObjectToObject(req, "body");
JAddNumberToObject(body, "_time", 14); // Note creation time, which compact format otherwise drops
JAddNumberToObject(body, "fill_pct", 14.1); // 4-byte float; current fill at time of note
JAddNumberToObject(body, "fill_gal", 14.1); // current fill gallons
JAddNumberToObject(body, "min_fill_pct", 14.1); // lowest fill seen in this reporting window
JAddNumberToObject(body, "temp_c", 14.1); // window-averaged tank-shell temperature
JAddNumberToObject(body, "gal_per_day", 14.1);
JAddNumberToObject(body, "days_until_empty", 14.1);
JAddNumberToObject(body, "transmitter_ma", 14.1); // current transmitter output current
ok_status = notecard.sendRequest(req);
// ...
J *req = notecard.newRequest("env.template");
J *body = JAddObjectToObject(req, "body");
JAddNumberToObject(body, "tank_capacity_gal", 14.1);
// ...
JAddNumberToObject(body, "sample_interval_min", 22); // 2-byte unsigned int (1–1440)
JAddNumberToObject(body, "report_interval_hr", 21); // 1-byte unsigned int (1–168)
// ...
JAddStringToObject(body, "wifi_ssid", TSTRINGV); // variable-length string
JAddStringToObject(body, "wifi_password", TSTRINGV);
ok_env = notecard.sendRequest(req);The Note.template API documentation has the full reference on Notecard template codes.
Key code snippet 2: 4-20 mA level transmitter current → fill percentage → fill gallons
computeFillPct (in propane_tank_telemetry_helpers.h) converts the
float-type LP gauge-port transmitter current to fill percentage with a linear
interpolation between the configured empty and full calibration points, and
computeFillGal scales that to gallons. There's no density or temperature
correction, because the float rides the liquid surface directly and its current
output is already an accurate fill reading across the full operating
temperature range.
// Convert 4-20 mA float-type level transmitter current to fill percentage (0–100 %).
// ...
// A simple linear interpolation converts the transmitter current to fill %:
// fill_pct = (current_ma − sensor_empty_ma) / span × 100
//
// Returns NAN if current_ma is NAN (transmitter fault).
static float computeFillPct(float current_ma,
float sensor_empty_ma, float sensor_full_ma) {
if (isnan(current_ma)) return NAN;
float span = sensor_full_ma - sensor_empty_ma;
if (span < 0.1f) return NAN;
float fill_pct = (current_ma - sensor_empty_ma) / span * 100.0f;
return fmaxf(0.0f, fminf(100.0f, fill_pct));
}
// Convert fill percentage to gallons via a simple linear scale.
// ...
static float computeFillGal(float fill_pct, float tank_capacity_gal) {
if (isnan(fill_pct)) return NAN;
return (fill_pct / 100.0f) * tank_capacity_gal;
}In runSampleCycle() the call is:
float fill_pct = computeFillPct(xmtr_ma, SENSOR_EMPTY_MA, SENSOR_FULL_MA);The firmware still reads temperature on every cycle for the daily summary, but it doesn't pass it to the fill calculation.
Key code snippet 3: sleeping until the Notecard raises ATTN
setup() arms the ATTN wake once with cxSleepBegin(). Each pass through
loop() then runs the per-wake configuration and one sample cycle, and
cxSleepUntilAttn() asks the Notecard to hold ATTN low for exactly
SAMPLE_INTERVAL_MIN × 60 seconds, confirms the pin has gone low, and puts the
STM32 into STOP2. Since the host stays alive, the firmware checks the result of
the card.attn request instead of firing it off and forgetting it. The
Notecard's ATTN pin brings the host back on schedule, and loop() carries on
with the next sample.
// Wake from STOP2 on the ATTN rising edge.
cxSleepBegin();
// ...
void loop() {
// Sample sensors and evaluate thresholds, then sleep until the Notecard
// raises ATTN SAMPLE_INTERVAL_MIN × 60 s from now.
perWakeConfigure();
runSampleCycle();
state.cycles++;
uint32_t sleep_sec = (uint32_t)SAMPLE_INTERVAL_MIN * 60UL;
if (!cxSleepUntilAttn(notecard, sleep_sec, NULL, &usbSerial)) {
// 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.
delay(sleep_sec * 1000UL);
}
}8. Data Flow
Every sample_interval_min (15 min by default) the host wakes, reads the LP
gauge-port level transmitter and DS18B20 temperature probe, converts the
transmitter current directly to fill %, updates the consumption EWMA, and
evaluates three alert conditions.
Collected. Fill percentage (from the 4-20 mA float-type level transmitter current, linearly interpolated between the configured empty and full calibration points), fill gallons (fill % × tank capacity), tank shell temperature (DS18B20 °C, included in every summary for seasonal demand analytics), raw transmitter current (mA, for diagnostics), smoothed daily consumption rate (gal/day), and projected days-until-empty.
Transmitted.
tank_status.qo: one templated Note perreport_interval_hr(daily by default). It carries the current (most recent valid)fill_pct,fill_gal, andtransmitter_mainstead of window averages, so a refill or a sharp draw-down shows up accurately at reporting time. Alongside those, it includesmin_fill_pct(the lowest fill percentage seen during the window, for analytics), window-averagedtemp_c(the day's thermal environment), the current EWMAgal_per_day(a running smoothed consumption estimate), and the deriveddays_until_empty(current fill gallons divided by the EWMA rate). The Notes are batched and synced in a single cellular session, for 365 Notes per tank per year.tank_alert.qo: sent withsync:truewhen a threshold trips, skipping the outbound timer. It's suppressed foralert_cooldown_hrafter each firing so nobody gets alarm fatigue.
Routed. Notehub sends tank_alert.qo to whatever real-time channel the
dealer's operations team uses (a dispatch system webhook, SMS gateway, or paging
service) and tank_status.qo to a time-series store that feeds historical trend
analysis and route-density modeling.
Alerts trigger on.
low_fill: fill percentage drops belowfill_alert_pct(default 20 %). It fires once peralert_cooldown_hruntil the tank is refilled.high_consumption: the smoothed daily consumption rate has exceededconsumption_alert_gal_per_day(default 100 gal/day) forconsumption_alert_streakconsecutive sample cycles (default 3). The streak debounce prevents false positives from transient ADC jitter or short-lived post-refill consumption spikes, and the counter resets to zero any time the rate drops back below threshold or the transmitter reports a fault. At the 15-minute default sample rate, a streak of 3 means the high rate has to hold for at least 30 minutes before the alert fires. Most residential tanks use 3–15 gal/day in normal operation, so 100 gal/day sustained points to a possible leak or piping fault.sensor_fault: transmitter current outside the valid 3.5–21 mA window. It points to wiring damage, a flooded enclosure, or a failed transmitter, and someone needs to inspect it in person.
Cloud-side usage model (high level). The dealer's analytics system receives
daily tank_status.qo Notes with fill_gal and gal_per_day for every tank.
The firmware computes a simple projection,
days_until_empty = fill_gal / gal_per_day, and includes it in each Note, so
the dealer's route planner can sort tanks by urgency without running any
analytics locally. Route density optimization (grouping tanks within a delivery
window by geography and urgency) belongs in the cloud, not the firmware. The
device's job is to get accurate fill data into Notehub on time.
9. Validation and Testing
Expected cadence in steady state. A working unit on a residential propane
tank should produce one tank_status.qo event per day and no tank_alert.qo
events during normal operation. During commissioning, expect a sensor_fault
event if the transmitter wiring isn't finished, and low-fill alerts if the tank
already happens to be below threshold.
Bench validation before field deployment. To simulate the transmitter on the
bench, drive the A0 analog input with a variable voltage in the 0.48–2.40 V
range. A potentiometer from the 3.3 V rail to GND works, and so does a bench DC
supply. Confirm the firmware maps 0.48 V to 0 % fill (4 mA), 1.44 V to 50 % fill
(12 mA), and 2.40 V to 100 % fill (20 mA). Check that the open-circuit condition
(A0 driven to 0 V or below 0.42 V, corresponding to < 3.5 mA) emits a
sensor_fault alert Note. For the DS18B20, confirm the firmware reports a valid
temperature on the serial port and that a missing probe produces NAN (which
becomes the -9999 sentinel in summary Notes) instead of a garbage reading.
Commissioning calibration. Once the unit is on the tank, set
tank_capacity_gal in the fleet's Notehub environment variables to the tank's
rated usable capacity. Check that the fill percentage in Notehub matches an
independent reference reading (the mechanical gauge, weight, or the LP
technician's measurement) within a few percent. If there's a persistent offset
that grows toward the high-fill end, the transmitter's span may not reach
exactly 20 mA at the full-float position. Adjust sensor_full_ma to the
transmitter's actual full-scale output current until the Notehub reading lines
up with the reference. If the offset is the same across the full range (a
roughly constant error from empty to full), the transmitter may have a
non-standard live-zero. In that case, change sensor_empty_ma from its default
4.0 mA to match the transmitter's actual output at the empty (float-at-bottom)
position. The fill calculation is a simple linear interpolation with no
temperature correction, so the commissioning measurement is valid at any
ambient temperature and doesn't need any special thermal condition.
Using Mojo to validate power behavior. Place the Mojo inline between the 5 V step-down output and the Notecarrier VBAT pad. The table below lists the expected current envelope on the 5 V rail for each major firmware state, along with where each figure comes from.
| Firmware state | Expected current (5 V rail, Mojo) | Source |
|---|---|---|
| Deep sleep (host in STOP2, Notecard idle, radio off) | ~10 µA @ 5 V | ~8 µA is the Blues-published idle figure for the NOTE-NBGLWX (see the low-power design guide and firmware best practices guide); the STM32L433 in STOP2 adds ~1–2 µA. The Mojo measures the entire powered subsystem (Notecard + host + Notecarrier CX regulators), so the bench reading will be modestly above the Notecard-only figure. Use trace shape and per-session energy as the main commissioning targets, not an exact match to the Notecard-only idle number. |
| Host awake and sampling (STM32 active, 16-sample ADC average, DS18B20 750 ms conversion, I²C Notecard call, ~5 s total) | 30–50 mA | Bench estimate for the host subsystem only, not a Blues-published figure. Covers STM32L433 active-mode current, 12-bit ADC operation, DS18B20 conversion current, and I²C Notecard transactions. The exact value varies with supply voltage and MCU clock. Trace shape (a brief spike every sample_interval_min, then flat near zero) is a more reliable commissioning indicator than the absolute mA reading. |
| Notecard network session over cellular (or satellite), small queued Note, good signal | in-session average ~250 mA from the onboard modem; brief peaks up to ~2 A for a few ms | Notecard for Skylo's Quectel BG95-S5 modem draws on the order of ~250 mA average during a network session (cellular and satellite are similar in magnitude), with brief higher peaks. A 2G transmit burst can pull nearly 2 A for a few milliseconds (see the Blues low-power hardware design application note). The Pololu D24V22F5 step-down (2.5 A rated) and SLA battery in this design can comfortably source ≥ 2 A. A cellular session typically lasts 30–60 s, so expect a broad hump on the Mojo trace, not a sharp spike. Weaker signal or first-time network registration makes the session longer and raises per-session energy. The RSRP/SINR signal-quality table in the Blues low-power design guide shows how signal conditions affect mAh per session. |
With a 15-minute sample and a 24-hour cellular sync, the Mojo trace should show
a brief (~5 s) 30–50 mA spike every 15 minutes from host-awake sampling, plus a
broader 30–60 s hump averaging ~250 mA once a day for the cellular sync. Between
sample spikes the trace should sit flat at the ~10 µA idle floor. If the current
stays elevated between spikes, the host isn't entering STOP2, so check the
ATTN→D5 jumper and confirm the firmware was built with USB support set to
None.
At the 15-minute default cadence, the expected 24-hour Mojo total comes from
three places. (1) idle: ~10 µA Notecard + host-in-STOP2 floor × 24 h ≈
~0.25 mAh. (The total 5 V rail idle, including Notecarrier CX regulator
quiescent current, will read a bit higher at the bench.) (2) sampling: 96
host-awake wakes/day × ~5 s @ 30–50 mA = ~4–6.7 mAh (96 wakes × 5 s ÷
3 600 s/h × 30–50 mA). (3) daily sync: ~250 mA × ~45 s ÷ 3 600 s/h ≈
~3 mAh. That puts the expected daily total at roughly 7–10 mAh/day. If
the Mojo's 24-hour total runs well above that range, the network session may be
staying open longer than expected, or the Notecard may be in continuous mode
instead of periodic. Confirm with hub.get.
The 4-20 mA transmitter loop runs off the 24 V boost converter circuit, so it
does not show up on the Mojo trace (Mojo measures only the 5 V Notecarrier
rail). For a complete energy audit of the transmitter loop, put a bench ammeter
in series with the boost converter's Vin+ lead on the 12 V system rail.
Counting boost converter losses, expect a continuous draw from the 12 V rail of
approximately 110 mW (4 mA loop, empty tank) up to 550 mW (20 mA loop, full
tank). You can read the 4–20 mA loop current itself directly in the serial
debug output as xmtr_ma.
When operating over satellite (NTN). The session-duration and daily-energy
figures above describe cellular sessions. When Notecard for Skylo falls back to
the Skylo satellite link, the idle draw stays the same (~8 µA) and a satellite
session draws a similar ~250 mA average from the same modem. Session duration,
the number of sessions Skylo's duty-cycle rules allow per day, and per-session
energy all differ from cellular, though. sync:true alert delivery depends on
a satellite pass being available, so latency is minutes, not seconds. Each
inbound sync also uses some of the bundled 10 KB satellite allocation, so widen
report_interval_hr at satellite sites to stretch it. Validate solar panel
sizing, battery capacity, and alert latency separately for any site expected to
operate over satellite using the Satellite Best Practices
guide before commissioning. Don't assume
the cellular sizing figures in this section are enough.
10. Troubleshooting
No device appearing in Notehub after power-up.
- Confirm the Notecard MAIN u.FL antenna is connected to an external antenna (not left stubbed internally). A missing or unplugged antenna prevents cellular registration entirely.
- Check that
PRODUCT_UIDis defined in the firmware and matches the ProductUID shown on your project's tile or Settings page in Notehub. - Make sure the 5 V step-down puts out exactly 5.0 V (measured at the Notecarrier +VBAT pad before applying load).
- Look at the serial console for Notecard and I²C errors. If the console isn't available, move the device somewhere with strong cellular signal (≥2 bars) and reboot.
Device appears in Notehub but no events are arriving.
- Confirm the device shows a recent session timestamp in Notehub (Devices → [Your device] → Events → Device Activity). A stale timestamp means the device isn't connecting.
- Check the Notecard's hub settings. With the Notecarrier CX's USB-C DIP switch
set to
NC, connect over USB-C, open the Notecard Playground, and send{"req":"hub.get"}. The response should show"mode": "periodic"and"outbound": 1440(minutes, so 24 hours). Then send{"req":"hub.status"}. If that response includes anerrfield, the Notecard may not be properly associated with the project. - Make sure environment variables are being delivered by sending
{"req":"env.get"}in Notecard Playground. If the response is empty, the device hasn't received the fleet's environment variables yet, which can take up to 48 hours at the default 48-hour inbound sync cadence (see §6 step 3).
Fill percentage is incorrect or drifts over time.
- Confirm the tank capacity is set correctly in the
tank_capacity_galenvironment variable. Capacity doesn't affect the fill percentage, but a wrong value scalesfill_galandgal_per_dayproportionally. - Check the transmitter wiring. The loop supply must come from the 24 V boost converter output (not the 12 V rail), and the junction between the transmitter − terminal and the shunt must connect to A0. With an empty tank (float at the bottom), A0 should read ~0.48 V on a meter, and with a full tank it should read ~2.40 V.
- If the fill reading is consistently high or low across the full range, the
transmitter may have a non-standard live-zero or span. §9 "Commissioning
calibration" explains how to adjust
sensor_empty_maandsensor_full_ma.
Alerts are firing constantly, or false sensor_fault alerts.
- A
sensor_faultalert means the transmitter current is outside the 3.5–21 mA window (open circuit or short). Check the loop wiring. Both conductors from the transmitter should be connected and undamaged, and the shunt resistor should be solidly connected. - High-consumption alerts may be false positives after a refill event. The
firmware requires
consumption_alert_streakconsecutive above-threshold cycles before alerting, so raiseconsumption_alert_streakin the environment variables to make it less sensitive to post-refill transients. - Increase
alert_cooldown_hrto stretch the minimum hours between repeated alerts of the same type.
Very high or very low current readings in serial debug.
- The firmware reads the transmitter current every sample cycle and prints it to the debug console (the LPUART on the Notecarrier CX debug jack via an ST-LINK; see §7). An empty tank should show ~4.0 mA, and a full tank ~20.0 mA. If readings are consistently outside that range, re-check the shunt wiring and the 24 V boost converter output voltage, which should be exactly 24 V at the transmitter + terminal with no load attached.
Battery drains quickly / device stops after a few hours.
- Confirm the host is actually entering STOP2. The firmware calls
cxSleepUntilAttn()after each sample cycle, and if it can't sleep, the host runs continuously and drains the battery in hours. Check theATTN→D5jumper (see §5 Step 9), and confirm the firmware was built with USB support set to None. With the USB CDC stack enabled and no USB host attached, the host leaves STOP2 within a millisecond of entering it. - If you used the Mojo for validation, make sure it's removed before field deployment, since it adds a continuous measurement load even during deep sleep.
- For sites running over the Skylo satellite (NTN) link, review the Satellite Best Practices guide. Satellite session timing and duty-cycle behavior differ from cellular and may shift the energy budget, so size the battery and solar panel for the expected satellite session pattern.
WiFi is not connecting even after credentials are set.
- WiFi credentials arrive through environment variables and take effect on the
next inbound sync. At the default 24-hour report cadence, inbound syncs
happen every 48 hours, so credentials may take up to 48 hours to reach the
device. To speed that up, temporarily lower
report_interval_hrto 1 hour, then restore the original value once you've confirmed the connection. - You can also set WiFi credentials over USB at the bench (see §6 step 3) before sealing the enclosure.
- Notecard for Skylo's WiFi uses its onboard 2.4 GHz antenna, and a metal enclosure will weaken that signal. If WiFi fallback matters at this site, use a non-metallic enclosure or rely on cellular/satellite, which use the external MAIN antenna.
11. Limitations and Next Steps
This reference design covers the common case: a dealer instrumenting a tank fleet for demand-driven dispatch with a float transmitter at the gauge port, a temperature probe on the shell, and cellular (or satellite) backhaul. Every trade-off below was a deliberate scope choice, and each one has a clear path to extend it for a production rollout.
Simplified for the POC
Continuously-powered current loop. The Rochester Sensors M6300-LP + R6315-12 and similar LP gauge-port float transmitters use a continuously-powered 4–20 mA current loop. Running from the 24 V boost converter, the loop draws 96–480 mW from the 24 V rail (4 mA × 24 V to 20 mA × 24 V). With the boost converter's ≈ 87 % efficiency, the draw on the 12 V system rail runs continuously from ≈ 110 mW (empty tank) to ≈ 550 mW (full tank), which makes it the biggest load in the system power budget. In the worst case (full tank, 20 mA), the transmitter loop draws ≈ 550 mW for 24 h = ≈ 13.2 Wh/day from the 12 V rail. A 10 W solar panel at ≥ 4 h/day peak sun yields ≈ 40 Wh, well above the combined transmitter and electronics load for mid-latitude deployments. For low-sun environments or high-latitude winter deployments, size the panel to 20 W. The tank spends most of its time between 20–80 % fill (about 7–17 mA), so the draw typically sits between about 200 and 460 mW, below the 550 mW worst case.
Fill-gallons calculation uses a linear scale. computeFillGal computes
fill_gal = (fill_pct / 100) × tank_capacity_gal. A horizontal cylinder's
cross-section adds a small nonlinearity between liquid height and volume (the
relationship curves near the top and bottom of the tank), and the firmware
doesn't correct for it. A geometry correction table keyed to
tank_inner_diameter_in could improve accuracy below ~20 % or above ~80 % fill,
at the cost of another environment variable and more complex calibration.
Across the typical operating range (20–80 % fill), the linear-scale error is
small.
Satellite (NTN) operation requires an unobstructed sky-view antenna and an
initial non-NTN sync. Notecard for Skylo adds satellite with no extra module,
but the Skylo link only works if the Skylo-certified MAIN antenna is mounted
outdoors with a clear view of the sky (northern hemisphere: the southern sky).
It can't reach the satellite from under a metal obstruction. Satellite is also
opt-in at the Notecard level. The firmware turns it on by setting
card.transport to wifi-cell-ntn, and the board must complete at least one
initial non-NTN (cellular or WiFi) sync to associate with Notehub and register
its Notefile and environment variable templates before NTN works, so
commission each unit where it has terrestrial coverage. Over satellite, expect
alert latency in minutes (not seconds), keep each Note within the NTN payload
budget, and remember that inbound syncs draw on the bundled 10 KB satellite
allocation. At remote tank
sites, confirm there's a good sky-view antenna location before you rely on the
satellite path.
Electronics must be outside the classified area; IS barrier may be required. Blues Notecard and Notecarrier CX electronics are not rated for hazardous locations and must be installed outside the classified area boundary defined by NFPA 58 and the authority having jurisdiction. If the 4–20 mA wiring crosses the classified-area boundary, the AHJ may require a listed intrinsic-safety (IS) barrier in the loop circuit between the tank-side transmitter and the enclosure-side electronics. Transmitter selection, fitting compatibility, classified-area boundary determination, and any IS barrier requirement fall outside the scope of the electronics design. Any connection to a propane pressure vessel must comply with NFPA 58 and applicable codes, and it must be performed by a licensed LP gas technician.
Consumption rate requires stable readings over time. The EWMA consumption
rate only means something after several days of operation, so on first
deployment days_until_empty may be unreliable until the EWMA converges. The
firmware starts consumption at zero and doesn't report days_until_empty until
it has at least two consecutive valid fill readings.
No flow meter. The firmware estimates consumption rate from successive fill readings, as a delta-volume over delta-time approximation. That's accurate enough for daily route planning but not for billing or high-precision leak detection. A turbine or ultrasonic flow meter at the regulator outlet would measure consumption directly, though integrating one would take an additional pulse-counting or analog input channel.
Mojo is bench-validation equipment only. The firmware doesn't read the Mojo's LTC2959 coulomb counter at runtime. If fleet-level power telemetry would help the dealer's operations team, adding cumulative mAh to the daily summary is a simple extension.
Production Next Steps
- Transmitter calibration tool: a Notehub JSONata route transform that reads
transmitter_mafrom the first three Notes after provisioning and suggests asensor_full_macorrection automatically, so nobody has to do the manual commissioning step. - Refill event detection: when
fill_pctincreases by more than 10 % in a single sample cycle, emit arefill_detectedNote with the pre- and post-fill readings. That lets the dealer's billing system close the loop on deliveries without a separate ticket. - Per-tank consumption model: extend the EWMA with seasonal coefficients (summer
vs. winter heating demand) so
days_until_emptyaccounts for known demand patterns instead of only trailing-average consumption. - Over-the-air host firmware updates through Notecard Outboard DFU once the fleet gets large. Then you can push a threshold recalibration or a new alert type fleet-wide without a site visit.
12. Summary
The dealer who used to roll a truck every six weeks because the calendar said so now rolls one because a specific tank is at 22% and trending toward empty in nine days. The build is a float transmitter at the gauge port, a temperature probe on the shell, and a single Notecard for Skylo with cellular, satellite, and WiFi on one module. It reaches cellular near town and the Skylo satellite network at the remote cabin with nothing to swap, and a daily summary in Notehub lets every tank in the territory report on itself. That means fewer miles, fewer emergency calls, and no more Friday-night apologies to the customer whose heat just went out.
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
- Modules
- Sensor reading strategy
- Sensor selection rationale
- Event payload design
- Low-power strategy
- Retry and error handling
- Key code snippet 1: template definition
- Key code snippet 2: 4-20 mA level transmitter current → fill percentage → fill gallons
- 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