Commercial Plug-Load & After-Hours Waste Dashboard

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/plug_load_monitor/ so it can learn how the device works and help you adapt it to your hardware.
This project is a cellular-connected energy savings monitor for commercial buildings. Non-invasive CT (current transformer) clamps clip onto branch circuits in a sub-panel, the device samples RMS current once a minute, and hourly per-circuit profiles go up to the Blues Notehub cloud service. Energy consultants get to see which circuits are burning money at 2 AM, and the device never touches the building's corporate network.
1. Project Overview
The problem. In most commercial buildings, the HVAC system is heavily instrumented and often metered. Plug loads are usually invisible. Those are the miscellaneous branch circuits feeding workstations, monitors, task lighting, small appliances, AV equipment, server closets, and vending machines. A typical mid-size office building has 50 to 200 branch circuits on its sub-panels, and a good share of them draw load around the clock when they shouldn't. Think of workstations nobody ever shuts down, vending machines that never sleep, building signage that runs from midnight to 5 AM, and network closets cooling equipment that was decommissioned two years ago but never unplugged.
An ESCO (Energy Service Company) or an independent energy consultant is usually the right person to find this waste and put a number on it. They're hired to audit the building and find savings the owner can't see on their own. The catch is that ESCOs often can't deploy anything. Corporate IT treats unmanaged IoT devices on the production WiFi as a security risk, and getting a new device approved and connected can take weeks or months. By the time the network form clears, the engagement has moved on or lost its momentum. So the ESCO ends up estimating plug loads from utility bills and walkthrough surveys instead of measuring them directly.
Why Notecard. Cellular skips the IT approval loop completely. The Notecard
Cell+WiFi connects to the public cellular network instead of the building LAN,
so it needs no credentials, VLAN, firewall exception, or IT ticket. In practice,
the energy consultant shows up, clips CT clamps around four branch-circuit hot
legs, and powers up the device. Sampling starts on the very first 60-second
wake. The first circuit_summary.qo Note (effectively a JSON payload) lands in
Notehub on the next hourly outbound sync, so you should see data within the
first hour of installation.
If you want to see data sooner during commissioning, set
report_interval_min=5 in Notehub before first power-up. Env vars
pre-provisioned in Notehub reach the Notecard's local cache on the first
successful cellular sync, and the firmware applies them on the next host wake
after that. Once the device is powered and running, though, the firmware
configures inbound:360 (a 6-hour cadence), so a value you change in Notehub
after boot can take up to 6 hours to reach the device. Notehub-terminal
commands have the same limit. They're queued for the Notecard's next scheduled
inbound window and can't wake a sleeping periodic device on demand. Still,
nobody fills out a network form, and nothing depends on whoever manages the
building's IT. That's the job cellular-first IoT was designed for: getting
sensors into places where a normal network connection would take longer to
provision than the whole project.
New to Blues?
Learn what we do in this 3-minute video that walks you through how we simplify wireless connectivity.
Learn what we do in this 3-minute video that walks you through how we simplify wireless connectivity.
Notecard Cell+WiFi also keeps WiFi as a fallback. A site that does offer accessible WiFi can use it, and sites that don't still get the cellular-first deployment model.
Deployment scenario. A small weatherproof enclosure mounts inside or next to the target sub-panel. CT clamps clip around individual branch-circuit hot wires at the sub-panel, so nobody cuts a wire or interrupts a circuit. The enclosure draws 5V DC from a compact AC/DC supply tapped at the panel feed. All panel work, including placing CT clamps inside the panel enclosure and landing the AC/DC supply on panel power, must be done by a qualified person following site lockout/tagout procedures and applicable electrical codes. Once the enclosure is powered and in place, the CT clamps clip on and off the circuit conductors without any further contact with live conductors. The initial installation still isn't a DIY job. A typical engagement puts one unit on each sub-panel and leaves it there for two to four weeks. That captures enough time-of-day variation for the cloud classifier to build a reliable load profile for each circuit.
2. System Architecture
Device-side responsibilities. Inside the enclosure next to the sub-panel,
the Notecarrier CX's STM32L433 host wakes once a minute when the Notecard raises
its ATTN pin (armed with
card.attn). It reads
RMS current on each installed CT channel (A0–A3) and rolls the readings into
per-circuit mean, peak, and active-minute accumulators. When a reporting window
closes, the host hands a circuit_summary.qo Note to the Notecard. Then it
drops into STM32 STOP2 (a few µA, RAM intact) until the next wake, and it spends
more than 99% of the day asleep.
Notecard responsibilities. The Notecard holds whatever the host queues in
its on-device Note queue until the
hub.set outbound
cadence (60 minutes by default) opens a cellular session and flushes everything
at once. WiFi is there as an opportunistic fallback for sites that happen to
allow it. On every inbound sync the Notecard also pulls environment
variables,
so you can tune channel count, sampling cadence, idle thresholds, and (if
enabled) business-hours configuration in the field without a firmware update.
Notehub responsibilities. Events travel over the embedded global SIM to
Blues Notehub, which ingests and stores every one and
applies the project's routes. The hourly circuit_summary.qo records flow on to
whatever time-series database or analytics platform the ESCO's dashboard runs
on. One firmware image covers every building in the engagement, and Smart
Fleets handle the
per-site differences (time zone, business hours, CT model), so you don't need
per-site firmware variants.
Routing to the cloud (high level). Notehub can route to HTTP, MQTT, AWS,
Azure, GCP, Snowflake, and other destinations. Route setup depends on your
project, so see the Notehub routing
docs. The natural
home for circuit_summary.qo is a time-series database or analytics platform
that handles load-profile classification and dashboarding. Those downstream
integrations are project-specific and outside the scope of this reference
design.
3. Technical Summary
When you've finished this guide and deployed the firmware, you'll have:
- A Notecarrier CX with an integrated STM32 host running the plug-load monitor sketch and sampling four branch circuits every 60 seconds.
- One
circuit_summary.qoNote per device arriving in Notehub every hour, with per-circuit mean RMS amps, peak RMS amps, and active-minutes (sample JSON below). - Optionally, after-hours
circuit_alert.qoNotes (real-time,sync:true) if you uncommentPLUG_LOAD_ALERTSin the firmware. - Environment variables you can edit in the Notehub Fleet UI to adjust thresholds and timing on running devices, with no firmware re-flash.
Example circuit_summary.qo from Notehub:
{
"file": "circuit_summary.qo",
"when": 1704067200,
"body": {
"ch1_mean": 8.4,
"ch1_peak": 14.1,
"ch1_act_min": 58.0,
"ch2_mean": 0.1,
"ch2_peak": 0.3,
"ch2_act_min": 0.0,
"ch3_mean": 12.7,
"ch3_peak": 15.9,
"ch3_act_min": 60.0,
"ch4_mean": -9999.0,
"ch4_peak": -9999.0,
"ch4_act_min": -9999.0,
"samples": 60
}
}All values are RMS amps except samples (a count) and act_min (minutes above
the idle threshold). A field equal to -9999.0 means that channel is above
circuit_count, so the firmware didn't read it.
4. Hardware Requirements
| Part | Qty | Rationale |
|---|---|---|
| Notecarrier CX | 1 | Integrated carrier with an onboard STM32L433 host, so you don't need a separate MCU. It exposes six analog inputs (A0–A5) on its dual 16-pin headers, and this design uses four of them for CT channels. The host sleeps in STOP2 between samples until the Notecard's ATTN pin wakes it. |
| Notecard Cell+WiFi (MBGLW) (datasheet) | 1 | Cellular-first connectivity means the device doesn't depend on site WiFi or the LAN. The onboard WiFi radio is there as an opportunistic fallback. Ships with an active global SIM that includes 500 MB and 10 years of service. |
| External cellular antenna, SMA, ~1 m lead (e.g. SparkFun CEL-16432) | 1 | Required whenever the enclosure mounts inside or next to a metal sub-panel, because a chip or flexible antenna left inside metalwork won't hold a reliable cellular connection. The SMA connector screws onto a bulkhead fitting that passes through a cable gland, so the antenna element sits outside the metal. |
| u.FL to SMA-female bulkhead pigtail, ~10–15 cm | 1 | Connects the Notecard's MAIN u.FL cellular port to the SMA bulkhead fitting on the enclosure wall. Keep it as short as you can to cut cable loss. |
| Blues Mojo | 1 | Coulomb counter for power validation on the bench. It confirms the sleep/wake pattern behaves as designed before you deploy to the field. |
| YHDC SCT-013-030 split-core CT, 30 A / 1 V | 4 | Non-invasive clamp-on current sensing that clips around the hot wire without breaking the circuit. The -030 (1 V) variant has an internal burden resistor, so you don't need external burden components. The 30 A rating covers standard 15 A and 20 A branch circuits with headroom, and the ct_full_scale_amps env var re-scales the reading if you use a different CT model. |
| TRRS 3.5 mm breakout (e.g. SparkFun BOB-11570) | 4 | The SCT-013-030 output cable ends in a 3.5 mm three-conductor plug. One breakout per CT brings the tip (signal) and sleeve (return) out to screw terminals. |
| 10 kΩ 1% resistor (bias divider) | 2 | A two-resistor voltage divider between +3V3 and GND sets the shared bias mid-point (≈1.65 V) that all four CT channels reference. Taking the bias from the on-board 3.3 V keeps the signal centered in the ADC input range. |
| 10 µF electrolytic capacitor | 1 | Decouples the shared bias node. Without it, divider noise would add a low-frequency artifact to every RMS measurement. |
| AC/DC supply, 5 V / 2 A (e.g. MeanWell IRM-10-5) | 1 | Turns the 120 VAC panel feed into 5 V DC for permanent, grid-tied power. A 2 A (10 W) rating suits a cellular design. The MBGLW's LTE Cat-1 bis modem (Quectel EG916Q-GL) averages ~250 mA during a session, and modem attach plus brief 2G/GSM-fallback transmit bursts in marginal-coverage areas can pull well above that from the 5 V rail during radio warm-up. A supply too small to source those peaks can brown out and abort the cellular session. The IRM-10-5's 10 W output leaves enough headroom for the Notecard's peak draw, the host MCU's active phase, and some margin for the installation environment. |
| NEMA 4X enclosure, ~6×4×2″ | 1 | Houses the electronics near the sub-panel. The NEMA 4X rating handles the occasional humidity and spray in mechanical rooms and utility corridors. |
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.
5. Wiring and Assembly
All host I/O goes to the Notecarrier
CX dual 16-pin header,
and the Notecard Cell+WiFi seats in the carrier's M.2 slot. Mojo sits inline
between the 5 V supply output and the Notecarrier's +VBAT pad.
Antenna placement. The Notecard Cell+WiFi's cellular port is a u.FL
connector (MAIN). For any installation inside or next to a metal sub-panel or
enclosure, connect a short u.FL-to-SMA-female bulkhead pigtail to the Notecard's
MAIN u.FL port. Thread the SMA bulkhead fitting through a cable gland in the
enclosure wall, then screw the external cellular antenna onto the SMA fitting
outside the enclosure. A chip or flexible antenna left inside a metal enclosure
won't keep a reliable cellular link. Metalwork blocks the onboard WiFi chip
antenna too, so if WiFi fallback matters at a given site, run a second u.FL
pigtail from the Notecard's WiFi port to a second external antenna using the
same cable-gland approach.
Safety. Sub-panels contain hazardous voltages. CT installation must be done by a qualified person following site lockout/tagout procedures and applicable electrical codes. The CT clamps themselves are non-invasive, so you don't have to break or de-energize the circuit you're monitoring. The AC/DC supply wiring does connect to live conductors, so always de-energize the panel before making line-voltage connections.
Shared bias circuit (build this once for all four channels):
- Connect +3V3 (Notecarrier CX header) → 10 kΩ R1 → BIAS NODE.
- Connect BIAS NODE → 10 kΩ R2 → GND.
- Connect the 10 µF capacitor from BIAS NODE to GND (electrolytic, positive lead to BIAS NODE).
- The BIAS NODE voltage sits at about 1.65 V (half of 3.3 V), and it's the common return reference for all four CT channels.
Per-channel CT connection (repeat for each CT):
- TRRS breakout TIP terminal → Notecarrier CX analog pin (A0 for ch1, A1 for ch2, A2 for ch3, A3 for ch4).
- TRRS breakout SLEEVE terminal → BIAS NODE (the shared mid-point from above).
- Plug the SCT-013-030's 3.5 mm cable into the breakout's jack.
- Clip the CT clamp around one hot leg of the branch circuit you want to monitor. Clamping both legs of a single-phase 240 V circuit cancels the measurement, so monitor only one leg per circuit.
ATTN-to-D5 jumper (host sleep):
Fit a short jumper between the ATTN and D5 pins on the Notecarrier CX
header. Both are on the same 16-pin header (ATTN at position 3, D5 at
position 14). The Notecard pulls ATTN low while the host sleeps and raises it
when the next sample is due, and the rising edge on D5 wakes the host from
STOP2. Leave the Notecarrier CX EN pin unconnected. On the CX it enables the
shared 3.3 V VIO rail (host, Qwiic, 3V3_OUT, and Notecard I/O), so driving
it from ATTN would brown out the whole board instead of putting the host to
sleep. Putting a Host to Sleep Between Sensor
Readings
walks through the full pattern.
Power and Mojo:
- 120 VAC feed from panel → MeanWell IRM-10-5 → 5 V DC output.
- Mojo
BATinput ← 5 V DC output. - Mojo
LOADoutput → Notecarrier CX +VBAT pad. - Mojo Qwiic connector → Notecarrier CX Qwiic port (reports cumulative mAh to the Notecard for bench validation).
For firmware flashing, set the Notecarrier CX DIP switch to HST, which routes
the USB-C port to the onboard STM32 host (its DFU bootloader). Flip it to NC
to route USB-C to the Notecard for direct API testing. Debug output doesn't use
USB at all. It goes to the LPUART on the debug jack through an ST-LINK (see
§7).
6. Notehub Setup
-
Create a project. Follow the Notecard quickstart if you haven't already.
-
Claim the Notecard and create a Fleet. The Notecard associates itself with your project on its first cellular session. Create a Fleet for each building site (one fleet = one sub-panel deployment location). Smart Fleets let you set per-site environment variables without maintaining per-device firmware variants.
-
Pre-provision environment variables (recommended for commissioning). Go to Projects → [Your Project] → Fleets → [Your Fleet] → Environment and add any non-default variables before you power up the device for the first time. That way they reach the Notecard's local cache on the first successful cellular sync. Every variable below is optional, and the tables show the firmware defaults.
Sync timing: The firmware configures
inbound:360, so the Notecard checks Notehub for updated env vars every 6 hours. If you change a variable in Notehub after the device is running, it won't take effect until that next inbound sync. To force an immediate pickup on a running device, connect a USB cable on-site, flip the Notecarrier CX DIP switch toNC(which routes USB serial straight to the Notecard), and send{"req":"hub.sync"}over the serial connection. That triggers an immediate bidirectional session.Variable Default Purpose sample_interval_sec60Seconds between CT readings. Lowering it improves time resolution but costs more host processor energy per hour. Summary transmission volume doesn't change (one Note per report_interval_minno matter the sample rate).report_interval_min60Minutes between circuit_summary.qoNotes. Also updates the Notecard'shub.setoutbound interval so the two stay in sync.circuit_count4Number of CT clamps installed (1–4). Channels above this count aren't read and show up as -9999(no data) in summary Notes. If you reducecircuit_countpartway through a summary window, samples already collected for the now-disabled channels still go out in the next summary. The change takes full effect at the window boundary after that.idle_threshold_amps0.50RMS amps below which a circuit counts as "off" for active-minute counting. Raise it if a circuit's leakage floor produces false activity counts. ct_full_scale_amps30.0Full-scale primary current of the installed CT model. Override it if you use a different CT (e.g. 20.0for a 20 A model).The firmware only reads the next set of variables when
PLUG_LOAD_ALERTSis defined infirmware/plug_load_monitor/plug_load_monitor_helpers.h(see §7). In the default build they do nothing.Variable Default Purpose ( PLUG_LOAD_ALERTSbuilds only)after_hours_threshold_amps2.0RMS amps above which an after-hours alert fires on a circuit that should be off. biz_hours_start8Local hour (0–23, inclusive) when business hours begin. After-hours detection is inactive from this hour until biz_hours_end. It must be lower thanbiz_hours_end, or the firmware ignores both values.biz_hours_end18Local hour (0–23, exclusive) when business hours end. tz_offset_hours0Whole-integer hour offset from UTC, used to convert Notecard timestamps to local time for business-hours evaluation. Use -5for US Eastern Standard Time and-4for Eastern Daylight Time. The firmware stores this value asint8_tand does integer arithmetic, so it doesn't support sites in half-hour (e.g. India IST UTC+5:30) or 45-minute (e.g. Nepal UTC+5:45) offset zones. Business-hours windows will be evaluated incorrectly at those sites.alert_cooldown_min60Minimum minutes between repeat after-hours alerts on the same circuit. Keeps a circuit with a sustained load all night from burying you in alerts. -
Configure data routes (Projects → Data & Routing). Add a route for
circuit_summary.qothat forwards the batched hourly records to a time-series database or analytics platform for load-profile classification. If you build withPLUG_LOAD_ALERTSenabled, add a second route forcircuit_alert.qo. Those records are low-volume and time-sensitive, so send them to an email, Slack, or CMMS destination.
7. Firmware Design
The firmware is four files in firmware/:
| File | Role |
|---|---|
plug_load_monitor.ino | Arduino sketch with setup() and loop(), global variable definitions, and sleep/wake orchestration |
plug_load_monitor_helpers.h | Shared constants, types, extern declarations, function prototypes, and feature flags (PLUG_LOAD_DEBUG, PLUG_LOAD_ALERTS) |
plug_load_monitor_helpers.cpp | All helper implementations: hub config, template registration, env-var fetch, CT measurement, and Note emission |
cx_sleep.h | Host sleep: card.attn request, STOP2 entry, and ATTN wake |
Dependencies:
- Arduino core for STM32
(
stm32duino/Arduino_Core_STM32). Blues Wireless Notecard(note-arduinolibrary). Install it from the Arduino Library Manager (arduino-cli lib install "Blues Wireless Notecard"), and check the note-arduino releases page for the latest version.STM32duino Low Powerand its dependencySTM32duino RTC, both from the Library Manager. This library 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 select Blues boards → Cygnet (pnum=CYGNET) and set USB
support (if available) to None (usb=none). If the USB CDC stack is
enabled and no USB host is attached (the Notecarrier CX USB-C switch in NC, or
no cable at all), the STM32's USB wakeup interrupt fires continuously and the
host never stays in STOP2. Debug output (when PLUG_LOAD_DEBUG is defined in
plug_load_monitor_helpers.h) goes to the LPUART on the Notecarrier CX debug
jack instead, which an ST-LINK V3 exposes as a virtual COM port (see Serial
Logging With STLINK).
Optional alert extension. The PLUG_LOAD_ALERTS flag in
plug_load_monitor_helpers.h is commented out by default. Uncommenting it adds
the circuit_alert.qo Notefile, along with a note.template registration, a
card.time call on each wake, business-hours evaluation, and an immediate
sync:true alert Note when a circuit exceeds after_hours_threshold_amps
outside business hours. The baseline build (flag undefined) compiles none of
this code. It makes no extra I²C calls, has no second Notefile, and never opens
an unscheduled cellular session.
Modules
| Responsibility | Where |
|---|---|
Notecard hub configuration (hub.set, outbound cadence) | hubConfigure |
Note template registration (circuit_summary.qo) | defineTemplates |
Accelerometer quiesce at power-up (card.motion.mode) | setup() |
Env-variable fetch and local config update (every wake, in loop()) | fetchEnvOverrides |
| Per-channel RMS current measurement | readChannelAmpsRMS |
| Per-cycle accumulation and summary trigger | runSampleCycle |
| Summary Note emission and window reset | sendSummary |
Host sleep (STOP2 until the Notecard raises ATTN) | cx_sleep.h: cxSleepBegin (in setup), cxSleepUntilAttn (in loop) |
| Application state (held in RAM across sleep cycles) | AppState |
After-hours time-of-day check (requires PLUG_LOAD_ALERTS) | isAfterHours |
Immediate after-hours alert emission (requires PLUG_LOAD_ALERTS) | sendAlert |
Sensor reading strategy
Each CT channel uses a two-pass ADC approach taken straight from the Open Energy
Monitor technique. In the first pass, the firmware averages CT_BIAS_SAMPLES
(256) readings to measure the bias network's actual DC offset. The shared
voltage divider is nominally 1.65 V, but real resistor tolerances make a
measured offset more accurate than an assumed one. In the second pass, it takes
CT_RMS_SAMPLES (1480) readings, subtracts the DC offset, squares and sums
them, then divides and takes the square root to get an RMS count value. That
count converts to volts at 3.3 V / 4095 counts and then scales to amps at 30 A
per 1 V RMS (the SCT-013-030's rated output).
The 1480-sample RMS window was chosen empirically. Its actual elapsed time and mains-cycle coverage depend on the platform's ADC conversion timing, which varies across STM32 Arduino core configurations and clock speeds. In practice the window is long enough for a stable single-sample RMS estimate, but bench-validate the exact cycle coverage on your target hardware build instead of assuming it from the sample count.
setup() turns on the STM32L433's 12-bit ADC explicitly with
analogReadResolution(12). Without that call, the Arduino STM32 core defaults
to 10-bit, which would cut current-sensing resolution by 4×.
Event payload design
The firmware defines two template-backed Notefiles. Templates store records as fixed-length binary instead of free-form JSON, which shrinks on-wire size roughly 3–5×. That matters for a device sending 24 summary Notes per panel per day over a multi-month engagement.
circuit_summary.qo (hourly):
{
"file": "circuit_summary.qo",
"body": {
"ch1_mean": 8.4,
"ch1_peak": 14.1,
"ch1_act_min": 58.0,
"ch2_mean": 0.1,
"ch2_peak": 0.3,
"ch2_act_min": 0.0,
"ch3_mean": 12.7,
"ch3_peak": 15.9,
"ch3_act_min": 60.0,
"ch4_mean": -9999.0,
"ch4_peak": -9999.0,
"ch4_act_min": -9999.0,
"samples": 60
}
}Here all three ch4 fields carry -9999.0 (the INVALID_SENTINEL) because
circuit_count is set to 3 for this deployment, with only three CT clamps
installed. When n_arms[ch] == 0 (no valid samples for that channel during the
window), the firmware emits INVALID_SENTINEL for chN_mean, chN_peak,
and chN_act_min. Downstream consumers must treat any field equal to
-9999.0 as "no data," not as a zero-amp or zero-minute reading.
act_min comes from one-minute snapshots, not from measuring continuous
waveform occupancy. Each sample whose RMS current meets or exceeds
idle_threshold_amps adds sample_interval_sec seconds (60 seconds by
default) to the active total, and that total is divided by 60 when the Note is
emitted. A circuit that's active for only part of a sample interval scores the
same as one that's active for the whole interval.
circuit_alert.qo (immediate, sync:true, requires PLUG_LOAD_ALERTS):
Define PLUG_LOAD_ALERTS in plug_load_monitor_helpers.h to turn on the alert
extension, and the firmware also emits a second Notefile for real-time
notification. The template registration, the sendAlert() function,
notecardEpoch(), isAfterHours(), and the alert-gating block in
runSampleCycle() all sit behind #ifdef PLUG_LOAD_ALERTS in
plug_load_monitor_helpers.cpp. The core summary stream is the same whether or
not the alert extension is compiled in.
{
"file": "circuit_alert.qo",
"body": {
"circuit": 3,
"arms": 12.7,
"alert_type": "after_hours_load",
"hour_local": 2
}
}The hour_local field carries the firmware's local-time computation (UTC plus
tz_offset_hours), so anyone reading the alert in Notehub can make sense of it
without knowing the site's time zone configuration.
Low-power strategy
The enclosure is grid-tied to 120 VAC, but the host still sleeps deeply between
samples. setup() runs once at power-up to configure the Notecard, and each
pass through loop() is one sample cycle. After the cycle, the host 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 enters STM32 STOP2, where the host draws a few microamps and keeps its
RAM intact. When the interval is up, the Notecard raises ATTN. That rising
edge on D5 wakes the host and execution picks up on the next line of
loop(), so the AppState struct just lives in RAM and nothing gets
serialized to the Notecard. Between cellular sessions, the Notecard sits in its
own low-power idle
state (~8–18 µA @ 5
V).
Sampling and transmitting are decoupled on purpose. The host wakes every 60
seconds, but the Notecard only opens a cellular session once an hour. With the
PLUG_LOAD_ALERTS extension enabled, alert Notes carry sync:true and skip
the hourly window, so a 2 AM anomaly pages someone right away instead of
waiting for the next scheduled session. The default build doesn't do this.
If the sleep request fails or ATTN never goes low (the ATTN→D5 jumper is
missing), cxSleepUntilAttn() returns false and loop() keeps the sample
cadence itself. It stays awake for sample_interval_sec seconds, then re-reads
env vars and calls runSampleCycle() before it tries to sleep again. The
accumulation, alert, and summary logic are identical on both paths.
Sleeping the host between samples also keeps the design portable. If a site ever needs it, the same firmware runs unchanged from a small backup battery or a solar cell.
Retry and error handling
A handful of deliberate choices keep the firmware working through the failures that show up in real field deployments. Each one guards against a specific failure mode, and they interlock, so it helps to understand them together.
Distinguishing a zero-load circuit from a sensor fault, and why this design
doesn't. readChannelAmpsRMS clamps its return value to zero if the computed
value is negative. That's a defensive guard, since the sqrt-based computation
won't naturally produce a negative result under normal ADC conditions. It helps
to know what a near-zero reading does and doesn't tell you. A CT that's
plugged in but clamped around a conductor carrying near-zero current reads near
zero. The CT output shorts tip to sleeve at the bias potential, so the
bias-subtracted RMS really is small. A CT cable that's unplugged is a
different story, because it leaves the analog input floating. With no drive
path to the bias node, the pin can pick up arbitrary levels through stray 60 Hz
capacitive coupling from the panel environment and produce large or erratic ADC
readings, not a predictable near-zero floor. This firmware has no explicit
per-channel fault-detection mechanism, so it doesn't tell open-input and
zero-load conditions apart. Treat that as a known limitation, not a guarantee
that every channel is healthy.
The INVALID_SENTINEL value is not a sensor-fault flag. Summary Notes
carry the INVALID_SENTINEL (-9999.0) value only when n_arms[ch] == 0 at
summary time. Every configured channel gets read on every wake cycle, so in
practice that means a channel above circuit_count, which the firmware never
reads. Downstream consumers shouldn't treat it as a per-sample sensor-fault
indicator.
Retaining last-known-good configuration through transient I²C failures.
When fetchEnvOverrides succeeds, the firmware copies the effective
configuration into AppState.saved_cfg as the last-known-good snapshot. The
CFG_* globals themselves stay in RAM across STOP2 sleeps, so a transient I²C
failure keeps the last known-good values instead of silently reverting to
compile-time defaults for that cycle. On a cold boot, env.get returns an
empty body because the Notecard's local cache isn't populated yet, so the device
runs on compile-time defaults for that first wake.
This has a timing consequence. Env vars you pre-provision in Notehub before
power-up reach the Notecard's local cache during the first successful cellular
sync. fetchEnvOverrides picks them up on the next host wake after that sync,
and if report_interval_min changed, hubConfigure re-applies the new outbound
cadence right away. However, hubConfigure sets inbound:360, so the
Notecard only pulls fresh env vars from Notehub every 6 hours. If you change
an env var in Notehub while the device is running, it will not show up on the
device until that next inbound sync fires. The next 60-second host wake won't
pick it up. To push a change sooner, connect a USB cable on-site, flip the
Notecarrier CX DIP switch to NC, and send {"req":"hub.sync"} directly over
the serial connection. Notehub-terminal commands are subject to the same
inbound:360 delivery window, and they can't trigger an immediate sync on a
sleeping periodic device.
Idempotent template definition survives card resets and swaps. The firmware
calls defineTemplates unconditionally on every wake. note.template is
idempotent, so re-issuing it on an intact Notecard does nothing. After a
Notecard factory reset or card replacement, though, re-issuing it restores the
fixed-schema binary encoding before any note.add calls reach the Notecard,
which closes the window where Notes could queue against a missing template.
Template-confirmation flags are tracked per Notefile
(g_summary_template_applied, plus g_alert_template_applied in
PLUG_LOAD_ALERTS builds), so a transient I²C failure registering one template
doesn't block emission on the other Notefile. The firmware deliberately does
not trust either flag across wakes. defineTemplates re-issues the templates
every wake regardless, because a host-side boolean can't reliably reflect
Notecard state across a card reset or swap.
Alert cooldowns persist across sleep. Per-channel alert cooldowns
(alert_last_unix[]) live in the RAM-resident AppState struct, which STOP2
retains across sleep cycles. That means a circuit that fires an alert at 23:55
can't fire again until alert_cooldown_min has elapsed, no matter how many
sleep boundaries fall in between.
Key code snippet 1: template definition
The template gives the Notecard the fixed schema for summary records. 14.1
encodes a 4-byte IEEE 754 float, and 12 encodes a 2-byte signed integer. Every
field name here has to match the note.add body fields in sendSummary
exactly. The elided lines repeat the three fields for ch2 through ch4, and
the result lands in g_summary_template_applied, which sendSummary() checks
before it adds a Note.
J *req = notecard.newRequest("note.template");
JAddStringToObject(req, "file", "circuit_summary.qo");
JAddNumberToObject(req, "port", 50);
J *body = JAddObjectToObject(req, "body");
JAddNumberToObject(body, "ch1_mean", 14.1);
JAddNumberToObject(body, "ch1_peak", 14.1);
JAddNumberToObject(body, "ch1_act_min", 14.1);
// ...
JAddNumberToObject(body, "samples", 12);
g_summary_template_applied = notecard.sendRequest(req);Key code snippet 2: immediate-sync alert (PLUG_LOAD_ALERTS builds only)
sync:true tells the Notecard to open a session right away instead of waiting
for the next scheduled outbound window. With PLUG_LOAD_ALERTS defined, an
after-hours alert Note reaches Notehub about as fast as a session can be
established (~15–60 seconds) after the threshold trips. The full implementation
lives in plug_load_monitor_helpers.cpp inside #ifdef PLUG_LOAD_ALERTS
guards, in sendAlert() and the alert-gating block inside runSampleCycle().
That block passes "after_hours_load" as alert_type and only starts the
channel's cooldown when sendAlert() returns true.
J *req = notecard.newRequest("note.add");
JAddStringToObject(req, "file", "circuit_alert.qo");
JAddBoolToObject(req, "sync", true); // bypass outbound timer — alert now
J *body = JAddObjectToObject(req, "body");
JAddNumberToObject(body, "circuit", (int)circuit_1based);
JAddNumberToObject(body, "arms", arms);
JAddStringToObject(body, "alert_type", alert_type);
JAddNumberToObject(body, "hour_local", hour_local);
return notecard.sendRequest(req);Key code snippet 3: two-pass RMS measurement
In readChannelAmpsRMS(), the first loop measures the DC bias offset, and the
second computes the RMS of the AC component around it:
static float readChannelAmpsRMS(uint8_t pin) {
uint32_t acc = 0;
for (uint16_t i = 0; i < CT_BIAS_SAMPLES; i++) acc += analogRead(pin);
int32_t dc_offset = (int32_t)(acc / CT_BIAS_SAMPLES);
uint64_t sum_sq = 0;
for (uint16_t i = 0; i < CT_RMS_SAMPLES; i++) {
int32_t s = (int32_t)analogRead(pin) - dc_offset;
sum_sq += (uint64_t)((int64_t)s * s);
}
float rms_counts = sqrtf((float)sum_sq / (float)CT_RMS_SAMPLES);
float rms_v = rms_counts * ADC_VREF_V / (float)ADC_COUNTS;
float arms = rms_v * (CFG_CT_FULL_SCALE_AMPS / CT_VOUT_AT_FULL_SCALE);
return (arms < 0.0f) ? 0.0f : arms;
}Key code snippet 4: sleeping until the Notecard raises ATTN
void setup() {
// ...
// Wake from STOP2 on the ATTN rising edge.
cxSleepBegin();
}
void loop() {
// ...
runSampleCycle();
state.cycles++;
// ...
if (!cxSleepUntilAttn(notecard, CFG_SAMPLE_INTERVAL_SEC, NULL, log)) {
// The Notecard didn't take the sleep request, or ATTN never went low
// (check the ATTN -> D5 jumper). Keep the sample cadence and try again.
// ...
delay(CFG_SAMPLE_INTERVAL_SEC * 1000UL);
}
}cxSleepUntilAttn() asks the Notecard to hold ATTN low for
CFG_SAMPLE_INTERVAL_SEC seconds, confirms the pin has gone low, and puts the
STM32 into STOP2. Because the host stays alive, the firmware checks the result
of the card.attn call instead of treating it as fire-and-forget. The
Notecard's ATTN pin brings the host back on schedule, and loop() moves on to
the next sample. AppState is still in RAM, so nothing needs rehydrating.
8. Build and Flash
-
Install the Arduino STM32 core and libraries:
arduino-cli core install STMicroelectronics:stm32 arduino-cli lib install "Blues Wireless Notecard" "STM32duino Low Power" "STM32duino RTC" -
Set your Notehub ProductUID. Sign up at notehub.io and create a project. Copy the ProductUID and paste it into
firmware/plug_load_monitor/plug_load_monitor_helpers.hasPRODUCT_UID. (The define lives in the shared header so both the.inoand.cpptranslation units can see it.) -
Compile and flash. Connect the Notecarrier CX over USB-C and set the DIP switch to
HST, which routes the USB-C port to the host MCU's DFU bootloader. Then flash with:arduino-cli compile -b STMicroelectronics:stm32:Blues:pnum=CYGNET,usb=none firmware/plug_load_monitor/ arduino-cli upload -b STMicroelectronics:stm32:Blues:pnum=CYGNET,usb=none -p /dev/ttyACM0 firmware/plug_load_monitor/The
usb=noneboard option disables the USB CDC stack, which has to be off for the host to sleep (see §7).firmware/plug_load_monitor/sketch.yamlsets the same board options, so runningarduino-cli compilein that directory without-bpicks them up too. Follow the stm32duino upload instructions for your operating system (DFU upload may need manual bootloader entry: holdBOOT, tapRST), or upload over SWD with an ST-LINK on the debug jack. You can also use the Arduino IDE. Open the sketch, apply the build settings from §7 (board Blues boards → Cygnet, USB support None), then compile and upload.
9. Data Flow
Every 60 seconds the host wakes up, reads all active CT channels, and checks whether the hourly summary window has elapsed.
- Collected (per wake, per channel). Single-sample RMS amps. Each sample goes straight into the rolling window and is never transmitted on its own.
- Transmitted.
circuit_summary.qo: one record everyreport_interval_min(60 minutes by default, so 24 records/day/device). Each record carries mean RMS amps, peak RMS amps, and active-minutes for each of the four channels, plus the total sample count. Active-minutes (act_min) is a thresholded sample-count estimate. Each one-minute snapshot aboveidle_threshold_ampsadds one minute, and none of it comes from continuous waveform analysis (see §7 for the precise computation). The Notecard's automatic UTC timestamp lets the downstream system do time-of-day analysis.circuit_alert.qo: only present whenPLUG_LOAD_ALERTSis defined. The firmware emits it whenarms >= after_hours_threshold_ampsoutside business hours, withsync:truefor immediate delivery, and rate-limits it per channel to once peralert_cooldown_min. It isn't part of the default build.
- Routed.
circuit_summary.qorecords land in Notehub and route to a time-series store for load-profile classification. WithPLUG_LOAD_ALERTSenabled,circuit_alert.qorecords take a separate route to a real-time channel. - Downstream classification. A downstream classifier uses the rolling
circuit_summary.qostream to give each circuit a sustained load profile (always-on, scheduled, or occupied-hours) based on howact_minandmeanvary by time of day over the deployment period. Classification and dashboarding are project-specific integrations outside the scope of this reference design.
10. Validation and Testing
Expected steady-state cadence. In a healthy deployment, each device sends
one circuit_summary.qo Note to Notehub per hour. In a PLUG_LOAD_ALERTS
build, the circuit_alert.qo file should stay quiet during business hours.
After hours, it depends entirely on what the building's circuits are actually
doing. An always-on server room shows a steady non-zero mean around the clock,
while a circuit powering desktop workstations should trend toward zero after
business hours. The default build never creates circuit_alert.qo at all.
Bench first-light. Before you deploy, check on the bench that the RMS
readings make sense. Clamp one CT around a known load (a lamp, a fan, or a lab
power supply with a known current draw). Confirm that the ch1_mean field in
the first summary Note is in the expected range and that ch1_act_min equals
the number of minutes the load was on during the window. To check the near-zero
floor, plug a CT into its TRRS breakout and clip it around a conductor carrying
no current (or a de-energized wire). With the CT plugged in but unloaded, its
internal burden resistor holds the tip close to the sleeve potential, so the
bias-subtracted RMS should be stable and well below idle_threshold_amps.
Leaving the TRRS jack unplugged is a different case. With no CT inserted, the
analog input floats and can produce large, erratic readings from stray 60 Hz
capacitive pickup in the panel environment, so don't use a floating-input
reading as a "zero amps" baseline.
Using Mojo to validate power behavior. Wired inline at the Notecarrier CX
+VBAT pad, Mojo measures the whole powered subsystem: the Notecard plus
the Notecarrier CX on-board regulators, not the bare Notecard. The table below
lists Notecard's published datasheet figures for reference. Expect the
whole-assembly idle floor Mojo measures to run somewhat higher than the bare
Notecard figure, because the Notecarrier CX on-board LDOs draw quiescent current
even while the host MCU is in STOP2. During an active cellular session the modem
dominates, and the whole-assembly reading tracks the datasheet value closely.
Use Mojo to check the pattern of power consumption instead of matching a
single number against a Notecard-only spec.
| Phase | Notecard datasheet (bare Notecard only) |
|---|---|
| Deep sleep (radio off, host in STOP2) | ~8–18 µA @ 5 V (the STM32L433 in STOP2 adds ~1–2 µA). See low-power design guide |
| Host awake, sampling 4 CT channels | N/A (host MCU run-mode plus 4× ADC bursts, ~100–300 milliseconds). Check the STM32L433 datasheet and confirm the actual whole-assembly figure with Mojo on your bench. |
| Cellular session (LTE Cat-1 bis, single Note) | ~250 mA average, with brief higher-current bursts during modem attach and any 2G/GSM-fallback transmit |
After-hours alert testing (PLUG_LOAD_ALERTS builds only). When
PLUG_LOAD_ALERTS is defined and the firmware is rebuilt, isAfterHours()
returns false whenever card.time reports epoch 0. Business-hours evaluation
stays inactive until the Notecard gets valid network time from a successful
cellular (or WiFi) session. At first light, or on a bench with no service, no
after-hours alert will fire even if a circuit exceeds
after_hours_threshold_amps. Make sure the device has shown up in Notehub
(which proves at least one successful session) before you expect after-hours
alerts in testing. The default build doesn't compile this function, so there's
no alert testing to do.
Splice the
Mojo
inline between the AC/DC supply output and the Notecarrier CX +VBAT pad. A
healthy power trace shows brief active pulses every 60 seconds (the host awake
and sampling, usually 1–3 seconds each), each followed by a quiet low-current
floor, and then a longer, higher-current session once an hour when the Notecard
transmits. If there's no quiet floor between active phases, and the current
stays elevated with no periodic drop, the host isn't entering STOP2. The most
common causes are a missing ATTN→D5 jumper (the firmware then waits out the
interval awake and retries) or a build with USB support enabled, so check the
jumper and the usb=none build option. If the current shows continuous high
draw all the time, hub.set may still be in continuous mode from an earlier
bench session. To fix it, put the Notecarrier CX DIP switch in NC mode and
send {"req":"hub.set","mode":"periodic","outbound":60,"inbound":360} directly
over the USB-C connection.
11. Troubleshooting
No Notehub activity after first power-up (no claims, no Notes in Notehub, no LED activity on Notecard).
- Confirm the external cellular antenna is screwed onto the SMA bulkhead on the enclosure exterior (not inside the metal panel). A chip or flexible antenna inside metalwork won't get a cellular lock.
- Check that the u.FL to SMA pigtail is securely seated on the Notecard's
MAINu.FL connector. - Check the Notecarrier CX DIP switch position.
HSTroutes the USB-C port to the host MCU (for flashing), andNCroutes it to the Notecard (for direct API access). Neither position affects host sleep or the 5 V supply. - If you're using WiFi fallback, make sure the onboard WiFi antenna is unobstructed, since a metal enclosure blocks it the same way.
Non-zero RMS readings on a circuit with no load (CT plugged in, clamp on de-energized or zero-current wire).
- That's normal. The CT's internal burden resistor holds the tip close to the bias node (≈1.65 V), so the RMS is small and stable, usually well below the idle threshold. An unplugged CT is a different problem (next item).
Erratic or large readings on one or more channels; no pattern to the values across samples.
- Confirm the TRRS jack is fully seated in the breakout. A partially seated jack leaves the tip floating, and stray 60 Hz capacitive coupling from the panel environment can then drive large, noisy ADC readings.
- Make sure the SLEEVE terminal is connected to the BIAS NODE. If the sleeve is floating or connected somewhere else, the AC signal has no return path, and the ADC input can drift and pick up arbitrary noise.
First summary Note appears after many hours, not within the first hour.
- The device defaults to a 60-minute reporting window, and the window starts at
the first sample after power-up, not on a clock-hour boundary. So the first
Note is queued about 60 minutes after power-up and goes out on the next
outbound sync. To validate faster, set
report_interval_min=5in Notehub before first power-up, then look for a summary Note within 5 minutes of the device showing up in Notehub.
Summary Notes appear with expected values, but alerts (if using
PLUG_LOAD_ALERTS) never fire.
- Make sure
PLUG_LOAD_ALERTSis uncommented infirmware/plug_load_monitor/plug_load_monitor_helpers.hbefore you build. - Check that the device has claimed in Notehub, which proves at least one
cellular session happened. Business-hours evaluation needs valid network time
from
card.time, and that only returns a meaningful epoch after the first successful cellular (or WiFi) sync. - Confirm
biz_hours_start,biz_hours_end, andtz_offset_hoursare set correctly in the Fleet environment variables. By default, business hours are 8 AM–6 PM UTC, which only makes sense if the building is in the UTC time zone.
12. Limitations and Next Steps
This design is built around one deployment idea: a consultant clips CT clamps onto branch circuits and leaves the device for a few weeks. The simplifications below fit that engagement model. A longer-term install, a billing-grade meter, or a richer alerting layer would want to extend the design in these places.
Simplified for this Proof-of-Concept
Each of these was a deliberate scope choice. A longer-term install would want the missing capability, but leaving it out keeps a few-week consulting engagement simple enough to deploy.
Single-phase hot leg only. Each CT clamp monitors one hot leg. A 240 V split-phase circuit, like one feeding a large appliance or an HVAC unit, needs two CTs and a current sum in firmware. Three-phase circuits need three CTs per circuit and aren't supported in this design.
No actual wattage. The firmware doesn't measure voltage, so it reports amps, not watts. True power (W) = V × A × power factor, which means any wattage or kWh figure derived from amps alone rests on site-specific voltage and power-factor assumptions. Without direct measurement, those estimates can be badly off for mixed commercial plug loads, where power factor varies widely by device type. Treat amp-only energy calculations as rough order-of-magnitude indicators, not billable measurements.
True-power measurement needs an isolated voltage reference, either a low-ratio isolation transformer feeding the MCU ADC or a dedicated energy-metering analog front end (AFE) with built-in isolation. It also needs phase-aligned sampling of the voltage and current waveforms and a power-factor computation. A resistive voltage divider from mains to the MCU ADC is not a safe path, because it lacks the isolation needed to protect the circuit and the installer from line voltage. One isolated voltage reference per phase usually covers every circuit on that phase, but the voltage and current samples must be time-synchronized for the power-factor computation to be accurate.
Up to four circuits. The Notecarrier CX has six analog inputs (A0–A5), and
this design uses four (A0–A3). MAX_CHANNELS is hard-coded to 4, the firmware
clamps circuit_count to 1–4, and the note.template body and sendSummary
field list only define fields for ch1–ch4. So going to six circuits is a
firmware change, not just a hardware change. You'd have to update the pin map,
the MAX_CHANNELS constant, the state arrays (sum_arms, peak_arms,
n_arms, active_secs, alert_last_unix), the note.template body fields,
the sendSummary field list, and any downstream schema or dashboard that
assumes four channels. On the hardware side, two more CT + breakout pairs finish
the job.
Classifier is a downstream integration. Building a deployable cloud function
(AWS Lambda, Google Cloud Functions, or similar), connecting it to a time-series
database, and wiring it to a dashboard is a project-specific integration step
outside the scope of this reference design. The firmware does produce the
circuit_summary.qo inputs a classifier needs. If you enable
PLUG_LOAD_ALERTS, though, keep one side effect in mind. Until a classifier is
deployed and there's an allowlist of circuits that are supposed to be always-on,
circuit_alert.qo will fire for any circuit that exceeds
after_hours_threshold_amps at night. That includes server rooms, 24/7 signage,
and other intentional always-on loads. Expect false-positive alerts during the
initial deployment period.
Business-hours detection uses simple hour-of-day (PLUG_LOAD_ALERTS only).
With the alert extension enabled, after-hours detection looks only at the hour
of day, so it doesn't account for weekends, holidays, or shift patterns. A
more sophisticated implementation would pull a site calendar from an environment
variable or a Notehub inbound Note. Also, tz_offset_hours only supports
whole-hour UTC offsets (it's stored as int8_t), so sites in half-hour or
45-minute UTC-offset zones will get their business-hours windows evaluated
incorrectly.
Mojo is bench equipment. The firmware doesn't read Mojo's LTC2959 coulomb
counter over the Qwiic bus during normal operation. For deployments where
fleet-level energy budgeting matters, adding a mojo_mah field to the summary
Note is a simple extension.
No explicit open-CT or stuck-channel detection. The firmware can't tell two conditions apart: (a) a CT that's plugged in and clamped around a conductor carrying near-zero current, which legitimately reads near zero, and (b) a CT cable that's unplugged, or an input channel that's otherwise open-circuit. The sleeve's bias path only exists when the CT cable is seated in the TRRS jack, so an unplugged CT leaves the analog input floating. A floating pin can produce arbitrary, noisy ADC values from stray 60 Hz capacitive pickup, not a reliable near-zero floor. Without a defined pull from the analog input to the bias node (for example, a weak bleed resistor across the TRRS tip-to-sleeve terminals on the breakout), open inputs look exactly like genuine low-current readings to the firmware. Production firmware should add open-channel detection, either a hardware pull to bias on the analog input side or software checks for implausibly constant or erratic readings across many cycles, so operators can tell a sensor fault from a circuit that really is unloaded.
Sensor accuracy. Per its datasheet, the SCT-013-030's non-linearity is ±3% from 10% to 120% of rated current (3–36 A for a 30 A clamp), and it isn't specified below that. Treat sub-3 A readings as indicative only.
Production Next Steps
To turn this proof-of-concept into a hardened, longer-lived deployment, you'd close the gaps above. These extensions are worth the most, listed roughly in order of effort and impact.
True-power measurement adds the most capability. Add per-phase voltage sensing through an isolated voltage transformer or a dedicated energy-metering analog front end (AFE) with built-in isolation. Do not use a direct resistive divider from mains to the MCU ADC, because it lacks the isolation needed for safe mains-connected measurement. From there, implement phase-aligned voltage and current sampling and a cos(φ) power-factor computation to report actual watts and kWh. One isolated voltage reference per phase usually covers every circuit on that phase. What matters most is that the voltage and current samples are time-synchronized, so the power-factor term means something.
Extended CT range is the easiest change. For sub-panels with 60 A or 100 A
feeder circuits, swap in the SCT-013-060 (60 A / 1 V) or SCT-013-000V
(100 A / 1 V) and update the ct_full_scale_amps env var. You don't need a
firmware change.
Weekend and holiday awareness makes the alerting layer smarter. Pass a site
calendar or day-of-week bitmask as an environment variable, and the
PLUG_LOAD_ALERTS business-hours check can respect Saturdays, Sundays, and
holidays instead of treating every night the same.
Over-the-air firmware updates save truck rolls. Wire the host's BOOT/RESET pins to Notecard AUX pins for Notecard Outboard DFU, and you can push firmware to the whole fleet remotely without revisiting each site.
Per-circuit calibration offset improves accuracy at the low end. Measure a small zero-current offset for each circuit at commissioning with the circuit breaker open, store it in the env vars, and subtract each channel's own noise floor from its reading.
13. Summary
The ESCO consultant who used to estimate after-hours plug loads from utility
bills and a clipboard now walks into the building, clips four CT clamps onto the
sub-panel, taps 120 VAC, and starts collecting per-circuit profiles within the
hour. Nobody files a network form, sets up a VLAN, or waits six weeks on an IT
ticket. After a few weeks of hourly summaries, the 2 AM act_min trend on each
circuit points straight at the waste: the workstation bank that should be off,
the signage that runs all night, and the network closet that's been quietly
drawing load since the gear it cools was retired.
On This Page
- 1. Project Overview
- 2. System Architecture
- 3. Technical Summary
- 4. Hardware Requirements
- 5. Wiring and Assembly
- 6. Notehub Setup
- 7. Firmware Design
- Modules
- Sensor reading strategy
- Event payload design
- Low-power strategy
- Retry and error handling
- Key code snippet 1: template definition
- Key code snippet 2: immediate-sync alert (PLUG_LOAD_ALERTS builds only)
- Key code snippet 3: two-pass RMS measurement
- Key code snippet 4: sleeping until the Notecard raises ATTN
- 8. Build and Flash
- 9. Data Flow
- 10. Validation and Testing
- 11. Troubleshooting
- 12. Limitations and Next Steps
- 13. Summary