Municipal Wastewater Lift Station Monitor

This reference application is intended to provide inspiration and help you get started quickly. It uses specific hardware choices that may not match your own implementation. Focus on the sections most relevant to your use case. If you'd like to discuss your project and whether it's a good fit for Blues, feel free to reach out.
Building with an AI assistant? Point it at this accelerator's firmware — firmware/lift_station_monitor/ — so it can learn how the device works and help you adapt it to your hardware.
This project is a downtime prevention retrofit for municipal wastewater lift stations that catches pump failures, discharge obstructions, and high-water conditions before they become a sanitary overflow. A handful of sensors and a single Notecard for Skylo — one module that carries cellular, WiFi, and Skylo satellite radios and fails over between them automatically — transform a sealed concrete vault into a remotely-monitored station that delivers alerts to the on-call crew within minutes, not hours after a manual site visit. One SKU and one firmware image cover the entire fleet, from the station three blocks from city hall to the one at the edge of the service territory beyond any cell tower.
1. Project Overview
The problem. A lift station (also called a pump station) is a below-grade concrete vault or roadside cabinet that collects raw sewage from the surrounding gravity sewer system and pumps it uphill toward the treatment plant. Every municipality has dozens of them, often scattered across low-lying neighborhoods, industrial zones, and rural road shoulders — most with no onsite staff and no way to know what's happening inside until a citizen calls to report a smell or, worse, a spill.
When a lift station fails, the wet well — the collection basin that feeds the pumps — fills up and overflows. The result is an SSO: a sanitary sewer overflow. SSOs draw immediate regulatory attention; they trigger EPA reporting obligations, risk consent decree violations, and require expensive emergency cleanups. A station that fails on a Friday evening and isn't discovered until Monday morning is a public health event, a PR crisis, and a significant unplanned expense all at once. The failure modes are rarely dramatic: a pump fails to start because its float control sticks, a discharge check valve fails and allows backflow that clogs the impeller, or a wet-well float switch trips but no one receives the alarm because the SCADA dial-up modem lost its phone line. Each of these is detectable minutes after it starts — if someone is watching.
This project is that watcher. It straps to the inside of the station, samples the wet-well level every 60 seconds, measures current draw on each pump, and monitors the high-water float switch — four sensing points across three sensor types. Onboard edge logic on the STM32-based host MCU evaluates three fault rules every 60 seconds and routes alerts to the Blues Notehub cloud service the instant any rule trips — over cellular where a tower is in reach, and over the Skylo satellite network where one isn't. The on-call crew gets paged before the wet well overflows, not after.
Why Notecard for Skylo. The wireless-first architecture here isn't a convenience — it's a necessity. Lift stations sit in concrete vaults underground, often with no AC power outlet in the vault itself (power runs to the pump control panel, not a wall socket). They're geographically distributed across a municipality in a pattern that matches the sewer network, not the municipal network: there's no fiber running to a roadside pump cabinet, and there's no corporate WiFi AP that can reach through a concrete lid to a sensor inside. Utility supervisors would need to deploy and maintain a WiFi access point at every single station to achieve what one Notecard covers automatically.
Notecard for
Skylo
(NOTE-NBGLWX) carries three radios on one M.2 module — cellular (LTE-M / NB-IoT
/ GPRS), WiFi, and satellite over the Skylo
non-terrestrial network (NTN) — and selects among them automatically. The
firmware sets a single
card.transport
preference of wifi-cell-ntn: prefer WiFi where an accessible AP happens to be
in range (rare for a sealed vault), fall back to cellular (the de-facto primary
at the vast majority of municipal infrastructure), and fall back again to Skylo
satellite at stations on the fringe of the service territory, beyond the reach
of any cellular carrier. Failover is handled inside the Notecard; the host
firmware never branches on which network is live. That collapses what used to be
a two-SKU decision — a cellular Notecard for in-coverage stations, a satellite
device for the rest — into a single part number, a single antenna kit, and a
single firmware image that deploys unchanged across the entire fleet. There is
nothing to swap when a station turns out to have weaker coverage than the survey
suggested: the same board that runs on cellular downtown automatically reaches
the Skylo network at the rural lift station.
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 sealed NEMA 4X enclosure mounted inside the lift station's above-grade control cabinet, powered from the 120 VAC control circuit that already powers the pump starters. The specified Blues hardware and NEMA 4X ABS enclosure are not rated for hazardous (classified) locations. Wet wells and sealed vaults can accumulate methane and hydrogen sulfide — both potentially classified atmospheres under NFPA 820 / NEC Article 820. Do not install this hardware inside the wet well or any classified-atmosphere zone. If your jurisdiction classifies the vault interior as a hazardous location, any hardware in that zone must be rated for the classification; consult a licensed electrical engineer before proceeding. Sensor cables enter through conduit fittings: one multiconductor cable to the submersible level transducer in the wet well, two split-core CT jaws clamped around the pump motor supply conductors inside the control panel, and one float switch cable to a new dedicated high-water alarm float switch hung in the wet well alongside the station's existing level floats. No station modification is required beyond adding three sensor connections to the existing control wiring. Notecard for Skylo's antenna cables exit through a conduit fitting to outdoor-mounted antennas on the cabinet exterior or above-grade access point — the same Skylo-certified antenna carries both cellular and satellite, so no antenna swap is needed if a station ends up relying on the satellite link.
2. System Architecture
Device-side responsibilities. Inside the above-grade cabinet, the
Notecarrier CX's STM32L433 host wakes once a minute when the Notecard raises its
ATTN pin (armed via
card.attn), reads
three sensor types across four inputs (level, CT1, CT2, float switch), and walks
the three fault rules before the wet well can shift more than an inch or two.
The Notecard sits next to it on I²C — no AT commands, no modem state machine, no
serial buffers to babysit. Between samples the host sleeps in STM32 STOP2 (a few
µA) with its RAM intact, so everything that has to carry over to the next cycle
(the previous level reading, alert cooldowns, summary accumulators) simply stays
in memory and the host picks up exactly where it left off.
Notecard responsibilities. Notecard for Skylo runs the same playbook
regardless of which radio is live: queue Notes
locally, ship them on the
hub.set outbound
cadence (default 60 minutes), and push any sync:true alert to the head of the
queue for the next available session. The card.transport wifi-cell-ntn
preference set at boot decides the path automatically: on cellular that means a
Note in flight 15–60 seconds after the host queues it; when the unit has fallen
back to satellite it means first-in-line for the next Skylo session, which takes
a few minutes rather than seconds. The same Notecard also distributes
environment
variables
from Notehub on every inbound sync, so a utility supervisor can retune level
setpoints, current thresholds, or rising-rate sensitivity for the whole fleet
from a browser — no firmware reflash, no vault lid lifted.
Notehub responsibilities. The Notecard's embedded global SIM and bundled
Skylo satellite allocation hand events off to Notehub,
which timestamps and stores every one and applies project-level
routes. Alerts and
summaries land in separate Notefiles by
design — lift_alert.qo can fan out to PagerDuty or SMS while lift_summary.qo
lands in a long-term analytics store, with no filter logic in the route itself.
Smart Fleets make it
easy to group stations by service zone or pump type while still allowing
per-station overrides for the one vault that always reads a little funny.
Routing to the cloud (high level). Notehub supports HTTP, MQTT, AWS, Azure, GCP, Snowflake, and a wide range of other destinations; route setup is project-specific and not implemented here. See the Notehub routing docs for configuration guidance.
Satellite (NTN) operation considerations
The same Notecard for Skylo runs the same firmware everywhere, but at stations that routinely fall back to the satellite link the NTN path has operational characteristics that must be planned for, not assumed away. These apply only while the unit is transmitting over satellite — stations within cellular coverage behave like any other cellular Notecard.
Alert latency. sync:true Notes are prioritized for the next available
satellite session, but locating a Skylo satellite and completing transmission
can take several minutes. When a station is operating over satellite, "alert in
minutes" is realistic; "alert in seconds" is not. The 30-minute alert cooldown
in the firmware is still meaningful because a satellite station is reporting a
fault to the crew before the wet well overflows, not instantaneously.
Inbound sync cadence and data cost. Each inbound sync (used to pull updated
environment variables from Notehub) consumes approximately 50 bytes of satellite
data. At the default inbound:120 (every 2 hours), that is ~600 bytes per day —
a significant fraction of the bundled 10 KB satellite allocation. For stations
expected to operate over satellite, set inbound_interval_min to 240 or
higher via the Notehub environment-variable panel to reduce inbound satellite
data consumption. The firmware re-issues hub.set whenever
inbound_interval_min or summary_interval_min changes, so neither adjustment
requires a firmware reflash (see the env-var table in Section
6).
Payload discipline. The Skylo NTN link enforces a hard 256-byte maximum per
Note; Notes exceeding this limit are silently dropped by the satellite network.
The note.template
encoding used by this firmware (with format:"compact" and a numeric port)
keeps both lift_alert.qo and lift_summary.qo well within that ceiling. Do
not add free-form string fields to these Notefiles, and validate payload size on
any schema change.
Antenna placement. Satellite operation requires Notecard for Skylo's main
antenna outdoors and free from obstructions — for the northern hemisphere, an
unobstructed view of the southern sky, where Skylo's GEO satellites sit above
the equator. A station where the enclosure is entirely below grade or inside a
steel cabinet will require an above-grade antenna cable run; plan that conduit
path at installation time. Use only the Skylo-certified antenna supplied with
Notecard for Skylo on the MAIN u.FL port; substituting an uncertified antenna
risks regulatory non-compliance and link failure. This same antenna also carries
the terrestrial cellular signal, so the placement that enables satellite
fallback serves cellular as well.
Power envelope. Notecard for Skylo idles at typically ~8 µA. A network session — cellular or satellite — draws on the order of ~250 mA average from the onboard modem, with brief higher peaks (the BG95-S5 can pull nearly 2 A for a few milliseconds on a 2G transmit burst). The HDR-15-5 (3 A rated) handles these peaks with margin. See the Validation section for a per-state current breakdown.
Mandatory initial non-NTN sync. Before any satellite (NTN) operation is
possible, Notecard for Skylo must complete at least one non-NTN sync with
Notehub over cellular or WiFi to associate with the project and register
Notefile templates. The cold-boot hub.set in periodic mode performs that
first sync over cellular/WiFi automatically, so ensure the unit has cellular (or
WiFi) coverage during initial commissioning, even if the deployment site relies
on satellite for routine operation.
3. Technical Summary
What you'll have: A Notecarrier CX + Notecard for Skylo sending sample lift_alert and lift_summary events to your Notehub project every 60 seconds without needing sensors in the field.
- Create a Notehub project at notehub.io and copy its ProductUID.
- Flash the firmware:
(Replace
arduino-cli core install STMicroelectronics:stm32 arduino-cli lib install "Blues Wireless Notecard" "STM32duino Low Power" "STM32duino RTC" cd firmware/lift_station_monitor arduino-cli compile -b STMicroelectronics:stm32:Blues:pnum=CYGNET,usb=none \ --build-property "compiler.cpp.extra_flags=-DPRODUCT_UID=\"com.example:demo\"" \ -u -p /dev/ttyACM0/dev/ttyACM0with your Notecarrier serial port; on macOS use/dev/tty.usbmodem*; on Windows useCOM*.) Theusb=noneboard option disables the USB CDC stack, which must be off for the host to sleep (see Section 7);sketch.yamlin that directory sets the same board options. - Open the ST-LINK virtual COM port at 115200 baud (debug output is on the
Notecarrier CX debug jack, not USB); verify logs show "Notecard configured,"
hub.setrequests, andnote.addcalls. - Check Notehub Devices — your Notecard appears within 60 seconds. Click it
to see
lift_alert.qoandlift_summary.qoevents in the Events panel. - Tune thresholds — in the Fleet panel, set environment variables (e.g.,
high_level_pct: 50.0) and watch the serial log show the updated values on the next wake.
For bench-only testing, use compile-time flags to inject synthetic sensor values (see "Bench fault simulation" below in Section 8).
A single SKU covers the full deployment spectrum. Every station uses a Notecard for Skylo (datasheet): stations within LTE coverage transmit over cellular, and stations beyond reliable cellular reach fall back automatically to the Skylo satellite network — no second device, no different part number, no antenna swap. The firmware and Notehub configuration are identical everywhere; stations expected to operate over satellite simply benefit from wider inbound sync intervals to conserve bundled satellite data. See Satellite (NTN) operation considerations in Section 2.
Here is a sample Note this device emits:
{
"file": "lift_alert.qo",
"body": {
"alert": "pump_fail_to_start",
"level_pct": 87.4,
"pump1_amps": 0.2,
"pump2_amps": 0.1,
"float_sw": false
},
"sync": true
}4. Hardware Requirements
| Part | Qty | Rationale |
|---|---|---|
| Notecarrier CX | 1 | Integrated carrier with an onboard STM32L433 host — no separate MCU needed for this analog + digital sensor mix. The host sleeps in STOP2 between samples and the Notecard's ATTN pin wakes it. |
| Notecard for Skylo (NOTE-NBGLWX) (datasheet) | 1 | One M.2 module carrying cellular (LTE-M / NB-IoT / GPRS, Quectel BG95-S5 modem), WiFi (Silicon Labs WFM200S), and Skylo satellite (NTN) radios. The firmware's card.transport wifi-cell-ntn setting makes it prefer cellular at in-coverage stations and fall back automatically to the Skylo satellite network at stations beyond cellular reach — no second device or part-number decision. Removes per-site network provisioning; the embedded global SIM and bundled satellite allocation cover the whole fleet from one SKU. Requires the antennas below. |
| Blues Mojo | 1 | Coulomb-counter on the power rail for bench-validation of the sleep/wake/transmit energy profile. Not deployed to the field. |
| WIKA LH-10, 0–15 PSI gauge, 4–20 mA 2-wire, 316L SS, IP68, with vented cable | 1 | Submersible hydrostatic level transmitter purpose-built for water/wastewater wet-well immersion (the WIKA S-10 is a general-purpose / sanitary transmitter; the LH-10 is the family member rated for permanent submersion in raw sewage). The vented polyurethane cable (an internal vent tube equalizes the sensor's reference side to atmosphere, making the measurement gauge pressure = water head) doubles as the support tether — secured with a stainless cable grip at the cover plate. 316L SS wetted parts tolerate raw sewage. 15 PSI range covers ≈ 10 m of wet-well head. 4–20 mA 2-wire loop-powered output; wiring is identical to any other loop-powered transmitter. Specify "vented cable" and M20×1.5 or ½ NPT conduit seal fitting when ordering. Available from WIKA, instrumart.com, and major industrial distributors. |
| 150 Ω, 1% resistor (level sensor shunt) | 1 | Converts 4–20 mA loop current to 0.6–3.0 V for the 3.3 V STM32 ADC (full-scale within VREF). |
| SCT-013-030 split-core CT, 30 A / 1 V RMS (SparkFun SEN-11005) (current-output CT; add external burden resistor for 1 V_rms scaling) | 2 | Non-invasive current sensing on each pump motor supply lead; no break in the power circuit required. 30 A range is appropriate for single-phase motors up to approximately 5 HP at 230 V (FLA ≈ 28 A). Larger or three-phase motors require a higher-ratio CT. See Limitations. |
| TRRS 3.5 mm audio jack breakout (SparkFun BOB-11570) | 2 | The SCT-013's pigtail terminates in a TRRS plug; this breakout exposes Tip and Sleeve for the AC signal and shield/return. |
| 10 kΩ, 1% resistor (CT bias divider, 2 per pump) | 4 | Two-resistor divider centers the AC CT signal at VREF/2 ≈ 1.65 V so the unipolar STM32 ADC sees only positive voltages. |
| 10 µF electrolytic capacitor (CT bias decoupling, 1 per pump) | 2 | Low-pass filter on the bias node; reduces HF noise on the ADC input. |
| Gems Sensors RS-500-Y-PP, SPST N.O., polypropylene float switch | 1 | Sewage-rated polypropylene construction; normally-open contact closes on high-water. Mounts through the wet-well cover or on a cable-held hanger bracket. Available from Grainger and industrial distributors. Specify vertical or horizontal actuation to match the wet-well geometry. |
| MeanWell HDR-15-24, 85–264 VAC input, 24 VDC / 0.63 A, DIN-rail | 1 | AC–DC DIN-rail supply that derives 24 VDC from the station's 120 VAC control circuit. Powers the 4–20 mA sensor loop; 15 W is ample for the 0.48 W peak loop load. |
| MeanWell HDR-15-5, 85–264 VAC input, 5 VDC / 3 A, DIN-rail | 1 | AC–DC DIN-rail supply that derives 5 VDC for the Notecarrier CX USB-C port from the same 120 VAC control leg. |
| Skylo-certified LTE/satellite antenna included with Notecard for Skylo (u.FL, supports the S-Band / L-Band B23 / B255 / B256 bands) | 1 | Connects to the MAIN u.FL port and carries both the terrestrial cellular signal and the Skylo satellite link — a single antenna for both networks. Use only the Skylo-certified antenna supplied with Notecard for Skylo; substituting an uncertified antenna risks regulatory non-compliance and link failure. Mount outdoors on the cabinet exterior or above-grade access point with an unobstructed view of the sky (northern hemisphere: the southern sky); route through a liquid-tight fitting. For an external SMA mag-mount instead of the bare u.FL antenna, add a u.FL-to-SMA-F bulkhead pigtail (e.g. Taoglas CAB.0150.A.01). |
| 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 outdoors with a clear sky view alongside the main antenna; route through a liquid-tight fitting. |
| Hammond 1554N2GCLY NEMA 4X ABS enclosure, 8.07 × 6.10 × 3.94″ | 1 | Polycarbonate-gasketed splash-resistant housing. Use liquid-tight conduit fittings (Heyco or equivalent) for all sensor cable entries. |
Bundled connectivity (Notecard for Skylo): Ships with an active global SIM including 500 MB of cellular data and 10 years of service, plus 10 KB of bundled Skylo satellite data — 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). Minimizing inbound sync frequency conserves the bundled satellite allocation. See Section 2 for guidance.
5. Wiring and Assembly
Safety. Lift-station control panels contain 120 VAC mains wiring operating in wet, corrosive, and potentially hazardous-atmosphere environments. Installation must be performed by a licensed electrician or qualified instrumentation technician following your jurisdiction's electrical code and your utility's lockout/tagout (LOTO) procedures before opening any panel. Wet wells are classified confined spaces — follow applicable confined-space entry (CSE) regulations (OSHA 29 CFR 1910.146 in the US) before entering the vault: test for H₂S and oxygen deficiency, establish an attendant and rescue plan, and use supplied-air or appropriate respiratory protection as required. Raw sewage contains pathogens; wear appropriate PPE and observe hygiene protocols for all work inside the vault. This reference design is sensor and monitoring only — it does not command pump start/stop and makes no safety-critical outputs.
All host I/O lands on the Notecarrier
CX dual 16-pin headers.
Notecard for Skylo seats into the M.2 slot. Its MAIN u.FL port connects to the
included Skylo-certified antenna, which carries both the cellular and satellite
signals — connect it directly, or route it through a u.FL-to-SMA-F bulkhead
pigtail in the enclosure wall if you are using an external SMA mag-mount. Its
GPS u.FL port connects to the passive GPS/GNSS antenna. Both antennas mount
outdoors with a clear sky view (northern hemisphere: the southern sky), so the
unit can reach the Skylo satellite network wherever it falls back from cellular.
The Mojo connects via the Qwiic connector on
the Notecarrier and sits inline between the 5 V supply and the Notecarrier's
+VBAT pad during bench validation.
Level sensor (4–20 mA current loop)
The WIKA LH-10 is a submersible hydrostatic transmitter purpose-built for wet-well immersion, with a 4–20 mA two-wire loop-powered output and 316L stainless wetted parts. (The S-10 referenced in some WIKA literature is the family's general-purpose / sanitary variant, do not substitute it for permanent submersion in raw sewage.)
Physical installation. Lower the sensor into the wet well to a stable depth above the lowest feasible pumped level. The vented cable is the support tether — secure it with a stainless cable grip or strain-relief clamp at the wet-well cover plate so the sensor's weight is carried mechanically, not by the conductors. The vent tube inside the cable must be kept clear and open to atmosphere at the enclosure end; do not seal or submerge the vent opening. Thread the two conductors through a liquid-tight conduit fitting into the enclosure.
Loop wiring:
+24 Vfrom the MeanWell HDR-15-24 → sensor+conductor (typically red or brown, confirm with the cable marking).- Sensor
–conductor (black or blue) → 150 Ω, 1% shunt resistor →GND. - Notecarrier CX
A0→ junction between the sensor–conductor and the top of the 150 Ω shunt.
The 150 Ω shunt converts the 4–20 mA loop current to 0.6–3.0 V, which maps
linearly to 0–100% wet-well depth within the STM32L433's 3.3 V ADC reference.
Loop margin: V_supply – V_shunt_max = 24 V – 3.0 V = 21 V, well above the
sensor's minimum loop voltage (typically 10–12 V).
Pump current sensors (SCT-013-030 CTs)
Each CT clips around one supply conductor feeding a pump motor (clamp on only
one leg, clamping both legs will cancel the fields and read zero). Wire each CT
circuit identically; the following shows pump 1 on A1:
- Insert the CT's TRRS plug into the SparkFun BOB-11570 breakout.
- Tip (signal) and Sleeve (return/shield) are the active terminals on the BOB.
- Bias circuit on
A1: connect two 10 kΩ resistors in series from+3V3toGND; the junction (midpoint) is the bias node at VREF/2 ≈ 1.65 V. Place a 10 µF capacitor from the bias node toGNDto filter HF noise. - CT Tip → bias node; CT Sleeve →
GND. - Notecarrier CX
A1→ bias node.
Repeat for pump 2 CT on A2 with its own independent bias circuit.
Float switch
Install the Gems RS-500-Y-PP as a dedicated monitoring float, independent of the station's existing pump-control and SCADA float switches. Do not tap into existing float-control or alarm wiring — those circuits typically carry 120 VAC or drive pump starter contactors and are not safe or appropriate for direct GPIO connection. Hang the new float at the desired high-water monitoring setpoint (typically just below the station's rated overflow level) alongside the station's existing level floats; the wet-well cover usually has spare conduit entries or room for an additional cable grip.
The Gems RS-500-Y-PP provides a galvanically isolated SPST N.O. dry contact rated for this environment:
- One float switch terminal → Notecarrier CX
D2. - Other terminal →
GND. - In firmware,
D2is configuredINPUT_PULLUP; the switch closing pulls the pin to GND (logic LOW = alarm active).
Host-sleep jumper (ATTN → D5)
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; 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 rather than sleep the host. See Putting
a Host to Sleep Between Sensor
Readings
for the full pattern.
Power
- 120 VAC from the station's control circuit → MeanWell HDR-15-24 (24 VDC) and MeanWell HDR-15-5 (5 VDC), both DIN-rail mounted inside the NEMA 4X enclosure.
- HDR-15-24
V+→ 4–20 mA level sensor loop positive terminal. - HDR-15-24
V-→ system GND (the same node as Notecarrier CX GND and the bottom of the 150 Ω shunt). This common connection is required to complete the 4–20 mA loop return path. Without it the loop current has no return and the shunt voltage will be incorrect. - HDR-15-5
V+→ Notecarrier CX USB-C port (or +VBAT pad if USB-C is not used). - HDR-15-5
V-→ system GND. - Mojo sits inline on the 5 V rail between the HDR-15-5 output and the Notecarrier CX power input during bench validation (see Section 8).
6. Notehub Setup
6.1 Project and Device Claim
- Create a project. Sign up at notehub.io and create
a project. Copy the
ProductUID and paste it
into the firmware as
PRODUCT_UID(inlift_station_monitor_helpers.h). Rebuild and flash. - Claim the Notecard. Power the Notecarrier CX. On first cellular sync, the Notecard associates with your project automatically — no manual claim step required. Watch the Devices panel in Notehub; your Notecard appears within 60 seconds.
6.2 Environment Variables for Threshold Tuning
All thresholds below are optional overrides of firmware defaults. Set them via Notehub > Fleet > Environment, not in the firmware. Any variable set in Notehub is picked up by the device on its next inbound sync without a firmware reflash.
| Variable | Default | Purpose |
|---|---|---|
pump_on_amps | 3.0 | Current draw (A) above which a pump is considered running. Adjust for larger/smaller motors. |
high_level_pct | 85.0 | Wet-well fill level (%) at which the fail-to-start check activates. Should be set below the float switch trip point. |
rising_rate_pct | 2.0 | Level rise (% per 60-second sample) while a pump is running that triggers a clog alert. |
summary_interval_min | 60 | Minutes between summary Notes. The firmware also re-issues hub.set outbound to match this value whenever it changes, keeping the Notecard's outbound sync window aligned with the summary rate. |
inbound_interval_min | 120 | Minutes between inbound syncs (environment-variable pulls from Notehub). Satellite deployments: set to 240 or higher to conserve bundled satellite data (~50 bytes per inbound sync). The firmware re-issues hub.set inbound to match this value whenever it changes — no firmware reflash required. |
To set variables: Click your project's Fleet, then the Environment tab.
Add each variable as a key-value pair (e.g., high_level_pct = 75.0), then
click Save. The device pulls the updated values on its next inbound sync.
6.3 Routing Events to the Outside World
Add routes in Notehub > Routes to forward events to your on-call notification system (PagerDuty, Slack, email, webhook) or analytics backend:
- Alert route: Route
lift_alert.qoto your real-time on-call service (Notehub supports HTTP, MQTT, AWS SNS, Azure Event Hubs, Slack, PagerDuty, and others). - Summary route: Route
lift_summary.qoto a long-term analytics store (Snowflake, BigQuery, TimescaleDB, etc. via HTTPS or JDBC).
Keeping alerts and summaries in separate Notefiles means each route handles them independently — alerts fire immediately for urgent notification, summaries batch for efficient storage. See the Notehub routing docs for supported destination types and step-by-step setup.
7. Firmware Design
The firmware is split across four files in
firmware/lift_station_monitor/:
| File | Role |
|---|---|
lift_station_monitor.ino | Main sketch: setup(), loop(), runSampleCycle(), runDetectionCycle(), sendAlert(), sendSummary() |
lift_station_monitor_helpers.h | Compile-time constants, AppState struct definition, extern globals, and helper-function prototypes |
lift_station_monitor_helpers.cpp | Helper implementations: parsing, clamping, notecardConfigure(), defineTemplates(), fetchEnvOverrides(), applyHubSetIfChanged(), sensor reads |
cx_sleep.h | Host sleep: card.attn request, STOP2 entry, ATTN wake |
The .ino file is self-contained for the Arduino IDE (which compiles .ino +
.cpp files in the same sketch folder together automatically); the split keeps
the main sketch readable and puts reusable utilities in their own compilation
unit.
Dependencies:
- Arduino core for STM32
(
stm32duino/Arduino_Core_STM32). Blues Wireless Notecard(note-arduinolibrary). Install via Arduino Library Manager orarduino-cli lib install "Blues Wireless Notecard".STM32duino Low Power(and its dependencySTM32duino RTC) — install via Library Manager. Provides the STOP2 entry and ATTN-pin wake used incx_sleep.h.
Build settings: the Notecarrier CX's host is the same STM32L433 as the Blues
Cygnet, so the board is Blues boards → Cygnet (pnum=CYGNET), with USB
support (if available) set to None (usb=none). With the USB CDC stack
enabled and no USB host attached (the Notecarrier CX USB-C switch in NC, or a
power-only USB-C feed), 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 — an ST-LINK V3 exposes it as a virtual COM
port; see Serial Logging With
STLINK.
Modules
| Responsibility | Function |
|---|---|
Notecard configuration (hub.set, card.transport wifi-cell-ntn for cellular→satellite fallback, accelerometer disable) | notecardConfigure |
| Notefile template registration | defineTemplates |
| Env-var threshold fetch (every wake) | fetchEnvOverrides |
Re-issue hub.set when summary_interval_min changes (every wake, in loop()) | applyHubSetIfChanged |
| Level sensor ADC read and % conversion | readLevelPct |
| CT-based pump current measurement | readPumpAmps |
| Float switch debounced read | readFloatSwitch |
| Three-rule fault detection with cooldowns | runDetectionCycle |
| Immediate-sync alert emission | sendAlert |
| Hourly aggregated summary emission | sendSummary |
Host sleep (STOP2 until the Notecard raises ATTN) | cx_sleep.h: cxSleepBegin, cxSleepUntilAttn |
| Application state (held in RAM across sleep cycles) | AppState struct |
Sensor reading strategy
-
Level (4–20 mA transducer). The firmware averages 64 ADC samples (with 500 microseconds inter-sample delay to allow the STM32 ADC input to settle) and maps the result to a 0–100% scale using calibration constants derived from the 150 Ω shunt physics: 4 mA → 745 counts, 20 mA → 3723 counts on a 12-bit, 3.3 V ADC.
analogReadResolution(12)is called insetup()to enable 12-bit mode on the STM32L433. -
Pump current (SCT-013-030 CT). Each CT produces an AC signal centered at VREF/2 by the bias resistor divider. The firmware first measures the DC bias over 256 bare
analogRead()calls, then accumulates the squared deviation from that bias over 1024analogRead()calls and takes the RMS. The actual sampling window is MCU/ADC-rate-dependent — the STM32L433's successive-approximation ADC completes a raw conversion in a few microseconds, but each ArduinoanalogRead()call adds tens of microseconds of pin and ADC setup overhead, so 1024 samples spans on the order of tens of milliseconds (roughly 100 ms, per the firmware's own estimate) — several mains cycles — and the integration window is not synchronized to the AC mains cycle. This is sufficient for detecting whether a pump is running or not, but does not constitute a calibrated true-RMS measurement. The SCT-013-030's specification is 1 V RMS per 30 A RMS, soI_rms = V_rms × 30. Both CT channels are read every cycle; a pump is considered running when its current reading exceedspump_on_amps. -
Float switch. A 5-reading majority-vote debounce (50 milliseconds total) filters contact bounce. The result is a single boolean:
trueif the float switch is indicating a high-water condition,falseif normal.
Event payload design
Two template-backed Notefiles. Templates give both files a fixed-width wire encoding, shrinking each Note by roughly 3–5× versus free-form JSON — meaningful over a multi-year deployment with 24 summary Notes per day per station.
lift_alert.qo — Emitted immediately (within seconds for cellular, minutes
for satellite) when any fault rule trips. Example:
{
"file": "lift_alert.qo",
"body": {
"alert": "pump_fail_to_start",
"level_pct": 87.4,
"pump1_amps": 0.2,
"pump2_amps": 0.1,
"float_sw": false
},
"sync": true
}Fields:
alert: one of"pump_fail_to_start","pump_clog", or"high_water_alarm".level_pct: wet-well fill at time of alert.pump1_amps,pump2_amps: instantaneous current draw on each motor supply at time of alert.float_sw: whether the high-water float switch is closed (active) at time of alert.
lift_summary.qo — Queued every 60 minutes (or summary_interval_min) and
flushed in the next Notecard outbound session. Example:
{
"file": "lift_summary.qo",
"body": {
"level_pct": 42.1,
"level_avg_pct": 38.6,
"pump1_amps_avg": 14.2,
"pump2_amps_avg": 0.0,
"pump1_run_min": 18.0,
"pump2_run_min": 0.0,
"float_sw": false,
"alert_count": 0,
"level_faults": 0,
"ct1_faults": 0,
"ct2_faults": 0
}
}Fields:
level_pct: instantaneous reading at the end of the summary window.level_avg_pct: mean of all 60 samples collected during the window (or fewer ifsummary_interval_minwas changed mid-window).pump1_amps_avg,pump2_amps_avg: average current draw per pump across the window.pump1_run_min,pump2_run_min: how many minutes each pump was detected running (useful for lead/lag duty balance assessment).float_sw: whether the float switch is closed at summary time.alert_count: how many alerts fired during this window (0 = clean window, >0 = trouble).level_faults,ct1_faults,ct2_faults: count of samples where the sensor ADC returned an out-of-range value (open circuit, short, rail saturation, or other hardware fault). If nonzero, the associated average field is degraded by hardware issues, not actual station state.
Low-power strategy
setup() runs once at power-up to configure the Notecard; each pass through
loop() is one wake cycle and lasts only a few seconds: read sensors (~300
milliseconds for CT RMS), evaluate rules, queue or sync Notes, then call
cxSleepUntilAttn() (in cx_sleep.h). That function sends a
card.attn sleep
request for SAMPLE_INTERVAL_SEC seconds, waits for the Notecard to pull ATTN
low, and puts the STM32 host into STOP2, where it draws a few microamps with its
RAM intact. When the interval elapses the Notecard raises ATTN; the rising
edge on D5 wakes the host and execution resumes on the next line of loop(),
so the AppState struct simply lives in RAM — nothing is serialized to the
Notecard. If the sleep request fails (the Notecard is still booting after a cold
start) or ATTN never goes low (jumper missing), loop() waits out the
interval awake and tries again on the next cycle. Notecard for Skylo itself sits
in its own low-power
idle state between
radio sessions (~8 µA @ 5 V), regardless of which radio it last used.
Sync strategy. The Notecard runs in
hub.set mode:"periodic"
— the correct choice for a duty-cycled sensor node. In periodic mode the radio
is fully off between sessions; the Notecard wakes on the configured outbound
timer, ships queued Notes, then returns to low-power idle. notecardConfigure
sets outbound:60 (60-minute outbound sync interval) and inbound:120 (2-hour
env-var pull cadence). These cadences are deliberately decoupled from the
60-second sample interval: sensor reads accumulate in the in-flight summary
window, and the Notecard opens a radio session only once per hour for summaries.
Alert Notes set sync:true, which causes the Notecard to open a session as soon
as the host queues the Note — bypassing the outbound timer entirely.
notecardConfigure also issues a one-time
card.transport
{"method":"wifi-cell-ntn"} so the Notecard prefers WiFi, then cellular, then
Skylo satellite (NTN) — the fallback that lets the same firmware serve both
in-coverage and beyond-coverage stations. For stations expected to operate over
satellite, outbound:60 is a reasonable starting point, but the inbound:120
cadence should be reviewed: each inbound satellite check consumes approximately
50 bytes of bundled satellite data, and twice-hourly polling is a significant
fraction of the 10 KB bundled allocation. Raise inbound_interval_min to 240
or higher via the Notehub env-var panel for satellite stations. If the outbound
summary rate is also reduced (via summary_interval_min), set both env vars
together. The firmware re-issues hub.set with the updated outbound and inbound
values whenever either variable changes — no firmware reflash needed.
Sampling cadence (60 seconds) and transmission cadence (default 60 minutes, set
by summary_interval_min) are deliberately decoupled: each summary window's
sensor reads feed one summary Note, but only alert Notes bypass the outbound
timer. The firmware re-issues hub.set if an operator changes
summary_interval_min via Notehub, so the Notecard's outbound window stays
aligned with the summary rate automatically. At nominal operating conditions a
single-station deployment generates one outbound session per summary interval
plus occasional alert sessions — a tiny fraction of Notecard for Skylo's
included 500 MB of cellular data, and (for stations operating over satellite) a
meaningful but manageable fraction of its bundled 10 KB satellite allocation.
Retry and error handling
- Cold-boot I²C race. The first
hub.setusesnotecard.sendRequestWithRetry(req, 10)to paper over the window where the host MCU comes up before the Notecard's I²C listener is ready — this is a documented condition in thenote-arduinolibrary. - Env-var fetch failure.
fetchEnvOverridesusesrequestAndResponseand silently returns on a NULL response. A failedenv.geton any given wake retains the last valid threshold values held in RAM. No alert is emitted; the system continues sampling at the previously known thresholds. - Alert deduplication. Per-alert cycle-based cooldown counters (30 cycles × 60 seconds = 30 minutes) prevent a sustained fault condition from paging the on-call engineer repeatedly. Each alert type re-arms independently; a pump fail-to-start and a float-switch alarm can page simultaneously.
- State after a reset. Application state lives in RAM and survives every
STOP2 sleep; only a power cycle or reset clears it. In that case
setup()zero-initializes all state and re-runshub.setandnote.template, both of which are idempotent at the Notecard. Summary-window accumulators reset to zero; at most one summary window of data is lost. note.addretry and accumulator preservation.sendAlertusesnotecard.sendRequestWithRetry(req, 5)so a transient I²C hiccup gets a second chance before the call is declared failed; alert cooldowns are armed only on a confirmed success, meaning a failed send leaves the cooldown at zero and the alert is retried on the next 60-second cycle.sendSummarychecks the return value ofnotecard.sendRequest()and the caller clears summary accumulators only on success — if the Notecard is temporarily unreachable the accumulated window data is preserved and the send is retried on the next cycle.note.templateregistration is retried on every wake until both templates succeed (seeg_state.templates_registered). The remaining narrower gap: neithersendAlertnorsendSummaryinspects the Notecard response'serrfield, so a Notecard-side error that does not produce a NULL response is not surfaced to the host log. Production deployments that need end-to-end confirmation should addnotecard.responseError(rsp)checks around thesendRequestreturn path.- Sensor open/short detection — partial coverage. The level sensor path is
fault-checked at the ADC: an averaged count below
LEVEL_ADC_FAULT_LO(≈ 645) flags an open loop (sensor unplugged, broken vented cable, lost loop power), and a count aboveLEVEL_ADC_FAULT_HI(≈ 3823) flags a shorted loop or severe overpressure. Both conditions emitLEVEL_INVALID_SENTINEL(-9999) instead of a clamped 0 % / 100 % reading and incrementlevel_faultsin the hourly summary. The CT channels are similarly guarded: a bias point outside[CT_BIAS_MIN, CT_BIAS_MAX](1024–3072 counts) flags a broken bias-divider resistor or a CT terminal shorted to a rail, and any sample insideCT_RAIL_MARGINof 0 or 4095 during the RMS window flags rail saturation (shorted secondary, severely over-ranged input). Faulted CT samples emitCT_INVALID_SENTINELand incrementct1_faults/ct2_faults. The remaining gap is an open CT secondary winding combined with an intact bias divider: that condition reads ≈ Vref/2 with near-zero variance, indistinguishable in software from a legitimately idle pump. Operators should treat a sustainedct*_faultscount or an unexplained high-water alarm with reported zero pump current as a prompt for a physical inspection.
Key code snippet 1: CT RMS current measurement
Two-phase CT read: establish the bias point, then compute RMS of the AC deviation.
// Step 1: measure DC bias (mid-rail ≈ 1.65 V in 12-bit counts)
long bias_sum = 0;
for (int i = 0; i < CT_BIAS_SAMPLES; i++) {
bias_sum += analogRead(pin);
}
float bias = (float)bias_sum / (float)CT_BIAS_SAMPLES;
// Step 2: RMS integration (1024 samples; duration is MCU/ADC-rate-dependent)
double sq_sum = 0.0;
for (int i = 0; i < CT_RMS_SAMPLES; i++) {
float s = (float)analogRead(pin) - bias;
sq_sum += (double)s * s;
}
float v_rms = (float)sqrt(sq_sum / CT_RMS_SAMPLES) * (3.3f / 4095.0f);
float amps = v_rms * CT_AMPS_PER_VOLT; // 30 A per 1 V RMSKey code snippet 2: three-rule fault detection with cooldown
// Rule 2: Pump fail-to-start — level deep, no pump running
if (!any_on && level_pct >= g_high_level_pct &&
g_state.cooldown_fail_to_start == 0) {
sendAlert("pump_fail_to_start", level_pct, p1_a, p2_a, float_sw);
g_state.cooldown_fail_to_start = ALERT_COOLDOWN_CYCLES;
g_state.alert_count++;
}
// Rule 3: Pump clog — pump running, level still rising
float delta = level_pct - g_state.prev_level_pct;
if (any_on && delta >= g_rising_rate_pct && g_state.cooldown_clog == 0) {
sendAlert("pump_clog", level_pct, p1_a, p2_a, float_sw);
g_state.cooldown_clog = ALERT_COOLDOWN_CYCLES;
g_state.alert_count++;
}Key code snippet 3: immediate-sync alert
sync:true tells the Notecard to bypass the hourly outbound window and wake the
radio immediately — critical for a fault event where minutes matter.
J *req = notecard.newRequest("note.add");
JAddStringToObject(req, "file", NOTEFILE_ALERT);
JAddBoolToObject(req, "sync", true); // bypass outbound interval; wake radio now
J *body = JAddObjectToObject(req, "body");
JAddStringToObject(body, "alert", type);
JAddNumberToObject(body, "level_pct", level_pct);
JAddNumberToObject(body, "pump1_amps", p1_a);
JAddNumberToObject(body, "pump2_amps", p2_a);
JAddBoolToObject(body, "float_sw", float_sw);
// Retry briefly so a transient I²C hiccup doesn't silently drop a fault note.
// Returns true if the Notecard accepted the request; cooldown is armed only on
// success so a failed send is retried next cycle rather than suppressed for 30 min.
bool ok = notecard.sendRequestWithRetry(req, 5);8. Data Flow
Collected (every 60 seconds): wet-well fill level (%), pump 1 RMS current (A), pump 2 RMS current (A), float switch state.
Summarized (every 60 minutes): instantaneous and average level, average current per pump, per-pump runtime minutes in the window, float switch state, alert count in the window.
Transmitted:
lift_alert.qo— emitted immediately on any rule trip withsync:true. Each alert carries the triggering level, both pump currents, and float switch state so the operator can assess severity without waiting for the next summary.lift_summary.qo— queued hourly and flushed in the Notecard's next outbound cellular session (default 60 minutes after the previous one). One Note per hour per station, 24 Notes per day.
Alerts trigger on:
high_water_alarm— float switch contact closes (highest priority; hardware-level confirmation that the wet well is dangerously full regardless of level sensor state).pump_fail_to_start— wet-well level is at or abovehigh_level_pctand no pump is drawing current abovepump_on_amps. Indicates a pump that is not responding to its float control signal or has a failed contactor.pump_clog— at least one pump is drawing current (≥pump_on_amps) but the wet-well level is rising at ≥rising_rate_pctper cycle. Indicates that the pump is not moving water effectively — consistent with a discharge obstruction, a failed or partially closed check valve, a worn impeller, or high inflow exceeding pump capacity.
Routed: lift_alert.qo goes to a real-time notification channel.
lift_summary.qo goes to a long-term store. Notehub applies project routes
without any filter logic needed in the route itself, because the separation of
Notefiles at the source is already the filter.
9. Validation and Testing
Expected steady-state. In normal operation a properly functioning lift
station generates one lift_summary.qo Note per summary interval (default 60
minutes) and zero lift_alert.qo Notes. A healthy summary shows level_avg_pct
well below high_level_pct, alert_count: 0, and at least one pump with
non-zero runtime in the window. Lead/lag stations commonly show one pump
carrying the entire load in a quiet hour — zero runtime on the lag pump during a
single interval is normal, not an alarm condition.
Checking events in Notehub. After flashing and powering the board:
- Open Notehub > Devices.
- Click your Notecard; the Events tab shows all
lift_alert.qoandlift_summary.qoNotes received. - Click any event to expand its JSON payload and inspect the body fields (level, current, alert type, fault counts).
Bench fault simulation. The firmware clamps high_level_pct to a minimum of
1.0 % and rising_rate_pct to a minimum of 0.1 %, so setting either to
0.0 in Notehub has no effect — the firmware retains its last valid value. Use
the compile-time test flags below for guaranteed bench triggering, or follow the
hardware-assisted procedures:
pump_fail_to_start— compile-time flag (recommended). Rebuild the firmware with-DBENCH_FORCE_LEVEL_PCT=50.0in your build flags (Arduino IDE: add#define BENCH_FORCE_LEVEL_PCT 50.0at the top of the sketch, or pass viaarduino-cliwith--build-property "compiler.cpp.extra_flags=-DBENCH_FORCE_LEVEL_PCT=50.0"). The macro injects a synthetic 50 % level reading on every cycle, bypassing the ADC. Withhigh_level_pctset to1.0in Notehub env vars and no pump CT drawing current abovepump_on_amps, the rule fires on the next sample. A[BENCH]line in the debug log (ST-LINK virtual COM port) confirms the override is active. Remove the flag before flashing to a deployed station.pump_fail_to_start— hardware-assisted bench alternative. Sethigh_level_pctto1.0. Inject a mid-range voltage on A0 using a 10 kΩ / 10 kΩ resistor divider from +3V3 to GND (tap the midpoint, 1.65 V, through the 150 Ω shunt to A0, gives ≈ 2048 ADC counts ≈ 50 % fill level). With A1 and A2 reading near-zero (CTs clamped on a dead conductor), the rule fires on the next cycle after Notehub pushes the env-var update.pump_clog— compile-time flag (recommended). Rebuild with-DBENCH_CLOG_DELTA=2.0. The macro substitutes a synthetic +2.0 %/cycle delta for the measured level difference insiderunDetectionCycle(). Withrising_rate_pctat its default 2.0 % and a CT reading abovepump_on_ampsfor two consecutive cycles (clamp both CT jaws on a live conductor), the rule fires. Remove the flag before flashing to a deployed station.high_water_alarm— Briefly short pin D2 to GND (jumper wire or a bench push-button to GND) to simulate the float switch contact closing. No env-var change or firmware rebuild required.
Power validation with Mojo. Insert the Mojo inline on the 5 V rail feeding the Notecarrier CX during a bench run; Mojo measures the entire Notecarrier subsystem (Notecard + STM32 host + carrier regulators), not the Notecard alone. Approximate per-state draw at the 5 V rail (low-power design guide):
Notecard for Skylo's draw depends on which radio is active for a given session, but the idle and host-active states are identical regardless of network. Cellular and satellite sessions are similar in magnitude (~250 mA average from the onboard BG95-S5 modem), with brief higher peaks — a 2G transmit burst can momentarily pull nearly 2 A for a few milliseconds.
| Operating state | Notecard for Skylo | STM32 host | 5 V rail total |
|---|---|---|---|
| Deep sleep — host in STOP2, Notecard idle | ~8 µA | ~1–2 µA | ~10 µA |
| Host active — sensor reads + I²C (~300 milliseconds per 60 seconds cycle) | ~8 µA | ~20–30 mA | ~20–30 mA |
| Network session only, host asleep (batched summary) — cellular or satellite | ~250 mA avg, brief multi-hundred-mA (up to ~2 A for a few ms) peaks | ~1–2 µA | ~250 mA avg |
Host active + immediate network session (alert sync:true) | ~250 mA avg | ~20–30 mA | ~270 mA avg |
24-hour energy budget (cellular operation, powered continuously from 120 VAC supply, illustrative numbers; network session length dominates and varies with signal conditions — satellite sessions land in the same magnitude):
- Idle: ~10 µA × ~24 h ≈ ~0.25 mAh/day (the Notecard idle plus carrier-board quiescent draw, accumulated for the ~99.5% of the day the host is in STOP2).
- Host wakes: ~25 mA × 0.3 seconds × 60 wakes/hr × 24 hours ≈ 10,800 mA·s ≈ ~3 mAh/day (10,800 mA·s ÷ 3600 s/hr).
- Hourly outbound session: ~250 mA avg × ~10 seconds × 24 sessions/day ≈ 60,000 mA·s ≈ ~17 mAh/day (assumes a typical LTE-M session including network registration; a clean network and queued-template summary commonly lands in the 5–15 seconds range, while marginal coverage — or a fallback to satellite — stretches it well past 30 seconds).
- Occasional alerts (assume 2/day): ~250 mA × ~10 seconds × 2 ≈ ~1.4 mAh/day.
Total: roughly ~22 mAh/day in steady state, with hourly cellular sessions dominating the budget. Weak-signal sites where the modem camps in registration can easily push this 2–3× higher. The MeanWell HDR-15-5 (85–264 VAC input, 5 VDC 3 A output) provides continuous power from the station's 120 VAC control supply, so energy budgeting is not the deployment constraint, but validating these current draws with Mojo confirms the sleep/wake architecture is working and surfaces signal-quality issues early.
Expected Mojo trace over 24 hours:
- Dominant pattern: 60-second intervals of near-zero current (~10 µA, host and Notecard idle).
- Every 60 seconds: 300 milliseconds spike at ~25 mA (host wakes, reads sensors, issues I²C).
- Every ~60 minutes: longer spike at ~250–350 mA lasting ~2–5 seconds (Notecard modem session for summary).
- Occasional taller/longer spikes: alerts firing with
sync:true(variable timing, depends on fault events).
Troubleshooting constant mid-level draw: If Mojo shows a continuous ~10–50
mA rather than this spike pattern, the host is not entering STOP2 — check the
ATTN→D5 jumper (without it the firmware detects that ATTN never went low,
waits out the interval awake, and retries) and confirm the firmware was built
with the usb=none board option (with the USB CDC stack enabled the host leaves
STOP2 within a millisecond). The HST/NC DIP switch on the Notecarrier CX selects
only which device is connected to the USB-C port and has no effect on host
sleep. Mojo is the fastest way to confirm the sleep architecture is working
before the unit goes underground.
10. Troubleshooting
Firmware won't compile. Ensure you have the correct board package and libraries:
arduino-cli core install STMicroelectronics:stm32
arduino-cli lib install "Blues Wireless Notecard" "STM32duino Low Power" "STM32duino RTC"Notecard not appearing in Notehub. Check the firmware debug log:
- Open a serial terminal at 115200 baud on the ST-LINK virtual COM port (the Notecarrier CX debug jack; see Section 7).
- Power the Notecarrier CX; watch for
Notecard configuredandhub.setmessages. - Verify
PRODUCT_UIDinlift_station_monitor_helpers.his not empty and matches your Notehub project UID. - If
hub.setfails, the Notecard may not have cellular or WiFi coverage. Check antenna connection and signal strength (usecard.signalrequest via serial via the Notecard CLI).
No events appearing in Notehub after 5 minutes. Check:
- Network availability — does the Notecard have cellular or WiFi access?
Use the serial log or
card.signalto verify. - Event payload size — when Notecard for Skylo is transmitting over
satellite (NTN), Notes exceeding 256 bytes are silently dropped. Check the
serial log for
note.addsuccess/failure status. - Outbound sync window — summaries are queued and synced every 60 minutes
by default. Alerts fire immediately with
sync:true, so if you've triggered an alert and the Notecard has coverage, it should appear within 60 seconds. If not, check the serial log forsendAlertandnote.addoutput.
Constant non-zero current when powered (Mojo shows ~10–50 mA instead of spike pattern). The host is not entering STOP2:
- Verify the
ATTN→D5jumper on the Notecarrier CX header is in place (see Section 5); leaveENunconnected. - Confirm the firmware was built with USB support set to None
(
usb=none) — with the USB CDC stack enabled, the USB wakeup interrupt exits STOP2 immediately. - If still stuck, try a cold power-off (disconnect 120 VAC for 10 seconds), then reconnect.
Sensors read all-zero or invalid values. Check:
- Level sensor (A0): Verify the 150 Ω shunt resistor is connected in series between the sensor's negative wire and GND. The ADC should read 745–3723 counts (0–100 %). If it reads <745, the loop is open or the shunt is missing/damaged.
- CT channels (A1, A2): Verify the 10 kΩ bias divider and 10 µF decoupling cap are correctly installed. The DC bias should read around 2048 counts (VREF/2 ≈ 1.65 V). If the bias is outside 1024–3072, the resistors are mismatched or the CT is shorted.
- Float switch (D2): The pin is
INPUT_PULLUP, so a logic LOW (GND) is active (alarm). Jumper D2 to GND to test.
11. Limitations and Next Steps
This reference design covers the bulk of typical municipal lift stations, but it deliberately stops short of full SCADA replacement. The simplifications below are scope choices — places where a production deployment will want to add another sensor, another field-tunable, or another integration once a real utility starts running it.
Simplified for the POC
The simplifications below are scope choices — each is a place where a production deployment will want to add another sensor, another field-tunable, or another integration once a real utility starts running it.
Level sensor accuracy assumes a clean, straight wet well. The firmware maps ADC counts linearly to % fill using the transducer's pressure-to-level formula, assuming a uniform cross-section. Wet wells with irregular geometry or foaming conditions will read inaccurately; a field-calibrated offset via an environment variable is the production fix.
CT range and motor type. The SCT-013-030 (30 A) suits single-phase motors up to ~5 HP at 230 V. Motors larger than 5 HP single-phase, or any three-phase motor, require a higher-ratio CT (for example, the SCT-013-060, 60 A / 1 V). Three-phase installations also require one CT per phase; the current sketch reads one CT per pump, which serves as a running/not-running indicator on a single leg but does not produce true 3-phase RMS power.
Fail-to-start detection is level-threshold only. The firmware does not know
what level the pump float controls are actually set to. The high_level_pct
threshold is a firmware-side approximation of the hardware float-control
setpoint; the two may not match unless calibrated after installation.
No discharge pressure or flow measurement. The pump_clog rule fires on
level-rising-while-running, which is a necessary but not sufficient condition
for a clog — it also fires on genuine high-inflow conditions (heavy rain) or
when both pumps are running and inflow exceeds combined capacity. Production
deployments benefit from a discharge pressure sensor that can distinguish "pump
is pumping but the line is blocked" from "pump is pumping but inflow is just
overwhelming."
No SCADA integration. The sketch is standalone. Most municipal lift stations already have a local RTU or telemetry unit, and integrating with that system — reading dry contacts from the existing SCADA outputs, or making the Notecard's data available to the local RTU — is outside the scope of this POC.
Satellite (NTN) operation caveats. The firmware is identical everywhere, but
when Notecard for Skylo falls back to the satellite link it carries material
operational differences. Alert and summary Notes queue in the Notecard's local
store and sync on the satellite session schedule — sync:true Notes are
prioritized for the next available session, but locating a Skylo satellite and
completing transmission takes several minutes, not seconds. Each Note must stay
within the NTN 256-byte maximum; Notes exceeding this are silently dropped
by the satellite network without transmission. Inbound syncs (env-var pulls)
consume approximately 50 bytes of the 10 KB bundled satellite data allocation
each, so the default 2-hour inbound cadence costs roughly 600 bytes/day. The
Skylo-certified main antenna must be mounted outdoors with an unobstructed sky
view (in the northern hemisphere, an unobstructed view of the southern sky) for
the satellite link to work, so stations with the enclosure entirely below grade
will need an above-grade cable run. Satellite operation is opt-in at the
Notecard level: the firmware enables it by setting card.transport to
wifi-cell-ntn — without an ntn transport mode the board would stay on
cellular/WiFi only and never reach the satellite network. See Section
2 and Section
9 for the full breakdown.
Mojo is bench-validation only. The firmware does not read the Mojo's coulomb
counter register over Qwiic. Adding a mojo_mah field to the hourly summary is
a simple extension using the LTC2959 register map if fleet-level energy
telemetry is valuable.
Production Next Steps
Once a real utility is running the basic monitor, the following extensions are the natural progression — roughly from the most immediately useful to the most integration-dependent.
Per-station level calibration is the first refinement: a level_offset_pct
environment variable for wet-well geometry corrections, applied after the
ADC-to-percent conversion.
Three-phase current support adds A3 for the third CT leg on 3-phase pumps
and sums the squared contributions for a true 3-phase RMS reading.
A discharge pressure sensor on I²C (for example, a 4–20 mA → I²C transducer) would distinguish a clog from high-inflow, reducing false positives from storm events.
Notecard Outboard DFU enables over-the-air host firmware updates (on cellular/WiFi only), so threshold-algorithm improvements roll out to the fleet without a truck roll to each underground vault.
Pump cycle-count tracking logs each pump start and stop (the transition from
below to above pump_on_amps) to accumulate lifetime cycle counts and flag
motors approaching their rated duty-cycle limits.
Integration with the municipal SCADA or CMMS closes the loop: a
lift_alert.qo Notehub route that creates a CMMS work order automatically, so
the on-call response begins the moment the Notecard transmits, not the moment an
engineer reads an SMS.
12. Summary
The on-call engineer who used to first hear about a sanitary sewer overflow from a citizen complaint on Monday morning now gets a page at 2 AM Saturday — wet well rising while a pump is running, or a float switch closing, or a pump that should be on but isn't. Three sensors, one Notecard for Skylo that carries cellular, WiFi, and satellite on a single module, and three rules running on a sleeping host turn an opaque concrete vault into a station that calls for help before it overflows — and the exact same hardware and firmware drops onto the station three blocks from city hall or the one at the edge of the service territory, reaching cellular where there's a tower and the Skylo satellite network where there isn't, with nothing to swap.