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

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

Utility Distribution Transformer Load Monitor

Utility Distribution Transformer Load Monitor banner
note

This reference application is here to give you ideas and help you get started quickly. It's built on specific hardware that may not match yours, so focus on the sections that fit your use case. If you'd like to talk through your project and whether it's a good fit for Blues, reach out.

Building with an AI assistant? Point it at this accelerator's firmware in firmware/transformer_load_monitor/ so it can learn how the device works and help you adapt it to your hardware.

This project is an energy monitoring solution that gives utilities real-time load visibility at the pole, which is where transformer failures actually happen. A Notecarrier CX with three split-core current transformers on the secondary terminals and an I²C temperature sensor tracks per-phase loading, catches dangerous current imbalances, and correlates load with enclosure temperature as a stand-in for thermal stress. It all runs over cellular, and the site doesn't need any infrastructure.

1. Project Overview

The problem. A distribution transformer is the last powered device between the high-voltage grid and a customer's service entrance. Utilities usually instrument their substations carefully, with real-time SCADA, demand data, and fault recorders, but the transformer hanging on the pole outside a neighborhood gets none of that. The utility knows what's flowing into the substation. It has no idea what's flowing through any individual transformer until something fails.

Not knowing gets expensive. Distribution transformers age and fail for reasons you can identify and prevent. Sustained overloading degrades the insulation with each degree of excess temperature. Phase imbalance, where uneven single-phase loads sit on a three-phase transformer, creates circulating currents and localized hot spots. High ambient temperature combined with high loading adds thermal stress on top. None of these conditions shows up suddenly. They build over hours to days, and a transformer running hot on a Tuesday will fail during Friday's peak demand. It takes the neighborhood down with it, and the emergency truck roll costs many times what a proactive replacement would have.

This project fixes that blind spot. A handful of sensors clamped to the transformer secondary, a Notecard for the uplink, and a firmware loop that checks thresholds turn an opaque pole-top asset into continuously monitored equipment. It tells the operations center what's happening in real time and pages the right people before the neighborhood goes dark.

Why Notecard. Utility poles are some of the least connected infrastructure there is. They have no Ethernet or building WiFi, and often nothing but the transformer's own secondary power, so cellular is the only practical backhaul. The Notecard Cell+WiFi variant (MBGLW) carries a prepaid SIM with 500 MB of data and 10 years of service. You don't activate each site, negotiate carrier contracts, or file an IT ticket for network access. A utility rolling these monitors out across its service territory can put a single SKU on every pole in every neighborhood, from dense urban blocks to rural feeders, and Notecard's automatic carrier selection handles the varied network conditions without firmware changes.

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.

The power side is just as simple. With cellular, the device doesn't depend on any site infrastructure at all. The Notecard Cell+WiFi idles at ~18 µA @ 5V between samples while the host MCU sleeps in STOP2 at a few µA. At a 5-minute sample and hourly transmit cadence, the radio is awake for roughly 20–30 seconds per hour, which a small AC/DC supply tapped from the transformer's own secondary can comfortably sustain.

Deployment scenario. A NEMA 4X weatherproof enclosure is zip-tied or U-bolted to the transformer pole and powered from a small AC/DC supply connected to the 120VAC secondary. Three split-core CT leads exit through weatherproof cable glands and clamp directly onto the secondary conductors.

CT installation is non-invasive. The split-core sensors clamp directly onto energized secondary conductors without breaking the circuit. The power supply connection is a different story, because tapping the 120VAC secondary is live utility electrical work. Depending on utility policy and local jurisdiction, this step may need a scheduled outage, a qualified hot-work procedure, or written utility approval before the tap is energized (see §11 Limitations). You don't need an invasive tap into the transformer case or any on-site network provisioning. Once all the necessary work authorizations are in hand, a two-person crew that includes an electrician qualified for live secondary work can install and commission one unit in under an hour.

note

Power supply scope. This reference design assumes an accessible 120VAC secondary tap, which is the standard case for North American single-phase distribution transformers installed by a qualified crew. Three-phase padmount transformers, sealed primary-side enclosures, and installations where utility rules prohibit tapping the secondary need a different power arrangement. §11 Limitations covers the inductive harvesting alternative.

2. System Architecture

System architecture: three split-core CTs and MCP9808 → Notecarrier CX with STM32 host → Notecard MBGLW → cellular/WiFi → Notehub → routes to OMS/paging and historian

Device-side responsibilities. The host on the pole has a simple job that repeats. Every 5 minutes (configurable), the Notecarrier CX's STM32L433 host wakes and reads the configured CT channels. That's two for the common split-phase install, or three when phase_count=3 is set for three-phase. It also reads the I²C temperature sensor inside the enclosure, runs three threshold checks in firmware, and queues the results as Notes through the Notecard over I²C. As soon as that's done, it goes back to sleep. Between wakes the host sits in STM32 STOP2 (a few µA) until the Notecard's ATTN pin wakes it at the end of a card.attn sleep. STOP2 retains RAM, so all the state that has to survive the gap (summary accumulators, alert cooldown counters, elapsed time) just stays in memory, and nothing gets serialized to the Notecard.

Notecard responsibilities. The Notecard takes everything the host just queued and decides when it goes over the air. Notes wait in its on-device queue and flush on the hub.set outbound cadence (60 minutes by default). Any Note flagged sync:true, which includes every alert, skips that timer and ships right away. The Notecard also distributes environment variables from the Blues Notehub cloud service, so an operations engineer can retune thresholds across the fleet without re-flashing firmware. The device picks up the changes on its next inbound sync. GNSS is available on supported hardware if a future revision needs one-time asset location, but this design doesn't use it, since turning it on has antenna, power, and cost implications not covered here.

Notehub responsibilities. The Notecard's embedded global SIM carries each Note from the pole over supported carriers worldwide to Notehub, which ingests, stores, and routes the events downstream. Everything the device says goes into two Notefiles: xfmr_summary.qo for hourly templated rollups suited to long-term trending, and xfmr_alert.qo for event-triggered Notes that need immediate delivery. Grouping devices into Fleets by service territory or transformer rating lets engineers set threshold environment variables for a whole group (all the 25-kVA residential units on one feeder can share one threshold profile) and still override individual devices for unusual transformers. Smart Fleet rules can automatically assign a Notecard to the right fleet based on its reported data.

Routing to the cloud (high level only). Notehub can route to HTTP, MQTT, AWS IoT Core, Azure IoT Hub, GCP Pub/Sub, Snowflake, and other destinations. Route setup depends on your project, and this one doesn't ship with a specific downstream endpoint (see the Notehub routing docs). Alert and summary Notefiles are kept separate on purpose so each can be routed on its own. Alerts go to an outage-management or on-call system with low-latency delivery, and summaries go to a historian or analytics store with higher-volume, batched ingest.

3. Technical Summary

What you need:

  • Notecarrier CX + Notecard Cell+WiFi (MBGLW)
  • 3× YHDC SCT-013-000 CTs + 3.5 mm jack breakouts
  • Per-CT burden circuit: 22 Ω, 2× 10 kΩ, 10 µF cap (see §5)
  • MCP9808 I²C temperature sensor
  • 5V/2A AC/DC supply (e.g., MeanWell IRM-10-5)
  • Computer with Arduino IDE or arduino-cli

Bench validation (no live transformer needed):

  1. Assemble the three CT bias circuits on A0, A1, A2 (see the §5 wiring diagram), and fit the ATTN→D5 jumper for host sleep.
  2. Connect the MCP9808 to I²C (SDA/SCL).
  3. Flash the firmware. Set PRODUCT_UID in transformer_load_monitor.ino to your Notehub project ID, then upload with the board options STMicroelectronics:stm32:Blues:pnum=CYGNET,usb=none (see §7.1).
  4. Open a serial monitor at 115200 baud on the ST-LINK virtual COM port from the Notecarrier CX debug jack. You should see [sample] i_a=... lines every 5 minutes.
  5. Create a project in Notehub if you haven't already. The Notecard associates with it automatically on its first cellular connection.
  6. After 60 minutes you'll see [summary] queued on serial. Wait for the device's next outbound sync (hourly by default), then check Notehub Devices → Events for an xfmr_summary.qo entry.
  7. Clamp a CT on a known 120VAC load (a lamp works well) and check that the current value in the summary matches the expected load (within ~5%).

Here is a sample Note this device emits:

{
  "file": "xfmr_alert.qo",
  "body": {
    "alert":    "phase_imbalance",
    "i_a_rms":  91.4,
    "i_b_rms":  34.2,
    "i_c_rms":  88.7,
    "temp_c":   44.1,
    "extra":    62.6
  }
}

4. Hardware Requirements

PartQtyRationale
Notecarrier CX1Integrated carrier with an onboard STM32L433 host. It has three ADC-capable analog pins for the CT channels, I²C for the temperature sensor, and the Notecard's ATTN pin to wake the host from STOP2 deep sleep, so you don't need a separate MCU.
Notecard Cell+WiFi (MBGLW) (datasheet)1With cellular, no pole depends on IT. One SKU with a prepaid global SIM covers urban and rural feeders without carrier contracts or per-site activation.
Blues Mojo (bench / commissioning only, optional)1Coulomb counter for checking the power budget on the bench. It doesn't ship with the pole-mount unit and stays on the bench during commissioning. It's optional, but it lets you confirm the device hits its expected current draw before field deployment. See §9 for the current trace to look for.
YHDC SCT-013-000 split-core CT, 100A / 50mA3Non-invasive, clip-on current-output CT. The 100A / 50mA variant covers residential distribution transformers up to about 20 kVA at 240V secondary (~83A full-load). For transformers at or above 25 kVA at 240V (~104A at full load), use a higher-range current-output CT (see the note below). It connects directly to the resistor bias circuit with no integrator stage, and it's widely available from Mouser and Amazon.
22 Ω 1% resistor (burden, 1 per CT)3Converts the 50mA CT output to ~1.1V RMS at 100A, which sits well inside the host ADC's 0–3.3V input range after biasing.
10 kΩ 1% resistor (bias divider, 2 per CT)6Creates the Vcc/2 (~1.65V) DC bias point that centers the AC CT signal within the ADC input range.
10 µF electrolytic capacitor (1 per CT)3AC-couples the CT signal to the bias node and keeps DC from the bias network from leaking back into the CT.
3.5mm TRRS audio jack breakout (1 per CT)3The SCT-013-000 ends in a 3.5mm plug, and a breakout board saves you from cutting the cable.
Adafruit MCP9808 High Accuracy I2C Temperature Sensor Breakout #17821±0.25°C accuracy over –40°C to +125°C, an I²C bus, and a 2.7–5.5V supply make it a good fit for enclosure thermal monitoring. It doesn't use up any analog pins.
AC/DC supply, 5V/2A (e.g. MeanWell IRM-10-5)1Compact PCB-mount supply that makes 5V DC from the transformer's 120VAC secondary. The IRM-10-5 (10W, 2A continuous) has enough rating to sustain cellular sessions that can peak at ~2A. The smaller IRM-05-5 (1A) isn't enough for reliable cellular operation.
2A 250V slow-blow fuse, 5×20 mm (e.g. Littelfuse 218002)1Protects the 120VAC tap to the IRM-10-5 supply. It's required on the Line leg, installed in a panel-mount or PCB fuse holder rated for 250VAC.
5×20 mm fuse holder, 250VAC rated1Mounts the slow-blow fuse on the Line conductor inside the enclosure.
Cellular stub antenna (bench / commissioning only, included with Notecarrier CX dev kit)1Fine for bench bring-up and indoor testing, but not suitable for permanent outdoor pole-mount deployment.
Outdoor cellular antenna, 700–2700 MHz, SMA (deployment), e.g., SparkFun CEL-164321Replaces the bench stub on installed units. Choose a part rated for the outdoor temperature range and UV exposure. See §5 for placement guidance, which depends on the enclosure.
u.FL-to-SMA bulkhead pigtail, ≈100 mm, e.g., Taoglas CA-05250003 or equivalent1Connects the Notecard's u.FL antenna port to the externally mounted SMA antenna. It's required for every pole-mount deployment that uses the outdoor SMA antenna, and it doubles as the bulkhead lead through the wall of a metal enclosure.
NEMA 4X enclosure, ~6×4×2″1Weatherproof housing for outdoor pole-mount. Fiberglass or polycarbonate variants are lighter than steel and don't attenuate the cellular antenna.

All Blues hardware ships with an active SIM that includes 500 MB of data and 10 years of service, with no activation fees and no monthly commitment.

note

CT current range note. The SCT-013-000 rated at 100A suits transformers up to about 20 kVA at 240V secondary (~83A full-load, with ~17% headroom to the CT's 100A rated output). A common 25 kVA unit draws ~104A at full load, 4% above the CT's rating, so use a higher-range current-output CT for transformers at or above 25 kVA. Larger transformers (50–167 kVA) draw about 210–700A at 240V and need a higher-range current-output CT rated above that full-load current. The YHDC SCT-036-200 (200A/50mA) is one example of the type, though at 200A it tops out just under 50 kVA. Update CT_TURNS_RATIO and CT_BURDEN_OHMS in the firmware to match. Voltage-output CTs (such as the YHDC SCT-019-200, which produces 0.333V full-scale from an internal burden) are not drop-in replacements. They don't work with the external bias circuit above and need a different analog front end. See §11 Limitations for the production path.

warning

POC protection scope. The BOM above gives the 120VAC mains entry primary overcurrent protection (the slow-blow fuse) and nothing else. That's adequate starting hardware for a bench prototype or a controlled field trial, but it is not a complete protection story for a permanent outdoor pole-mount installation. A production design must add: a surge protective device (SPD) rated for the installation's overvoltage category (Category C / lightning-level, per IEC 61643-11 or UL 1449) on the Line/Neutral entry; enclosure grounding and bonding to the pole ground system per applicable NEC sections and utility rules; verified creepage and clearance in all mains-voltage wiring and connectors; UV and temperature qualification of all external cabling and the enclosure; and written utility approval before the secondary tap is permanently energized. Picking components for all of that depends on the installation and is outside this POC's scope. §5 and §11 discuss it further.

5. Wiring and Assembly

Wiring diagram: CT bias circuits (22 Ω burden + 10 kΩ divider + 10 µF cap) on A0/A1/A2; MCP9808 on I²C; ATTN → D5 jumper so the Notecard can wake the host from STOP2; 120 VAC → slow-blow fuse → IRM-10-5 → Mojo (bench) → +VBAT; outdoor SMA antenna via u.FL pigtail

All host I/O lands on the Notecarrier CX dual 16-pin header, and the Notecard Cell+WiFi (MBGLW) seats in the carrier's M.2 slot. If you use the Mojo for bench validation, it sits inline between the 5V supply and the Notecarrier's +VBAT pad. Connect its Qwiic cable to any free Qwiic port on the Notecarrier so it can log charge to the Notecard. The transformer firmware doesn't poll the Mojo. Its coulomb-count readings show up as separate Notecard events in Notehub, and it's only a bench commissioning tool, so it isn't installed in the deployed unit.

Bias circuit (build once per CT channel, identical for A, B, and C)

Each CT channel needs a small passive network that turns the CT's AC current into a voltage centered at Vcc/2, so the signal sits well inside the host ADC's 0–3.3V input range.

(per channel — repeat for A0, A1, A2)

 +3V3
   │
 R1 10 kΩ          ← top half of bias divider
   │
   ├─────────────────── ADC pin (A0 / A1 / A2)
   │                         │
 R2 10 kΩ          ←  C 10 µF (+ plate toward ADC pin)
   │                         │
  GND                   CT+ (TRRS tip)
                             │
                         Rb 22 Ω   ← burden resistor
                             │
                    CT– (TRRS sleeve) ──── GND

How it works:

  • Burden (Rb, 22 Ω): The SCT-013-000 is a current-output CT, and Rb converts its 50 mA RMS rated output to about 1.1 V RMS (≈1.56 V peak swing) at 100 A RMS primary current. Rb is wired directly across the CT output terminals (tip to sleeve).
  • Bias divider (R1 + R2, 10 kΩ each): Splits +3.3V to set a DC bias of ≈1.65V (Vcc/2) at the ADC pin, which centers the AC signal in the ADC's single-supply input range.
  • Coupling capacitor (C, 10 µF electrolytic): AC-couples CT+ to the ADC pin. The positive plate faces the ADC pin (higher DC potential, ≈1.65 V), and the negative plate faces CT+ (≈0 V DC). C passes the AC signal to the ADC pin and keeps DC from disturbing the bias point.
  • Each CT channel needs its own R1, R2, C, and Rb. Don't share bias nodes or burden resistors across channels.

Pin-by-pin connections

  • +3V3 → R1 top leg of each bias circuit; MCP9808 VDD.
  • GND → R2 bottom leg of each bias circuit; CT– (TRRS sleeve) of each jack; MCP9808 GND.
  • A0 → bias-divider mid-point (junction of R1 and R2) and positive plate of C for the Phase-A channel. The negative plate of C connects to CT+ (TRRS tip). CT+ also connects to one leg of Rb (22 Ω), and the other leg of Rb connects to GND / CT–.
  • A1 → same circuit, Phase-B CT.
  • A2 → same circuit, Phase-C CT. For split-phase installations (L1/L2 on A0/A1), leave A2 unconnected. The firmware defaults to phase_count=2, so you don't need to change any env var, and the unconnected channel is never sampled. For three-phase installations, wire this bias circuit and set phase_count=3 in Notehub before first power-on (see §6). Without that setting, A2 is never read and the imbalance calculations are incomplete.
  • SDA → MCP9808 SDA (the Notecarrier CX has on-board 4.7 kΩ pull-ups on the I²C bus).
  • SCL → MCP9808 SCL.
  • ATTN → D5 jumper (host sleep). Fit a short jumper between the ATTN and D5 pins, which sit on the same 16-pin header of the Notecarrier CX (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. See Putting a Host to Sleep Between Sensor Readings for the full pattern.
  • +VBAT → Mojo LOAD output (bench only); Mojo BAT input → 5V from the IRM-10-5 supply.
  • IRM-10-5 LINE/NEUTRAL → 120VAC tapped from the transformer secondary. The Line conductor must pass through a 2A 250V slow-blow fuse (in a rated fuse holder) before reaching the supply.

Cellular antenna

Connect the cellular antenna to the u.FL port on the front edge of the Notecard Cell+WiFi (MBGLW). The Blues development kit includes a compatible stub antenna, but for permanent pole-mount installations, a dedicated LTE antenna rated for outdoor use is preferred.

Placement guidelines:

  • Polycarbonate or fiberglass NEMA 4X enclosure: the antenna can mount inside the enclosure on a standoff or against the lid. RF passes through these materials with negligible loss.
  • Steel or aluminum enclosure: run a u.FL-to-SMA pigtail to a bulkhead-mount SMA antenna on the outside wall of the enclosure. Steel fully blocks the cellular signal from the inside.
  • Orient the antenna element vertically (parallel to the pole) for the best cellular radiation pattern.
  • Route the antenna cable away from the transformer core, primary conductors, and the 120VAC supply wiring so noise doesn't couple into the RF path.

3.5mm TRRS jack wiring

The SCT-013-000 uses a 3.5mm plug with tip = CT+ and sleeve = CT–. Wire the breakout board like this:

  • Tip → one end of the 22Ω burden (Rb) and to the AC coupling capacitor top plate
  • Sleeve → GND (and the other end of Rb)

CT installation

Clamp each CT around a single secondary conductor. Never put one around two conductors at once, because the fields would cancel. For a single-phase (120/240V center-tap) transformer, clamp the Phase-A CT on the X1 secondary conductor (L1 hot leg) and the Phase-B CT on the X3 secondary conductor (L2 hot leg). X2 is the center-tap neutral. Don't monitor X2 unless you want neutral current as a deliberate extra metric. Clamping one CT on X1 and the other on X2 would monitor one hot leg and the neutral, not the L1/L2 pair. For a three-phase transformer, clamp each CT on one secondary phase conductor.

You can leave the third CT slot (A2/Phase-C) unconnected for split-phase installations. The firmware defaults to phase_count=2, so no env var change is required. For three-phase installations, clamp the Phase-C CT on the third secondary conductor and set phase_count=3 in Notehub before first power-on. Without that setting, A2 is never sampled, and any C-phase overload or imbalance contribution is invisible to the firmware.

warning

CT open-circuit hazard. The SCT-013-000 is a current-output CT. Never disconnect the 22Ω burden resistor or unplug a CT jack while the CT is clamped on an energized conductor. An open-circuited current-output CT can develop dangerously high voltages across its terminals. Before you clamp the CT onto a live conductor, make sure the burden resistor is in-circuit. And before you disturb the burden wiring or disconnect the 3.5mm jack, take the CT off the conductor.

Safety. Distribution transformer secondary conductors carry lethal voltages. Installation must be done by qualified electrical workers wearing appropriate PPE, following utility safety procedures and applicable codes. This firmware is read-only and never switches the transformer or its connected loads. The CTs are non-invasive and clamp on without breaking the circuit, but working near energized conductors is still hazardous.

Surge and transient protection (POC omission). The 2A slow-blow fuse protects the IRM-10-5 supply against a sustained short, but it doesn't suppress the lightning-induced transients and switching surges that routinely show up on distribution transformer secondaries. That's acceptable for bench bring-up. For any unit meant for permanent pole-mount deployment, install a surge protective device (SPD) rated for the site's overvoltage category (Category C / lightning-level, per IEC 61643-11 or UL 1449) across Line/Neutral ahead of the fuse holder. Before energizing the tap, bond the enclosure to the pole grounding system and verify conductor routing, creepage/clearance distances, and any utility-specific installation approval requirements. See §11 for the full list.

6. Notehub Setup

  1. Create a project. Sign up at notehub.io and create a project. Copy the ProductUID (format: com.your-company.your-name:xfmr-monitor) and set it as PRODUCT_UID in the firmware before flashing.

  2. Claim the device. Power up the assembled unit. The Notecard associates itself with your project on its first cellular connection, so there's no manual claim step, and the device appears in the Devices tab within a minute or two.

  3. Create a Fleet per service territory. Fleets group devices for shared configuration. The natural unit is one fleet per transformer class or service territory. All the 25-kVA residential transformers on a feeder get the same rated-amps threshold, and the 50-kVA commercial units get a different one. Fleet-level environment variables reach every device in the fleet without re-flashing.

  4. Set environment variables. In Notehub, go to Fleet → Environment (or Device → Environment for a per-unit override). Every variable is optional for split-phase (L1/L2) installations, because the firmware defaults are set for that common case. Three-phase installations must set phase_count=3 and force an inbound sync before live monitoring begins. With the default phase_count=2 on a three-phase transformer, the A2/Phase-C channel is never read, and any C-phase overload or imbalance contribution is invisible to the firmware. The device picks up every other env var change on its next inbound sync (every 2 hours by default), with no truck roll.

    VariableDefaultPurpose
    sample_interval_sec300Seconds between sensor readings (minimum 30).
    summary_interval_min60Minutes between summary Notes. Changing this also re-applies hub.set to match the Notecard's outbound cadence.
    rated_amps100.0Transformer secondary rated amps per phase. The default matches the 100A SCT-013-000 CT range. Set it to the nameplate-derived secondary current (kVA rating ÷ secondary voltage), e.g., 83 for a 20-kVA / 240V unit or 62 for a 15-kVA / 240V unit. A 25-kVA / 240V transformer draws ~104A at full load, which is more than the default CT's 100A rating. Before deploying on transformers at or above 25 kVA, swap in a higher-range current-output CT and set this value to the transformer's actual nameplate current.
    overload_pct95.0Any single phase exceeding this percentage of rated_amps triggers an overload alert.
    imbalance_pct_thresh20.0(max_phase − min_phase) / max_phase above this percentage triggers a phase_imbalance alert. Only evaluated when at least one phase is carrying >2A.
    temp_alert_c70.0Enclosure temperature (°C) above which a high_temp alert fires. A typical enclosure's internal temperature limit is 50–65°C, and 70°C is a conservative field alert.
    alert_cooldown_sec1800Minimum seconds between successive alerts of the same type (default 30 minutes). Keeps a slowly drifting condition from setting off an alert storm.
    phase_count2Number of active CT channels (1–3). The default 2 matches the common split-phase (L1/L2 on A0/A1) installation, so it works out of the box with no env var change. Set to 3 for three-phase transformers (A0/A1/A2 all wired) before first power-on, or A2 is never read. Set to 1 for single-leg monitoring. Imbalance only means something across two or more phases, so with phase_count=1, imbalance_pct is always reported as 0.0 in both real-time alerts and summary Notes. With phase_count=2, L1 and L2 are compared without the unconnected C channel (always 0A) inflating the imbalance reading.
  5. Configure routes. Add one route for xfmr_alert.qo (to an outage-management system, on-call paging, or CMMS endpoint) and a second for xfmr_summary.qo (to a long-term store or analytics platform). Because the two Notefiles are separate, each can go to its own destination at its own urgency, and the routes don't need any filter logic.

What you should see in Notehub

Note: Template fields use numeric type hints to encode type and size, where 14.1 = 4-byte IEEE-754 float and 12 = 2-byte signed integer. Notehub decodes this compact binary format automatically.

  • _session.qo: Notecard session housekeeping on each cellular connect. If you see it, the radio link is healthy.
  • xfmr_summary.qo: one per summary_interval_min. Here's an annotated example payload from a three-phase install (phase_count=3, default rated_amps). The comments are only for explanation, and the actual JSON has none:
    {
      "i_a_rms":       47.3,   // phase-A mean RMS over loaded sample intervals only (A) — see §11
      "i_b_rms":       44.8,   // phase-B mean RMS over loaded sample intervals (A)
      "i_c_rms":       49.1,   // phase-C mean RMS over loaded sample intervals (A)
      "i_total":      141.2,   // sum of per-phase loaded-interval means (A)
      "loading_pct":   47.1,   // i_total / (rated_amps × phase_count) × 100; excludes idle intervals
      "imbalance_pct":  8.8,   // (max_phase − min_phase) / max_phase × 100; from loaded-interval means
      "temp_c":        38.5,   // mean enclosure temperature over valid temperature readings (°C)
      "overloads":       0,    // sample intervals during which any phase exceeded the overload threshold
      "samples":        12,    // loaded sample intervals (at least one phase above the noise floor)
      "total_wakes":   12      // all host wakes in the window (loaded + idle)
    }
    overloads counts sample intervals in which any phase exceeded the overload threshold, not distinct overload events. One sustained overload that lasts the whole summary window would report overloads: 12 at the default 5-minute sample rate. samples counts only intervals where at least one phase was above the 0.5 A noise floor, while total_wakes counts every host wake regardless of load. For time-weighted utilization analytics, multiply loading_pct by samples / total_wakes to account for idle intervals (see §11).
  • xfmr_alert.qo: emitted only when a threshold trips, and sent immediately. The alert field is overload, phase_imbalance, or high_temp.

7. Firmware Design

The firmware lives in the sketch directory firmware/transformer_load_monitor/ and is split across four files that must be compiled together:

FileRole
transformer_load_monitor.inoMain sketch: setup(), loop(), global declarations
transformer_load_monitor_helpers.hShared constants, structs, and function prototypes
transformer_load_monitor_helpers.cppAll helper function implementations
cx_sleep.hHost sleep: card.attn request, STOP2 entry, ATTN wake

The Arduino IDE and arduino-cli automatically compile every .ino, .h, and .cpp file in a sketch folder together, so always open or target the folder (firmware/transformer_load_monitor/), not an individual source file.

7.1 Installing and flashing

Dependencies:

  • Arduino core for STM32 (stm32duino/Arduino_Core_STM32). Add the board index URL https://github.com/stm32duino/BoardManagerFiles/raw/main/package_stmicroelectronics_index.json under File → Preferences → Additional Boards Manager URLs, then install "STM32 MCU based boards." The Notecarrier CX's host uses the Cygnet board definition (see the build settings below).
  • Blues Wireless Notecard library (note-arduino). Install it from the Arduino Library Manager (search for "Blues Wireless Notecard") or with arduino-cli lib install "Blues Wireless Notecard".
  • Adafruit MCP9808 Library, from the Arduino Library Manager (search for "MCP9808"). It depends on Adafruit BusIO, which the Library Manager installs for you.
  • STM32duino Low Power and its dependency STM32duino RTC (stm32duino/STM32LowPower). Install them from the Library Manager or with arduino-cli lib install "STM32duino Low Power" "STM32duino RTC". They provide the STOP2 entry and ATTN-pin wake used in cx_sleep.h.

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 (or the Notecarrier CX USB-C switch is 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).

Flashing with the Arduino IDE: Use File → Open… and select the folder firmware/transformer_load_monitor/, or double-click transformer_load_monitor.ino and the IDE loads all four files from the folder automatically. Apply the build settings above and click Upload. With the Notecarrier CX's USB-C switch in HST, you can reach the host's DFU bootloader over the USB-C port (hold BOOT, tap RST). You can also upload over SWD with an ST-LINK on the debug jack.

Flashing with arduino-cli:

# Install the STM32 core and libraries first if not already done
arduino-cli core install STMicroelectronics:stm32
arduino-cli lib install "Blues Wireless Notecard" "Adafruit MCP9808 Library" "STM32duino Low Power" "STM32duino RTC"

# Compile and upload — target the sketch folder so all four source files
# are included (/dev/cu.usbmodem* for Mac, /dev/ttyACM* for Linux).
# usb=none disables the USB CDC stack, which must be off for the host to sleep.
arduino-cli compile -b STMicroelectronics:stm32:Blues:pnum=CYGNET,usb=none firmware/transformer_load_monitor
arduino-cli upload  -b STMicroelectronics:stm32:Blues:pnum=CYGNET,usb=none -p /dev/cu.usbmodem* firmware/transformer_load_monitor

firmware/transformer_load_monitor/sketch.yaml sets the same board options, so running arduino-cli compile in that directory without -b picks them up too.

After flashing, open a serial monitor at 115200 baud on the ST-LINK virtual COM port. On first boot you should see [sample] i_a=... lines every sample_interval_sec seconds (5 minutes by default). If no CT is connected, then after summary_interval_min minutes (60 by default) you'll see [summary] queued (0 loaded / 12 total wakes). At that point the summary Note is queued in the Notecard's local on-device store, and it reaches Notehub on the next outbound cellular sync (60 minutes later by default). The summary always goes out as a liveness heartbeat, even with no load. Once the CTs are clamped on live conductors, the sample lines show non-zero current values and the summary line reports how many intervals were loaded out of the total wakes in that window.

7.2 Firmware constants and environment variables

transformer_load_monitor_helpers.h defines the main tuning constants:

ConstantDefaultMeaning
CT_SCALE(3.3/4096) × (2000/22)Converts raw ADC RMS counts to primary amps. 3.3V over 4096 counts is the volts per count of the 12-bit ADC, and 2000/22 is the CT turns ratio divided by the 22 Ω burden. The firmware computes it as (supply_v / adc_counts) × (CT_TURNS_RATIO / CT_BURDEN_OHMS), so for a higher-range CT you change those two constants and CT_SCALE follows.
CT_TURNS_RATIO2000Nameplate ratio (primary amps / secondary amps). For the SCT-013-000 (100A / 50mA) it's 2000. Higher-range CTs change this. A 200A / 50mA CT such as the SCT-036-200, for example, has a ratio of 4000.
CT_BURDEN_OHMS22Burden resistor value. Only change it if you substitute a different burden. CT_SCALE is derived from it, so it updates automatically.
CT_NOISE_FLOOR_A0.5Current values below this (in amps) are zeroed to suppress noise-floor pickup. Raise it to 1.0 if you see non-zero readings with no load.
CT_SAMPLE_PERIOD_US225Pacing interval (microseconds) between ADC samples. 225 microseconds gives ~4.44 kHz sampling, and the ~333-millisecond RMS window covers ~20 mains cycles at 60 Hz. Don't change it unless you understand the mains-cycle alignment issue described in §7.4.

7.3 Modules

ResponsibilityFunction
Notecard configuration (hub.set, note.template, card.motion.mode)hubConfigure, defineTemplates
Environment variable fetch + clampfetchEnvOverrides (batch env.get, inline JGetString + atol/atof parsing, range clamps)
Re-apply hub.set on summary-interval changefetchEnvOverrides (compares against state.lastAppliedSummaryMin)
CT RMS measurement (two-pass ADC sampling)readCtRms
Temperature reading via MCP9808readTemperatureC
Alert threshold evaluationcheckAlerts
Alert Note emission (sync:true)sendAlert
Hourly summary Note emissionsendSummary
Host sleep (STOP2 until the Notecard raises ATTN)cxSleepBegin, cxSleepUntilAttn (cx_sleep.h)

7.4 Sensor reading strategy

CTs. Each SCT-013-000 produces an AC current signal proportional to the line current flowing through its core. The external 22Ω burden resistor turns that into an AC voltage of about 1.1 V RMS (≈1.56 V peak) at 100 A RMS primary current, centered on the ADC's DC bias point (~1.65V = Vcc/2). The firmware uses a two-pass algorithm. First, 256 samples establish the actual DC offset, which may drift a little from Vcc/2 because of component tolerance. Then 1480 samples compute the RMS of the centered signal. Both passes pace samples to a fixed 225-microsecond period (≈4.44 kHz) using micros() and delayMicroseconds(), so the 1480-sample RMS window always spans ~333 milliseconds. That's about 20 mains cycles at 60 Hz, or ~16.7 cycles at 50 Hz. Without the pacing, the host ADC's native single-digit-µs throughput would finish the burst in well under one full mains cycle, and the RMS values would drift with whichever piece of the waveform happened to get captured.

The three channels are read one after another, so total active measurement time is about 1 second. The scale factor CT_SCALE = (3.3/4096) × (2000/22) converts ADC RMS counts directly to primary amps, and readings below 0.5 A are zeroed to suppress noise-floor pickup.

Temperature. On every wake, the firmware initializes the Adafruit MCP9808 (a cheap, idempotent begin() that doubles as a presence check), sets it to 0.0625°C resolution, reads it once, and puts it in shutdown mode before the host sleeps. A missing or unresponsive sensor returns –999.0°C. A separate valid_temp_samples counter only increments when the reading passes the –40°C to +125°C sanity range, and the summary average divides sum_temp_c by that counter instead of by total wake cycles. If a window has no valid temperature readings at all, the summary reports –999.0°C rather than an average that's biased low. The MCP9808 and the Notecard share the Notecarrier CX's on-board I²C pull-ups on the same bus, so you don't need external resistors.

7.5 Event payload design

The device uses one template-backed Notefile for summaries (xfmr_summary.qo) and one untemplated Notefile for alerts (xfmr_alert.qo). Templates give up some flexibility for efficiency. The Notecard stores fixed-length binary records internally, and the wire payload is ~3–5× smaller than free-form JSON, which matters for a fleet that may transmit hourly summaries for a decade.

Sample alert (phase imbalance event):

{
  "file": "xfmr_alert.qo",
  "body": {
    "alert":    "phase_imbalance",
    "i_a_rms":  91.4,
    "i_b_rms":  34.2,
    "i_c_rms":  88.7,
    "temp_c":   44.1,
    "extra":    62.6
  }
}

The extra field carries context that depends on the alert type. For overload it's the loading percentage of the worst phase, for phase_imbalance it's the computed imbalance percentage (max−min)/max, and for high_temp it's the total current across all phases at the time of the alert.

7.6 Low-power strategy

The device is grid-tied (a 120VAC tap on the transformer secondary), so the energy budget isn't a hard constraint, but the sleep pattern still matters. Keeping the host in STOP2 between samples gets rid of a continuous 50–80mA baseline, cuts heat buildup in the enclosure, and makes the power profile easy to read on a Mojo trace during bench validation.

After each sample cycle, loop() calls 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 and keeps its RAM. When the interval is up, the Notecard raises ATTN, the rising edge on D5 wakes the host, and execution picks up on the next line of loop(). Because the host resumes in place instead of cold-booting, the AppState struct (accumulators, cooldown counters, pending alerts, cached config) just lives in RAM. Nothing is serialized to the Notecard, and setup() runs only once per power-up. For the firmware author, sleeping is a single function call, and the Notecard keeps the schedule. If the sleep request fails or ATTN never goes low (say the jumper is missing), loop() waits out the interval awake and tries again on the next cycle, so the sample cadence holds either way.

Between cellular sessions, the Notecard Cell+WiFi itself idles at ~18 µA @ 5V. Alert Notes with sync:true skip the outbound queue and wake the radio right away, while summary Notes collect in the on-device queue and go out together once an hour.

7.7 Retry and error handling

  • hub_configured and template_configured are set only when the Notecard confirms the power-up hub.set and note.template requests, and loop() retries anything still unconfirmed on every wake.
  • cxSleepUntilAttn() returns false if the Notecard didn't accept the card.attn request, or if ATTN never went low because the ATTN→D5 jumper is missing. In that case loop() waits out sample_interval_sec awake with delay() and tries again on the next cycle. Sampling continues at the normal cadence, just at a much higher power cost.
  • fetchEnvOverrides reads every variable in one batch env.get. If that request fails, the firmware keeps its last good config (or the defaults on first boot), and any variable that's missing or empty falls back to its default. The firmware then clamps config values to sane ranges (e.g., sample_interval_sec has a floor of 30 seconds), so a misconfigured env var can't cause erratic behavior.
  • CT readings below the 0.5 A noise floor are zeroed. Sample intervals where no phase clears the noise floor are left out of current accumulation through the valid_samples counter, which makes the per-phase RMS averages loaded-interval averages rather than true time-weighted window averages. The total_wakes field in the summary payload records every host wake (loaded + idle), so dividing samples by total_wakes gives the fraction of the window that carried detectable load. That's useful for utilization weighting (see §11). A summary window with zero valid CT samples still emits an xfmr_summary.qo Note with zero current fields and samples: 0, which confirms the device is alive even when the CTs detect no load.
  • Each alert type has its own cooldown counter (6 cycles by default, which is 30 minutes at the 5-min sample rate), so a sustained overload doesn't page the operations center every 5 minutes. The alert types arm and disarm independently.
  • fetchEnvOverrides updates the hub.set outbound period whenever summary_interval_min differs from the cadence last applied to the Notecard, which keeps the Notecard's transmit timer in step with the firmware's summary window. A failed update is retried on the next wake.

7.8 Key code snippet 1: Note template definition

Template fields use numeric type hints: 14.1 means a 4-byte IEEE-754 float, and 12 means a 2-byte signed integer. NOTEFILE_SUMMARY is "xfmr_summary.qo", defined in transformer_load_monitor_helpers.h.

bool defineTemplates() {
    J *req = notecard.newRequest("note.template");
    JAddStringToObject(req, "file", NOTEFILE_SUMMARY);
    JAddNumberToObject(req, "port", 50);
    J *body = JAddObjectToObject(req, "body");
    JAddNumberToObject(body, "i_a_rms",       14.1);  // Phase-A RMS (A)
    JAddNumberToObject(body, "i_b_rms",       14.1);  // Phase-B RMS (A)
    JAddNumberToObject(body, "i_c_rms",       14.1);  // Phase-C RMS (A)
    JAddNumberToObject(body, "i_total",       14.1);  // Sum of phase currents (A)
    JAddNumberToObject(body, "loading_pct",   14.1);  // % of transformer nameplate
    JAddNumberToObject(body, "imbalance_pct", 14.1);  // Phase imbalance %
    JAddNumberToObject(body, "temp_c",        14.1);  // Enclosure temperature (°C)
    JAddNumberToObject(body, "overloads",     12);    // Overload intervals in window
    JAddNumberToObject(body, "samples",       12);    // Loaded sample intervals
    JAddNumberToObject(body, "total_wakes",   12);    // All host wakes in window
    bool ok = notecard.sendRequest(req);
    if (!ok) {
        debugSerial.println("[template] note.template (summary) failed; will retry on next wake");
    }
    return ok;
}

7.9 Key code snippet 2: CT RMS two-pass algorithm

Pass 1 finds the actual DC bias offset. It should be ~2048 counts for a well-built circuit, and measuring it accounts for component tolerance. Pass 2 computes the RMS of the centered AC signal with float accumulation, which avoids 32-bit integer overflow at full-scale input. Both passes pace samples to a fixed 225-microsecond period, so the RMS burst always spans ~333 milliseconds (≈20 mains cycles at 60 Hz) no matter how fast the host ADC natively runs. §7.4 explains why that matters.

float readCtRms(uint8_t pin) {
    // Pass 1: DC offset (~57 ms at the configured sample period).
    long sum = 0;
    uint32_t next_us = micros();
    for (int i = 0; i < CT_DC_SAMPLES; i++) {
        sum += analogRead(pin);
        next_us += CT_SAMPLE_PERIOD_US;
        int32_t wait = (int32_t)(next_us - micros());
        if (wait > 0 && wait < (int32_t)CT_SAMPLE_PERIOD_US) {
            delayMicroseconds((uint32_t)wait);
        }
    }
    int offset = (int)(sum / CT_DC_SAMPLES);

    // Pass 2: AC RMS (~333 ms ≈ 20 cycles @ 60 Hz / 16.7 cycles @ 50 Hz).
    float sum_sq = 0.0f;
    next_us = micros();
    for (int i = 0; i < CT_RMS_SAMPLES; i++) {
        float s = (float)(analogRead(pin) - offset);
        sum_sq += s * s;
        next_us += CT_SAMPLE_PERIOD_US;
        int32_t wait = (int32_t)(next_us - micros());
        if (wait > 0 && wait < (int32_t)CT_SAMPLE_PERIOD_US) {
            delayMicroseconds((uint32_t)wait);
        }
    }
    float rms_counts = sqrtf(sum_sq / CT_RMS_SAMPLES);

    // Convert ADC RMS counts → primary amps and suppress noise floor
    float i_rms = rms_counts * CT_SCALE;
    return (i_rms < CT_NOISE_FLOOR_A) ? 0.0f : i_rms;
}

7.10 Key code snippet 3: immediate-sync alert

sync:true tells the Notecard to skip the outbound queue timer and open a cellular session right away. Over cellular, the alert usually reaches Notehub within 15–60 seconds of the threshold trip. sendAlert() builds the Note from its arguments, so the same code sends overload, phase_imbalance, and high_temp alerts to NOTEFILE_ALERT ("xfmr_alert.qo"). It tries twice, and if both attempts fail, the alert stays in its pending slot for the next wake.

void sendAlert(uint8_t slot, const char *type, float i_a, float i_b,
               float i_c, float temp_c, float extra) {
    // ...
    bool queued = false;
    for (int attempt = 0; attempt < 2 && !queued; attempt++) {
        J *req = notecard.newRequest("note.add");
        JAddStringToObject(req, "file", NOTEFILE_ALERT);
        JAddBoolToObject(req,   "sync", true);

        J *body = JAddObjectToObject(req, "body");
        JAddStringToObject(body, "alert",   type);
        JAddNumberToObject(body, "i_a_rms", i_a);
        JAddNumberToObject(body, "i_b_rms", i_b);
        JAddNumberToObject(body, "i_c_rms", i_c);
        JAddNumberToObject(body, "temp_c",  temp_c);
        JAddNumberToObject(body, "extra",   extra);  // loading_pct, imbalance_pct, or i_total

        J *rsp = notecard.requestAndResponse(req);
        if (rsp) {
            const char *err = JGetString(rsp, "err");
            queued = !(err && *err);
            notecard.deleteResponse(rsp);
        }
        // ...
    }
    // ...
}

7.11 Key code snippet 4: sleeping until the Notecard raises ATTN

After sending any queued Notes, loop() asks the Notecard to hold ATTN low for sample_interval_sec seconds, confirms the pin has gone low, and puts the STM32 into STOP2. The host stays alive, so the firmware checks the result of the card.attn request instead of treating it as a fire-and-forget command. When the Notecard raises ATTN, execution resumes on the next line and loop() runs again with state intact.

void setup() {
    // ...
    // Arm the ATTN wake: the Notecard raises ATTN (D5) to end each sleep.
    cxSleepBegin();
}

void loop() {
    // ...
    // ---- Sleep ---------------------------------------------------------
    // Ask the Notecard to hold ATTN low for sample_interval_sec, then enter
    // STOP2.  When ATTN rises, execution resumes here and loop() runs again
    // with `state` intact in RAM.
    uint32_t sleep_sec = (cfg.sample_interval_sec > 0)
                         ? cfg.sample_interval_sec
                         : DEFAULT_SAMPLE_INTERVAL_SEC;
    if (!cxSleepUntilAttn(notecard, sleep_sec, NULL, &debugSerial)) {
        // The Notecard didn't take the sleep request, or ATTN never went low
        // (check the ATTN -> D5 jumper).  Keep the sample cadence and retry.
        debugSerial.println("[sleep] ATTN sleep failed — waiting out the interval awake");
        delay(sleep_sec * 1000UL);
    }
}

8. Data Flow

Data flow: three CT channels sampled via two-pass ADC RMS → accumulate into rolling window → three threshold rules → xfmr_alert.qo (sync:true, immediate) and xfmr_summary.qo (hourly template-encoded) → Notehub routes to OMS/paging and historian; state held in host RAM through STOP2 between wakes

Every 5 minutes (sample_interval_sec), the host wakes and reads the configured CT channels (A0 always, A1 when phase_count≥2, A2 when phase_count=3) and the temperature sensor. Then it checks three threshold rules against the fresh readings and the accumulated history.

Collected. Per-phase RMS current (A) on phases A, B, and C; enclosure temperature (°C); total CT current (sum of phases); derived loading percentage; derived phase-imbalance percentage; and the count of overloaded sample intervals in the current summary window.

Transmitted:

  • xfmr_summary.qo: one record per summary_interval_min (24 records/day by default). It holds per-phase means over loaded sample intervals (intervals where at least one phase exceeded the 0.5 A noise floor), mean enclosure temperature, the count of overloaded sample intervals, the number of loaded samples (samples), and total host wakes in the window (total_wakes). It's template-encoded, then queued and flushed by the Notecard's hourly outbound sync. Multiply loading_pct by samples / total_wakes to recover the true time-weighted window average (see §11).
  • xfmr_alert.qo: sent as soon as a threshold trips, with sync:true to bypass the outbound interval. Each alert type has a 30-minute cooldown to keep alerts from piling up into a storm.

Triggers. Three independent rules:

  • overload: any single phase exceeds rated_amps × (overload_pct / 100). The extra field carries the peak phase's loading percentage. A transformer that stays above 95% of its nameplate rating ages its insulation fast enough to measurably shorten its service life.
  • phase_imbalance: (max_phase − min_phase) / max_phase exceeds imbalance_pct_thresh while at least one phase is above 2A. The imbalance check is skipped entirely when phase_count=1 (single-leg mode), and imbalance_pct is always 0.0 in both the real-time alert path and the summary average. With phase_count=2, only L1 and L2 are compared, and the unconnected C channel (always 0A) stays out of the min/max calculation. The extra field carries the computed imbalance percentage. On three-phase transformers, phase imbalance generates negative-sequence currents that heat the core, waste capacity, and can push a lightly loaded phase into overvoltage.
  • high_temp: enclosure temperature exceeds temp_alert_c. The extra field carries the total current across all phases at the time of the alert. Temperature combined with load is the main driver of accelerated transformer aging, and the Arrhenius rule of thumb says every 10°C above design temperature cuts insulation life in half.

Routed. Both Notefiles go through Notehub, where operators set up routes that send xfmr_alert.qo to an outage-management system or on-call paging tool and xfmr_summary.qo to a long-term historian for trending and transformer-life analytics.

9. Validation and Testing

Expected steady-state behavior. On a healthy, lightly loaded transformer with well-balanced phases, the Notehub Events tab should show one xfmr_summary.qo per hour and zero xfmr_alert.qo events. The serial monitor should print [sample] lines every 5 minutes and [summary] queued once an hour (the Note reaches Notehub on the next outbound sync).

Bench bring-up without a live transformer. Connect the CTs to a known-current AC circuit (a 120VAC lamp load on one phase works well) and check that the i_a_rms field in the summary matches the expected current (within ~5%). With no load on a CT channel, the reading should be 0.0A. If it isn't, the bias network has a wiring error or the ADC offset is drifting outside the noise floor.

Triggering alerts for validation. Drop overload_pct to 50.0 in the Fleet environment, which is the lowest value the firmware allows (it clamps overload_pct to 50–125; see §7.7). The next inbound sync (≤2 hours by default) pulls the new value, and the next sample fires an overload alert on any load above half of rated_amps. Set overload_pct back to your intended value afterward.

Power validation with Mojo. The Notecard Cell+WiFi (MBGLW) idle current is ~18 µA @ 5V. Cellular transmit sessions draw ~250 mA on average, with brief peaks up to ~2 A. The host MCU sleeps in STOP2 between samples and adds only ~1–2 µA.

Expected power profile at the defaults (5-min sample, hourly transmit):

Firmware stateExpected current
Notecard idle between samples (radio off)~18 µA for the Notecard IC per the datasheet. The Mojo reading for the full board at 5V with the host in STOP2 (~1–2 µA) includes additional board quiescent draws, so use the Mojo trace, not the 18 µA figure, for whole-assembly power budgeting.
Host active: ADC sampling (~1 second per wake, 3 channels × ~20 mains cycles each)Measure with Mojo on your actual assembly. Host and Notecarrier board current during ADC sampling depends on the assembly and isn't a published Notecard figure.
Cellular session (hourly, ~10–30 seconds)~250 mA avg, ~2 A peak @ 5V
24-hour session energy (from published Notecard figures)Cellular sessions: 24 sessions × 10–30 seconds × 250 mA avg ÷ 3600 ≈ 17–50 mAh/day. Host ADC wakes and board/regulator quiescent current depend on the assembly, so measure both with Mojo on your actual hardware and add them to the cellular figure to get the whole-assembly daily budget before field deployment.

Here's what to look for on the Mojo trace:

  • Healthy: a steady low baseline at the board's quiescent current level, brief ~1 second blips every 5 minutes (host ADC reads across three channels), and one 10–30 second burst at ~250 mA each hour (cellular sync). Pin down the exact baseline with Mojo during commissioning, since it reflects Notecarrier and regulator draws with the host in STOP2.
  • Host not sleeping: a sustained baseline well above the quiescent level you measured with the host asleep. The host isn't entering STOP2. Check the ATTN→D5 jumper (the serial log shows [sleep] ATTN sleep failed when ATTN never reaches D5), and confirm the firmware was built with USB support set to None (usb=none). With USB CDC enabled, the host leaves STOP2 within a millisecond.
  • Weak signal: hourly bursts at the right spacing, but each burst is >60 seconds at peak. Route the cellular antenna away from the transformer casing.

Mojo is a bench-validation and regression tool, not a production requirement. Once a firmware revision passes the trace check, deployed units don't need it.

10. Troubleshooting

SymptomCauseSolution
Serial console shows [sample] lines but no [summary] lines after 60+ minutesSummary not being queuedCheck that the summary_interval_min env var is set (default 60). If you see [summary] queued but no Notehub events, the Notecard isn't syncing. See the "No events in Notehub" row below.
No events appearing in Notehub Events tab after 2+ hoursDevice not syncing to NotehubMake sure the Notecard has a valid SIM (Blues prepaid or user-provided). Check the Notecard's cellular signal with a card.wireless request from the Notecard In-Browser Terminal over USB (Notecarrier CX USB-C switch in NC). If the signal is poor, move the antenna away from the transformer core or switch to an outdoor SMA antenna (see §5).
Mojo trace shows sustained elevated baseline (not just brief ~1 second spikes every 5 minutes)Host not sleeping between samplesCheck that the ATTN→D5 jumper is seated on the Notecarrier CX header (see §5) and that the firmware was built with USB support set to None (usb=none). The serial log prints [sleep] ATTN sleep failed when cxSleepUntilAttn() can't sleep. If that line never appears but the baseline stays high, the USB CDC stack is usually to blame.
Current readings are non-zero even with no load on CTsBias circuit error or ADC offset driftCheck that all three bias circuits are wired correctly (see the §5 diagram) and that the capacitor's positive plate points toward the ADC pin. If the wiring is correct, the no-load noise is probably above the 0.5 A noise floor. Raise CT_NOISE_FLOOR_A in the firmware (to 1.0, say) to suppress it.
CT readings are consistently wrong (e.g., reading 50 A when the load is 100 A)Incorrect CT_SCALE constant or ADC resolution mismatchConfirm analogReadResolution(12) is called before any analogRead (it is, in setup()). If a different ADC resolution was selected before flashing, the scale factor is off by a factor of 2^(12 - new_bits) (a 10-bit setting reads 4× low). Recalculate CT_SCALE = (3.3 / 4096) × (2000 / 22) for 12-bit resolution, and see §7.2 for the formula.
Mojo shows correct steady state but summary payload has temp_c = -999.0MCP9808 not respondingCheck the I²C wiring (SDA/SCL) and confirm the I²C address is 0x18 (the default). Try the Adafruit_MCP9808 example sketch to see whether the sensor responds.

11. Limitations and Next Steps

A pole-mount monitor that drives operational decisions about live distribution equipment has a high bar to clear in production. It needs surge protection, signed utility approvals, billing-grade accuracy where that matters, and communications that survive when the transformer itself fails. This POC stays narrow on purpose, with three signals (per-phase load, imbalance, enclosure thermal stress) over one cellular path, so the alerting and trending pieces stay readable. The items below show where that scope ends and what a production build would naturally add.

Simplified for this POC

Each limit below was chosen to keep the alerting and trending code easy to follow, and each one notes what a production build would do differently.

The CT current range is bounded by the chosen sensor. The SCT-013-000 (100A) suits transformers up to about 20 kVA at 240V (~83A full-load). Transformers at or above 25 kVA at 240V draw ~104A, which is past this CT's rating, so they need a higher-range current-output CT (200A or 400A). Voltage-output CTs are not drop-in replacements (see §4). Update the firmware's CT_TURNS_RATIO and CT_BURDEN_OHMS constants to match the CT you choose, and all the downstream calculations scale automatically.

RMS accuracy is monitor-grade, not billing-grade. The two-pass ADC algorithm covers about 20 mains cycles per channel (~330 milliseconds per channel, ~1 second total for three channels) at the host ADC's throughput rate. That's accurate enough (~5–10%) for a load-threshold monitor, but it is not suitable for billing-grade energy metering. Extending the sample count to cover ~200 mains cycles per channel would improve RMS accuracy, at the cost of proportionally longer host-active time.

Summary averages reflect loaded intervals only. i_a_rms, i_b_rms, i_c_rms, i_total, loading_pct, and imbalance_pct in xfmr_summary.qo are computed only over the sample intervals (samples) where at least one phase exceeded the 0.5 A noise floor. Intervals with no detectable load are left out of the denominator. So a lightly or intermittently loaded window reports the average current during the intervals when load was present, not the true time-weighted average across the full window. That's the right behavior for fault detection and threshold alerting, because alert thresholds should be checked against actual load conditions without idle time diluting them. For utilization reporting or transformer-life analytics, multiply loading_pct by samples / total_wakes (both fields are in the summary payload) to recover the time-weighted window average.

Power factor is not measured. The firmware reports apparent current (A RMS), not real power (watts) or reactive power (VAR). A transformer's nameplate rating is in kVA (apparent power), so the loading percentage is correct as stated. Measuring true power would take voltage sensing on top of current sensing, which would be a meaningful production enhancement for a billing or power-quality application.

Temperature is single-point, an enclosure proxy and not a true ambient probe. This implementation uses an MCP9808 mounted inside the enclosure. It measures the enclosure's internal temperature, which isn't true outdoor ambient air temperature, and it isn't transformer core or winding temperature either. Enclosure temperature moves in the same direction as thermal stress, but it can drift far from outdoor ambient, especially on a sun-exposed pole, and it lags winding temperature by minutes to hours under transient load changes. A true ambient probe (in an external housing shielded from direct sun) or a winding-contact sensor (a PT100 RTD or thermocouple on the transformer case) would give you more actionable data. See Production Next Steps below.

This design uses conventional split-core CTs, not Rogowski coils. This implementation uses conventional current-output split-core CTs (YHDC SCT-013-000) instead of flexible Rogowski-type coils. True Rogowski coils produce a signal proportional to di/dt, so they need an external integrator board in front of the ADC. For power-frequency monitoring they have no inherent accuracy advantage over calibrated current-output CTs, and they add real cost and complexity. Flexible Rogowski coils (such as the Dent Instruments RoCoil or Magnelab RCT series) are the right choice for physically constrained installations where a rigid split-core jaw can't open wide enough to go around a large busbar or conductor bundle. Standard residential and commercial distribution transformer secondary leads don't have that constraint, so this design uses the simpler current-output CT.

There is no inductive power harvest. The project description mentions optional inductive energy harvesting from the transformer. This POC leaves it out, because a validated inductive harvester brings a lot of complexity and installation constraints compared with a direct 120VAC tap on the transformer secondary. Where tapping the secondary is impractical (sealed enclosures, third-party owned equipment), inductive harvesting is the right production next step. It would need its own separate, validated power-stage design, though, and that's out of scope here.

There is no backup power. If the transformer fails, which is exactly the scenario this device is trying to catch early, the monitor loses power too and can't report the outage itself. Adding a Blues Scoop with a small LiPo battery would let the device ride out a brief outage and send a "power lost" event before the battery dies. That's a meaningful production addition for "last gasp" outage detection.

Alert cooldown is cycle-based. The cooldown counter tracks sample cycles, not wall-clock time. If you change sample_interval_sec through an env var after an alert fires, the cooldown duration shifts by the same proportion. That's acceptable for most deployments. For strict SLA compliance, store the alert timestamp in persistent state instead.

There is no surge or transient protection. The 2A slow-blow fuse on the 120VAC tap protects against a sustained overcurrent fault, but it doesn't suppress lightning-induced transients, switching surges, or capacitor-bank switching events, and all of those are common on distribution transformer secondaries. A permanent pole-mount deployment requires: a surge protective device (SPD) rated for the site's overvoltage category (Category C / lightning-level, per IEC 61643-11 or UL 1449) installed on the Line/Neutral entry ahead of the fuse; enclosure grounding and bonding to the pole ground system per applicable NEC sections and utility rules; verified creepage and clearance in all mains-voltage wiring and connectors; UV and temperature qualification of all external cabling and the enclosure; and written utility approval before the secondary tap is permanently energized. This POC leaves out the SPD and grounding circuit as out of scope for a bench or controlled field-trial context, but do not omit them in any design intended for permanent field deployment.

Production Next Steps

Getting from a controlled trial to permanently energized pole-mount units across a whole fleet takes the additions below.

Surge and grounding protection is mandatory before permanent field deployment. That means a surge protective device (SPD) on the AC mains entry (Category C rated, per IEC 61643-11 or UL 1449), enclosure grounding/bonding to the pole ground system per applicable NEC sections and utility rules, and written utility approval.

Higher-range CTs (roughly 250A to 800A) cover 50–167 kVA distribution transformers. Update CT_TURNS_RATIO and CT_BURDEN_OHMS to match.

A contact temperature probe on the transformer case allows direct winding-temperature monitoring. Options include a PT100 RTD through a MAX31865 (SPI, which needs three additional GPIO pins for SCLK/MISO/CS) or a thermocouple with a MAX31856 (also SPI). An I²C digital sensor such as the Texas Instruments TMP117 (±0.1°C from –20°C to 50°C, rated –55°C to 150°C) avoids the extra SPI pins and covers typical transformer case temperatures.

Voltage measurement on the secondary (a resistor divider plus a precision analog input or an external ADC) would let the device compute real power, power factor, and true apparent kVA loading.

Inductive energy harvesting from a current-carrying secondary conductor is an alternative to the 120VAC tap, and it's valuable for sealed enclosures where the tap isn't accessible.

A Blues Scoop with LiPo makes last-gasp outage reporting possible when the primary supply fails.

Over-the-air host firmware updates across the deployed fleet matter a great deal when physically re-flashing hundreds of pole-mount units isn't practical. Notecard Outboard Firmware Update (ODFU) provides this capability. Check the specific wiring and bootloader support for the Notecarrier CX's STM32L433 host against Blues documentation before you commit to a production design.

Per-transformer commissioning would add a one-time rated_amps calibration through an env var at install time, based on a field measurement of the actual secondary current at a known load.

12. Summary

The pole-top transformer that used to stay invisible until Friday's peak demand took down the neighborhood now reports for itself every hour. Three split-core CTs clamped on the secondary leads and one I²C temperature sensor give the operations center the three signals that matter most for transformer health: per-phase loading, phase imbalance, and thermal stress. Hourly summaries feed capacity planning, and an xfmr_alert.qo Note goes out immediately when a phase climbs past 95% of rated current or the enclosure runs hot.

One SKU, one SIM, and one firmware image work on a dense urban feeder and on a rural single-wire run alike. That's what lets a pilot that works on paper grow into a fleet program that actually scales. The dispatcher hears about trouble before the streetlights go out, and the planner gets a year of loading history without sending a single truck out to read it.

On This Page

  • 1. Project Overview
  • 2. System Architecture
  • 3. Technical Summary
  • 4. Hardware Requirements
  • 5. Wiring and Assembly
    • Bias circuit (build once per CT channel, identical for A, B, and C)
    • Pin-by-pin connections
    • Cellular antenna
    • 3.5mm TRRS jack wiring
    • CT installation
  • 6. Notehub Setup
    • What you should see in Notehub
  • 7. Firmware Design
    • 7.1 Installing and flashing
    • 7.2 Firmware constants and environment variables
    • 7.3 Modules
    • 7.4 Sensor reading strategy
    • 7.5 Event payload design
    • 7.6 Low-power strategy
    • 7.7 Retry and error handling
    • 7.8 Key code snippet 1: Note template definition
    • 7.9 Key code snippet 2: CT RMS two-pass algorithm
    • 7.10 Key code snippet 3: immediate-sync alert
    • 7.11 Key code snippet 4: sleeping until the Notecard raises ATTN
  • 8. Data Flow
  • 9. Validation and Testing
  • 10. Troubleshooting
  • 11. Limitations and Next Steps
    • Simplified for this POC
    • Production Next Steps
  • 12. Summary

Resources

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