Remote Apiary Hive Health Monitor

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/apiary_hive_monitor/ so it can learn how the device works and help you adapt it to your hardware.
This project is a solar-powered hive monitor that turns each hive into a remotely-monitored asset. It tracks hive weight and brood-box climate every 15 minutes and records a short acoustic snapshot of the colony once a day. All of that gets reported without grid power, and without a truck roll every time the data looks wrong. A single Notecard for Skylo carries cellular, Skylo satellite, and WiFi radios on one module and fails over between them automatically. The same hardware and firmware reach the Blues Notehub cloud service whether the hive sits in an orchard with cell coverage or a meadow beyond any tower.
1. Project Overview
The problem. Commercial beekeepers run apiary yards in orchards, fields, and forest edges with no electrical infrastructure and no cellular signal worth depending on. A full-sized Langstroth hive in production can weigh 40–80 kg. What separates a thriving colony from a dead one, or from a hive that just swarmed and took 10,000 bees with it, shows up in kilograms, in degrees, and in a subtle shift in the pitch of the colony's hum. You can't see any of that from a distance without instruments. A traditional inspection means opening the box, smoking the bees, and physically weighing the stand. It takes time, disturbs the colony, and can't happen every 15 minutes. So hive collapse, swarm departure, and queen loss routinely get discovered days after the fact, when the economic damage is already done.
This project gives a beekeeper the remote eyes and ears they'd want at every yard but can't afford to station there in person. One weatherproof enclosure on the hive stand reads three independent signals: weight (colony biomass and honey stores), temperature and humidity inside the brood box (brood viability), and acoustic features sampled from an analog microphone at the hive entrance (behavioral state). The device processes all three locally, sampling weight and temperature every 15 minutes and capturing audio once a day, then rolls them into a daily summary and pushes it to Notehub. The summary goes out over cellular where a tower is in reach and over the Skylo satellite network where one isn't, and Notecard for Skylo picks the path automatically. In steady state the beekeeper gets one notification a day, plus an immediate alert whenever any signal crosses a threshold.
POC scope: daily audio snippet, features only. The acoustic path captures a single ~0.75-second snippet once per summary window (once a day by default). It computes zero-crossing rate (ZCR), RMS energy, and peak amplitude entirely on-device, and each summary carries only those three numbers. No raw audio is buffered, stored, or transmitted. Once a day fits the project scope, because colony acoustic state changes over hours, not minutes, and it avoids the power and payload cost of continuous acoustic sampling. See §7 and §11 for details.
Why Notecard for Skylo. Apiaries are put far from human infrastructure on
purpose. The idea is to place bees near crops, meadows, or forest edges where
there's forage and little human disturbance. Those sites have no AC power, and
cellular coverage is often marginal or missing. Each of those constraints rules
out the conventional IoT stack. A gateway-plus-cloud-SIM architecture takes
months of site negotiation per yard, and a device that only works with strong
cellular signal will fail in exactly the places where bees are most productive.
Notecard for
Skylo
(NOTE-NBGLWX) carries three radios on one M.2 module and picks among them
automatically. There's cellular (LTE-M / NB-IoT / GPRS) with its own prepaid
global SIM, satellite over the Skylo
non-terrestrial network (NTN), and WiFi. You don't negotiate with IT at
each site or sign a carrier contract, and the yards in coverage gaps don't need
a second device or part number. The firmware sets a single
card.transport
preference of wifi-cell-ntn (prefer WiFi, fall back to cellular, then fall
back again to Skylo satellite). The Notecard handles the failover itself, and
the host firmware never branches on which network is live. Skylo's
geostationary (GEO) network covers open-sky terrain well, and a hive stand in
an open orchard or meadow has plenty of open sky. Skylo coverage depends on
geography and service region, so verify current coverage for your deployment
region and review the satellite best practices
guide before relying on satellite as the
sole backhaul. Here, low-power cellular with automatic satellite failover is
the core of the architecture, and it's what makes this deployable in the real
operating environment of commercial apiculture.
New to Blues?
Learn what we do in this 3-minute video that walks you through how we simplify wireless connectivity.
Learn what we do in this 3-minute video that walks you through how we simplify wireless connectivity.
Deployment scenario. The monitor lives in a small weatherproof enclosure zip-tied or screwed to the side of the hive stand. A 5 W solar panel on a short gooseneck bracket feeds a Li-ion battery through a solar MPPT charger, and the load cell platform slides under the hive. Two sensor probes thread through existing seams or ventilation holes, one for temperature and humidity in the brood box and one for the microphone. You don't have to modify the hive body at all, and the bees never know anything changed.
2. System Architecture
Device-side responsibilities. Every 15 minutes the Notecard's ATTN pin
wakes the Notecarrier CX's onboard STM32L433 host through
card.attn. The host
reads hive weight and brood-box temperature and humidity, checks three
independent threshold rules, and folds the results into a running summary in
RAM. Audio only happens once a day. Colony acoustic state changes over hours,
so the firmware captures a ~0.75-second snippet on the rollover wake itself,
computes the ZCR, RMS, and peak amplitude on-device, and throws away the raw
samples, which never leave the device. A tripped threshold queues an alert Note
immediately, while a normal day's summary waits on the Notecard for the next
outbound sync. Between wakes the host sleeps in STM32 STOP2, drawing a few
microamps with RAM intact, until the Notecard's ATTN pin raises it again.
Notecard responsibilities. Notecard for Skylo buffers each
Note in its on-device flash queue and opens a
network session on the
hub.set outbound cadence
you configure. The card.transport wifi-cell-ntn preference set at boot
picks the path automatically. It uses cellular where a tower is in reach and
Skylo satellite where the orchard is beyond terrestrial coverage, so the daily
summary still reaches Notehub whether the apiary has cellular bars or not, and
the firmware never branches on which radio is live. Alert Notes carry
sync:true to skip the outbound timer. The beekeeper hears about a weight-drop
event in the same minute it crosses the threshold (or within minutes when the
unit is on satellite) instead of at the next scheduled flush. Environment
variables
pushed from Notehub retune thresholds and cadences in the field, so nobody has
to drive out to reflash firmware.
Notehub responsibilities. Each unit's embedded global SIM gets the Notecard onto carrier cellular networks worldwide and delivers data over the Internet to Notehub, which ingests every event and applies the project's routes. Hive summaries and alerts arrive in separate Notefiles, so one device can feed both the beekeeper's SMS endpoint and the long-term weight-trend store without any filter logic in between.
Routing to the cloud (high level). Notehub can route to HTTP, MQTT, AWS, Azure, GCP, Snowflake, and other targets. Route setup depends on your project and is out of scope here, so see the Notehub routing docs for details.
3. Technical Summary
-
Flash the firmware. Replace
PRODUCT_UIDinfirmware/apiary_hive_monitor/apiary_hive_monitor.inowith your Notehub ProductUID, then install the dependencies, build, and upload:arduino-cli core install STMicroelectronics:stm32 arduino-cli lib install "Blues Wireless Notecard" "STM32duino Low Power" "STM32duino RTC" "HX711 Arduino Library" "Adafruit SHT31 Library" arduino-cli compile --fqbn STMicroelectronics:stm32:Blues:pnum=CYGNET,usb=none firmware/apiary_hive_monitor/apiary_hive_monitor.ino arduino-cli upload -p /dev/ttyACM0 --fqbn STMicroelectronics:stm32:Blues:pnum=CYGNET,usb=none firmware/apiary_hive_monitorThe
usb=noneboard option turns off the USB CDC stack, and it has to be off for the host to sleep (see §7). The board options matchfirmware/apiary_hive_monitor/sketch.yaml, so if you run the commands from the sketch directory you can drop--fqbn. -
Wire sensors. Following §5, connect the HX711 (D5, D6), SHT31-D (SDA, SCL), and MAX9814 (A0) to the Notecarrier CX, and fit the
ATTN→D9jumper that lets the Notecard wake the host. Seat Notecard for Skylo in the M.2 slot and connect itsMAINandGPSantennas. -
Validate power. Use Mojo (bench only) to confirm ~15–40 mA while active and ~100–400 µA while asleep. Before field deployment, check that temperature/humidity readings and the audio ZCR show up in the summaries.
-
Commission in Notehub. Create a project, claim the Notecard, set
hx711_calibrationwith the known-weight test (the §9 procedure), and set environment variables at the fleet level. Then deploy.
What you'll have: A device that sends one compact daily Note with hive weight (kg), temperature/humidity averages, and acoustic features (ZCR, RMS, peak), plus immediate alerts for weight drops, temperature anomalies, or acoustic changes. It deploys fully to off-grid sites with no cellular infrastructure.
Payload example for hive_summary.qo (daily, compact template):
{
"weight_kg": 42.3,
"weight_delta": -0.8,
"temp_c_avg": 34.7,
"humidity_avg": 67.2,
"zcr_avg": 724,
"rms_avg": 0.12,
"peak_avg": 0.38,
"samples": 96,
"batt_mv": 3842
}Alert example for hive_alert.qo (immediate, sync:true):
{
"alert": "weight_drop",
"value1": 4.7,
"value2": 37.6,
"batt_mv": 3901
}Here is a sample Note this device emits:
{
"file": "hive_summary.qo",
"body": {
"weight_kg": 42.3,
"weight_delta": -0.8,
"temp_c_avg": 34.7,
"humidity_avg": 67.2,
"zcr_avg": 724,
"rms_avg": 0.12,
"peak_avg": 0.38,
"samples": 96,
"batt_mv": 3842
}
}4. Hardware Requirements
| Part | Qty | Rationale |
|---|---|---|
| Notecarrier CX (buy) | 1 | Integrated carrier with an embedded STM32L433 host MCU, so you don't need a separate Swan. It has A0–A5 analog inputs, SDA/SCL I2C, and enough digital GPIO pins for all three sensors plus the ATTN wake jumper. The host sleeps in STOP2 between wakes, and the Notecard's ATTN pin wakes it. |
| Notecard for Skylo (NOTE-NBGLWX) (buy) | 1 | One M.2 module carrying cellular (LTE-M / NB-IoT / GPRS, Quectel BG95-S5 modem), Skylo satellite (NTN), and WiFi (Silicon Labs WFM200S) radios. It seats in the Notecarrier CX M.2 slot. The firmware's card.transport wifi-cell-ntn setting makes it prefer WiFi where an access point is in range, fall back to cellular at apiaries with coverage, and fall back again to the Skylo satellite network at yards beyond cellular reach. You don't have to pick a second device or part number, and there's no separate satellite enclosure. With the integrated prepaid global SIM, there's no carrier contract and no per-site IT negotiation. Skylo GEO coverage varies by geography and service region, so verify coverage for your deployment area before relying on satellite as the sole backhaul (see satellite best practices). This design doesn't configure or transmit device location. The satellite stack uses the board's GPS/GNSS receiver internally for timing and ephemeris, not as a user-facing location source. Requires the antennas below. |
| M16 cable glands (nylon, IP68) | 2 | One for the sensor cable bundle and one where the antenna pigtail passes through the main enclosure. |
| Blues Mojo (buy) | 1 | Bench commissioning only. Coulomb counter for validating the power budget. It sits inline on the Sunny Buddy load rail between the charger and the Notecarrier CX +VBAT pad, and connects to the Notecarrier CX Qwiic port during bench testing. Take it out of the field assembly before deployment. See §9 for how to use it. |
| SparkFun Load Cell Amplifier HX711 (SEN-13879) | 1 | 24-bit ADC front end built for Wheatstone-bridge load cells. It talks to the host over two GPIO pins with a bit-banged protocol. |
| Zemic H8C-C3-100KG single-ended shear-beam load cell | 1 | IP67-rated alloy-steel single-ended shear-beam cell with 100 kg capacity, OIML C3 accuracy class, and a 4-wire Wheatstone bridge on a 1 m cable. The H8C is normally one of 3–4 cells in a platform scale. For this single-cell POC it's mounted as a cantilever weigh module, with the fixed end bolted to a rigid bracket and the free end carrying the load through a button or rocker pin. See §5 for the mount detail and §11 for the off-center loading limitations this brings. It's sized for a loaded double-brood Langstroth with supers (typically 40–80 kg). Available through Zemicus USA and Zemic-authorized scale-supply distributors. |
| Adafruit Sensirion SHT31-D Temperature & Humidity Sensor (product #2857) | 1 | I2C sensor for temperature and humidity inside the brood box, rated for high-humidity operation. Four female-to-female jumper wires connect it to the Notecarrier CX I2C header pins (VIN, GND, SDA, SCL), because the Notecarrier CX dual 16-pin header uses 0.1″ pitch and won't take a Qwiic/STEMMA QT connector directly. Brood temperature has to stay at 34–35 °C for healthy brood development, which makes it the most sensitive single health indicator in the hive. |
| Adafruit Electret Microphone Amplifier MAX9814 (product #1713) | 1 | Amplified analog electret microphone with automatic gain control (AGC). Its output connects to A0 on the Notecarrier CX for ADC sampling. The AGC keeps the signal in range across the wide swing in amplitude between a quiet winter cluster and an agitated, defensive colony. |
| SparkFun Sunny Buddy MPPT Solar Charger (PRT-12885) | 1 | Maximum power point tracking (MPPT) solar charger for single-cell Li-ion or LiPo (both chemistries share a 4.2 V CC/CV charge profile). The solar panel connects to the barrel-jack input (IN+/IN−), and the battery connects to the BATT JST connector or the BAT+/GND screw terminals. The LOAD+/GND output supplies the downstream device rail, through Mojo to the Notecarrier CX on the bench or directly in the field. |
| Adafruit Lithium Ion Battery Pack 3.7V 4400mAh (#354) | 1 | Energy buffer for overnight and cloudy-day operation. The pack is two 18650-format Li-ion cells in parallel with integrated protection. Both the form factor and the chemistry differ from a LiPo pouch, and the thermal-limit guidance in §11 is written specifically for Li-ion. At the nominal 15-minute sample cadence with the sleep-dominant firmware profile, estimated active-phase daily consumption is roughly 12–18 mAh (96 wakeup cycles × ~10 seconds at ~40 mA, plus one daily LTE-M/NB-IoT sync at ~250 mA × ~60 seconds). The sleeping assembly also draws approximately 100–400 µA quiescent (Notecard low-power idle plus the Notecarrier CX regulator and Sunny Buddy quiescent draw, per the §9 power table), which adds roughly 2.4–9.6 mAh/day. That puts the whole-system daily budget at approximately 14–28 mAh/day. A 4 400 mAh pack at 80 % depth of discharge (3 520 mAh usable) then gives you on the order of 125–250 days of reserve without any solar contribution. Actual reserve depends on NTN session frequency, cellular signal quality, and how ambient temperature affects Li-ion capacity, so validate it with Mojo during bench commissioning. The pack includes a JST-PH 2-pin connector that mates directly with the Sunny Buddy BATT JST port. |
| Voltaic Systems P105 5W 6V ETFE Solar Panel | 1 | 5 W, 6 V (peak 6.12 V / 940 mA) monocrystalline ETFE panel rated for long-term outdoor use. It ships with a 3.5 mm × 1.1 mm DC plug that goes straight into the Sunny Buddy's barrel-jack solar input. The Sunny Buddy caps charge current at 450 mA by default, so a full recharge of the 4400 mAh pack from empty takes roughly 10 hours of direct sun. |
| Hammond 1554F2GY polycarbonate enclosure (IP66, 120 × 90 × 60 mm) | 1 | IP66 polycarbonate enclosure with an opaque lid, sized for the Notecarrier CX, Sunny Buddy, and Li-ion pack. Drill two M16 cable-gland holes, one for the sensor cable bundle and one for the SMA antenna pigtail. The antenna mounts outside the enclosure (see the antenna rows below), so the opaque lid doesn't affect the satellite or cellular link. Available from Hammond and authorized distributors. |
| 4× female-to-female jumper wires, 150–200 mm (e.g. Adafruit #266) | 1 set | Connect the SHT31-D breakout's 0.1″ header pins (VIN, GND, SDA, SCL) to the matching pins on the Notecarrier CX dual 16-pin header. |
| STEMMA QT / Qwiic cable, 100 mm | 1 | Connects Mojo's Qwiic port to the Notecarrier CX Qwiic connector during bench commissioning, so you can read the coulomb-counter tally from the blues.dev In-Browser Terminal. Remove this cable before field deployment. |
| Skylo-certified LTE/satellite antenna included with Notecard for Skylo (u.FL) | 1 | Connects to the MAIN u.FL port and carries both the terrestrial cellular signal and the Skylo satellite link, so one antenna serves both networks. Use only the Skylo-certified antenna supplied with Notecard for Skylo, because an uncertified substitute risks regulatory non-compliance and link failure. Mount it outdoors on the enclosure lid or a hive-stand upright with an unobstructed view of the sky (in the northern hemisphere, the southern sky toward the equator, where Skylo's GEO satellites sit), and route it through a liquid-tight fitting. If you'd rather use an external SMA mount than the bare u.FL antenna, add a u.FL-to-SMA-F bulkhead pigtail (e.g. Adafruit #851) through the enclosure wall. |
| Passive GPS/GNSS antenna (u.FL) per the Notecard for Skylo datasheet | 1 | Connects to the GPS u.FL port and supplies the GNSS time/ephemeris the satellite stack uses internally. Mount it outdoors next to the main antenna with a clear view of the sky, and route it through a liquid-tight fitting. |
Bundled connectivity (Notecard for Skylo): It ships with an active global SIM that includes 500 MB of cellular data and 10 years of service, plus 10 KB of bundled Skylo satellite data, with no activation fees, no monthly subscription, and no separate satellite provider subscription. Syncing inbound less often saves the bundled satellite allocation, and §2 and §7 have guidance on that.
5. Wiring and Assembly
The enclosure mounts on the hive stand, and the bees never know it's there.
Three sensor leads thread through existing seams, one short jumper on the
header lets the Notecard wake the host, the antenna lives outside on the lid,
and a small solar panel on a nearby gooseneck bracket powers the whole thing.
Every host I/O lands on the Notecarrier
CX dual 16-pin
headers, and Notecard for Skylo seats in the M.2 slot. Its MAIN u.FL port
connects to the included Skylo-certified antenna, which carries both the
cellular and satellite signals, and its GPS u.FL port connects to the passive
GPS/GNSS antenna. Both antennas mount outside the box with an unobstructed view
of the sky.
Power chain. The Sunny Buddy MPPT charger sits between the solar panel and
the Li-ion battery, and the Li-ion pack connects to the Sunny Buddy's BATT
JST connector (or the BAT+/GND screw terminals). The system load comes off
the Sunny Buddy's LOAD+/GND screw terminals. The Notecarrier CX +VBAT pad
connects to LOAD+, and GND connects to the common ground rail. During bench
commissioning, Mojo sits inline on the positive load rail to measure downstream
device current:
- Solar panel DC plug → Sunny Buddy barrel jack (
IN+/IN−) - Battery JST-PH → Sunny Buddy
BATTJST connector (charges the battery from solar through MPPT, and battery negative sits at Sunny BuddyGND, which is common withLOAD GNDandIN−on the Sunny Buddy PCB) - Sunny Buddy
LOAD+→ MojoBATinput → MojoLOADoutput → Notecarrier CX+VBAT(bench only) - Sunny Buddy
LOAD GND→ MojoGND→ Notecarrier CXGND(bench: Mojo only intercepts the positive rail, and ground stays unbroken and common across all three boards) - Sunny Buddy
LOAD+→ Notecarrier CX+VBAT(field deployment, remove Mojo) - Sunny Buddy
LOAD GND→ Notecarrier CXGND(field deployment, direct ground return)
In this position, Mojo measures the current flowing from the Sunny Buddy's load rail to the Notecarrier. That's the total downstream device draw, not the battery's charge/discharge current directly. Mojo never interrupts ground, which passes straight through to the Notecarrier CX and back to the Sunny Buddy.
HX711 load cell amplifier:
- HX711
VCC→ Notecarrier CX+3V3_OUT - HX711
GND→ Notecarrier CXGND - HX711
DOUT→ Notecarrier CXD5 - HX711
PD_SCK→ Notecarrier CXD6 - Load cell
E+(excitation positive) → HX711E+ - Load cell
E−(excitation negative) → HX711E− - Load cell
A+(signal positive) → HX711A+ - Load cell
A−(signal negative) → HX711A−
Mount the H8C as a single-cell weigh module. Bolt the fixed end (the end with the strain-gauge cable exit) to a rigid steel angle bracket secured to the base or pad block, and leave the free end unconstrained so it can deflect under load. Thread a loading button or rocker pin into the tapped hole on the free end, then rest the hive stand on that loading point plus three outrigger anti-tip pads. The outriggers carry no load. They're only there to keep the platform from tipping. This turns a single-ended shear-beam, normally one of 3–4 cells in a platform scale, into a workable single-cell hive scale, at the cost of the off-center loading errors documented in §11. Follow the orientation and bolt-torque values in the H8C datasheet, because a cell installed upside-down or loaded on the wrong end gives incorrect readings and can damage the gauges. Run a thin bead of RTV silicone sealant around the cable exit to keep moisture out.
SHT31-D temperature and humidity sensor:
The Notecarrier CX dual 16-pin header uses 0.1″ pitch and won't take Qwiic/STEMMA QT connectors directly, so run four female-to-female jumper wires to the SHT31-D breakout's 0.1″ through-hole pads:
- SHT31-D
VIN→ Notecarrier CX+3V3_OUT - SHT31-D
GND→ Notecarrier CXGND - SHT31-D
SDA→ Notecarrier CXSDA - SHT31-D
SCL→ Notecarrier CXSCL
The SHT31-D's default address is 0x44, so it shares the I2C bus with the Notecard without any conflict.
- POC caveat: in-hive placement. The Adafruit SHT31-D breakout isn't a sealed probe. It's an exposed PCB with a bare humidity membrane. The brood box interior runs at 34–36 °C and ≥ 85 % RH year-round, and propolis builds up on the PCB body over time. The Sensirion SHT31 sensor element itself is rated for high-humidity operation, but the breakout's solder joints and traces aren't hardened for indefinite in-hive service. For short-term POC validation, thread the board in through an existing seam or ventilation slot, position the sensor head between frames 2 and 3 at mid-frame height, and coat the PCB body (not the sensing element) with beeswax or conformal coating to slow propolis adhesion. For production deployments, use a sensor with a sealed stainless probe tip instead of an exposed breakout board, and pot all external cable exits with marine-grade silicone or epoxy.
MAX9814 microphone:
- MAX9814
Vin→ Notecarrier CX+3V3_OUT - MAX9814
GND→ Notecarrier CXGND - MAX9814
Out→ Notecarrier CXA0 - Leave the MAX9814
Gainpin floating for the default 60 dB gain setting. If the AGC saturates in high-traffic hives, you can bring it down to 50 or 40 dB with resistors. - POC caveat: microphone placement. The Adafruit MAX9814 breakout is an unenclosed PCB with an exposed electret capsule, and it isn't hardened for in-hive service. Instead of placing it fully inside the hive body, mount the breakout board outside or at the edge of the hive entrance, close enough to pick up colony sound through the landing board gap. Between 85 %+ RH, hive acids, and propolis building up on and around the capsule opening, the acoustic response will fade over time, so this isn't supported as a long-term, maintenance-free installation. For anything beyond short-term bench validation, apply conformal coating to the PCB (masking the capsule mesh opening) and thread only a thin wiring lead through the seam. If the application needs acoustic sensing from deep inside the brood box, use a sealed MEMS microphone module rated for high-humidity environments.
Analog reference (+V_AREF pin): Leave it unconnected. The STM32L433 ADC
uses the internal 3.3 V reference for the A0 microphone input.
ATTN-to-D9 jumper (host sleep):
Fit a short jumper between the ATTN and D9 pins on the Notecarrier CX.
Both are on the same 16-pin header as the D5/D6 HX711 lines. D5 is the
usual choice for this jumper, but here it carries the HX711 data line, so the
firmware uses D9 instead through CX_ATTN_PIN. The Notecard pulls ATTN low
while the host sleeps and raises it when the next sample is due, and the rising
edge on D9 wakes the host from STOP2. Leave the Notecarrier CX EN pin
unconnected. On the CX it enables the shared 3.3 V VIO rail (host, Qwiic,
3V3_OUT, and Notecard I/O), so driving it from ATTN would brown out the
whole board instead of putting the host to sleep. Putting a Host to Sleep
Between Sensor
Readings
covers the full pattern.
Antennas (single board, cellular + satellite + GPS):
Notecard for Skylo's MAIN u.FL antenna carries both the cellular and the
Skylo satellite signal. That one antenna replaces what used to be a separate
cellular whip plus the antenna on an external satellite module.
- Connect the included Skylo-certified antenna's u.FL connector to the
Notecard's
MAINu.FL port. Orient the connector with the cable center-pin facing the port, and it clicks firmly when it's fully seated. Use only the Skylo-certified antenna supplied with the board, since an uncertified substitute risks regulatory non-compliance and link failure. - Connect the passive GPS/GNSS antenna's u.FL connector to the Notecard's
GPSu.FL port. - Mount both antennas outside the enclosure with an unobstructed view of the sky. For satellite operation in the northern hemisphere, give the main antenna a clear view of the southern sky (toward the equator), where Skylo's GEO satellites sit, and tilt and orient it to see as much sky as possible. Route each one through an M16 cable-gland port in the enclosure wall. If you'd rather use an external SMA-mount antenna, use a u.FL-to-SMA-F bulkhead pigtail (e.g. Adafruit #851) and secure the bulkhead with its lock nut from outside.
Because the satellite link shares the MAIN antenna with cellular, a placement
that gets a good cellular signal also enables satellite fallback. There's no
second module to mount and no JST cable to run. The opaque Hammond lid doesn't
matter either, since both antennas live outside the box.
Mojo bench connection (remove before field deployment). During bench
commissioning, run the STEMMA QT / Qwiic cable from Mojo's Qwiic port to
the Notecarrier CX Qwiic connector. The Notecard can then report the Mojo
coulomb-counter tally (cumulative mAh, charge/discharge rate) when you send
card.power requests from the blues.dev In-Browser Terminal. Remove the Mojo
and Qwiic cable before you seal the field enclosure, since the production
firmware doesn't query Mojo in the deployed build.
6. Notehub Setup
-
Create a project. Sign up at notehub.io and create a project. Copy the ProductUID and paste it into
firmware/apiary_hive_monitor/apiary_hive_monitor.inoasPRODUCT_UID. -
Claim the Notecard. Power up the unit somewhere with cellular (or WiFi) coverage. Notecard for Skylo associates with your project on its first non-NTN sync. Satellite won't work until this initial cellular/WiFi sync happens. That sync registers the device with Notehub, registers the Notefile templates, downloads the current satellite ephemeris, and sets device time, and no NTN transmission will succeed without all of them. Commission each unit where it has terrestrial coverage, even if it will usually run over satellite. See the satellite best practices guide.
First-boot timing note. The firmware anchors the summary window to the first successful
card.timeresponse, which is the moment the Notecard returns a valid epoch instead of an error. Any sensor readings collected before that anchor get discarded, and the window clock starts there. So the firsthive_summary.qoNote arrives after a fullreport_interval_hrwindow has passed from the first successful time sync (24 hours from that anchor by default). If the unit is commissioned indoors or somewhere with weak signal, that can be later than first power-up, because it has to get a cellular or satellite lock first. That's intentional. A summary sent before there's a valid time reference would hold at most one sensor reading and no meaningful weight delta. -
Set a fleet per apiary yard. Fleets group devices for shared configuration. With one fleet per yard, you can set threshold environment variables at the yard level, since all the hives in an orchard see similar ambient conditions, and still override them per device for a hive that's known to be a heavy producer or an established swarm catcher.
-
Set environment variables. In Notehub, go to Fleet → Environment and set any of the values below. They're all optional, and the table shows the firmware defaults. Any value set at the fleet or device level overrides the compile-time default on the device's next inbound sync. Smart Fleets can automate fleet assignment based on device metadata.
Commissioning note: inbound cadence. The firmware sets
inbound: 10080(one week) to keep satellite session costs down. That means the device won't pull env var changes from Notehub until the next scheduled inbound, which could be up to seven days away. During bench commissioning, force an immediate inbound sync after each env var change by sending{"req":"hub.sync"}from the blues.dev In-Browser Terminal (the terminal button in the upper right of any blues.dev page). You can also shorten the inbound interval for the bench session by sending{"req":"hub.set","mode":"periodic","outbound":1440,"inbound":15}from the terminal. Just restore the weekly inbound ("inbound":10080) before field deployment.Variable Default Purpose sample_interval_min15Minutes between sensor readings and threshold evaluations. report_interval_hr24Hours between summary Notes. Changing it also changes the Notecard's outbound sync cadence ( outbound = report_interval_hr × 60minutes). The firmware reissueshub.setautomatically on the next wake cycle after the env var is applied, so cellular and NTN transmission cost scales with how often you send summaries.weight_alert_kg_drop2.0Weight loss (kg) over the current summary window above which a weight_dropalert fires. The weight baseline (weight_first_kg) resets at the start of every summary window, so this threshold covers whateverreport_interval_hris set to. Change the report interval to 12 hours and it becomes a 12-hour loss threshold. Tune it per hive, because a productive colony naturally loses weight on cool, rainy days.temp_low_c32.0Brood-box temperature (°C) below which temp_anomalyfires. Below 32 °C, brood viability drops fast.temp_high_c36.0Brood-box temperature (°C) above which temp_anomalyfires. Above 36 °C, the colony is probably overheating or the sensor has moved.audio_zcr_alert1200Zero-crossing rate (counts per second) above which audio_anomalyfires. A high ZCR means the colony's behavior has shifted from its normal acoustic pattern, and it takes a physical inspection to find out why. The baseline is typically 600–800 for calm, settled colonies, but it varies by strain, season, and ambient noise.hx711_calibration2280.0HX711 scale factor (raw ADC counts per kg). Set this first using the known-weight procedure in §9. hx711_zero_offset_kg0.0Platform tare offset in kg. After you calibrate the scale factor, set this to the reading from the empty platform (the hive stand structure with no hive body). The firmware subtracts it from every weight reading. See the tare procedure in §9. reset_state(not set) Set it to 1and force an inbound sync to clear all accumulated summary-window data and restart the time anchor on the next wake. The firmware throws away any partial window in progress and starts a fresh accumulation cycle once it gets the next validcard.timeresponse. The reset is one-shot. The firmware records the current value in persisted state, so later wakes that still readreset_state=1are silently skipped. (That can go on for up to one inbound period, a week by default, before Notehub delivers the cleared value.) You can leavereset_state=1in Notehub or remove it, and either way the reset won't repeat. If you set the variable back to0, or remove it, and later set it to1again, a second reset fires exactly once. It's for starting a known-clean calibration window during bench commissioning. See §9. -
Configure routes. Add one route for
hive_alert.qo(low volume, sent to an SMS/email gateway or a CMMS endpoint) and a second forhive_summary.qo(daily, sent to a long-term analytics store for weight trend analysis across the season). Because the Notefiles are separate at the source, each one can go to a different destination without filter logic inside the route.
7. Firmware Design
Most of the time the firmware does almost nothing, and that's by design. Each 15-minute wake reads three sensors, folds the readings into a running summary, fires an alert if anything crossed a threshold, and goes back to sleep. The sketch is split across four files in one directory, which keeps the wake-cycle logic apart from the sensor drivers, Notecard helpers, and host-sleep mechanics:
firmware/apiary_hive_monitor/apiary_hive_monitor.ino: the main sketch, with wakeup sequencing, accumulation, alert evaluation, and sleep.firmware/apiary_hive_monitor/apiary_hive_monitor_helpers.h: the sharedHiveStatestruct, extern declarations, and function prototypes.firmware/apiary_hive_monitor/apiary_hive_monitor_helpers.cpp: sensor drivers, Notecard helper functions, and summary/alert emission.firmware/apiary_hive_monitor/cx_sleep.h: host sleep (card.attnrequest, STOP2 entry, ATTN wake).
To open the sketch in the Arduino IDE, choose File → Open and navigate to
firmware/apiary_hive_monitor/apiary_hive_monitor.ino. The IDE picks up the
helper files automatically because they're in the same directory.
Dependencies:
- Arduino core for STM32
(
stm32duino/Arduino_Core_STM32), from the Arduino IDE Boards Manager. Blues Wireless Notecard(thenote-arduinolibrary), from the Arduino Library Manager. The note-arduino releases page lists available versions. Check the changelog for breaking API changes before you upgrade.HX711 Arduino Libraryby Bogdan Necula and Lukas Bachschwell, from the Library Manager.Adafruit SHT31 Library, from the Library Manager.STM32duino Low Powerand its dependencySTM32duino RTC, from the Library Manager. Low Power 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 (battery power, or the Notecarrier CX USB-C
switch in NC), the STM32's USB wakeup interrupt fires continuously and the
host never stays in STOP2. Debug output (when DEBUG_SERIAL is defined in
apiary_hive_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).
Modules
| Responsibility | Where |
|---|---|
Notecard configuration (hub.set, card.transport wifi-cell-ntn for automatic cellular→Skylo satellite fallback, env.template so environment variables reach the device over NTN, accelerometer disabled; no location configured, since only the satellite stack uses the board's GPS receiver, internally) | notecardConfigure() |
| Note template definitions (compact, satellite-safe) | defineTemplates() |
| Environment variable fetch and clamping | fetchEnvOverrides() |
| HX711 10-sample weight average | readWeightKg() |
| SHT31-D I2C temperature and humidity | readTempHumidity() |
| Streaming audio feature extraction (ZCR, RMS, peak) | readAudioFeatures() |
| Threshold evaluation and immediate alert emission | sendAlert() |
| Daily aggregated summary emission | sendSummary() |
Host sleep (STOP2 until the Notecard raises ATTN on D9) | loop() → sleepUntilAttn() → cx_sleep.h: cxSleepBegin(), cxSleepUntilAttn() |
| Application state (held in RAM across sleep cycles) | HiveState |
Sensor reading strategy
Weight. The firmware wakes the HX711 by pulling the PD_SCK pin low, then
takes 10 readings in a row and averages them. The HX711 library handles the
bit-bang timing internally. A calibration factor (the hx711_calibration env
var) converts raw 24-bit ADC counts to kilograms. After the read, the firmware
puts the HX711 back to sleep (PD_SCK held high) to cut its ~1.5 mA idle draw
during the sleep cycle.
Temperature and humidity. A single SHT31-D measurement takes roughly 15 milliseconds, and the library manages the I2C transaction and CRC check internally. Values go into running sums for the daily average. If the I2C read fails (returns NaN), that sample stays out of the sum, so a disconnected sensor doesn't drag the average toward zero.
Audio feature extraction. Audio is captured once per summary window
(once a day by default). The firmware checks the audio_sampled flag after it
evaluates window expiry, not before. On a successful rollover the flag gets
cleared before the audio check runs, so audio is captured on the rollover wake
itself. That makes the rollover wake the true first wake of the new window for
all three sensor paths. On every later wake in the same window,
audio_sampled is set and the microphone isn't sampled again. On a failed
rollover (sendSummary() returned false), audio_sampled is still true from
the previous window and the block is skipped, which matches the frozen-snapshot
retry behavior for weight and temperature.
When the firmware samples audio, readAudioFeatures() runs 12 consecutive
256-sample windows on A0 at approximately 4 kHz
(AUDIO_SAMPLE_PERIOD_US = 225 µs), for a ~0.75-second snippet in total. It
computes the features one window at a time from a 512-byte stack buffer instead
of storing the whole snippet, which would take about 6 KB at 16-bit resolution.
readAudioFeatures() also returns a validity flag and rejects the read if the
signal is implausible. That covers a DC offset outside the expected mid-rail
band (a floating or disconnected A0), more than 30 % of samples within 32 LSB
of the ADC rail (clipping), or normalized RMS below a minimum threshold (a dead
or shorted microphone). On an invalid read, audio_anomaly isn't evaluated,
audio_sampled still gets set to prevent retries on every wake, and
zcr_avg, rms_avg, and peak_avg report −9999. That's the same sentinel
convention used for weight timeouts and temperature NaN reads. The firmware
only stores or transmits the three computed features and never records raw
audio.
- Zero-crossing rate (ZCR): The number of sign changes per second. A higher ZCR means more high-frequency content, which means more active colony sound.
- RMS energy: Root-mean-square of the windowed samples, normalized to the 12-bit ADC range. It tracks the hive's overall activity level.
- Peak amplitude: Normalized maximum sample value in the window.
The firmware averages these three features across the 12 windows and stores them in the daily accumulator. ZCR is the main anomaly indicator, and RMS and peak add context in the daily summary payload.
Event payload design
The device uses two
template-backed
Notefiles with "format": "compact". Skylo NTN (satellite) operation requires
it, and it's good practice anyway. Compact templates shrink on-wire payload
size by 3–5× compared with free-form JSON, which keeps each Note well under the
256-byte NTN payload ceiling.
hive_summary.qo (daily, templated compact):
{
"file": "hive_summary.qo",
"body": {
"weight_kg": 42.3,
"weight_delta": -0.8,
"temp_c_avg": 34.7,
"humidity_avg": 67.2,
"zcr_avg": 724,
"rms_avg": 0.12,
"peak_avg": 0.38,
"samples": 96,
"batt_mv": 3842
}
}hive_alert.qo (immediate, sync:true, templated compact):
{
"file": "hive_alert.qo",
"body": {
"alert": "weight_drop",
"value1": 4.7,
"value2": 37.6,
"batt_mv": 3901
},
"sync": true
}The value1 and value2 fields carry alert-specific data. For weight_drop
they hold the daily loss in kg and the current hive weight, for temp_anomaly
the current temperature and humidity, and for audio_anomaly the ZCR and RMS
values. One compact template handles all three alert types.
Low-power strategy
After each 15-minute sample cycle, loop() calls sleepUntilAttn(), which
calls cxSleepUntilAttn() (in cx_sleep.h). That sends a
card.attn sleep
request for sample_interval_min × 60 seconds, waits for the Notecard to pull
ATTN low, and drops the STM32L433 into STOP2, which costs a few microamps and
keeps RAM intact. When the interval is up, the Notecard raises ATTN, the
rising edge on D9 wakes the host, and execution continues on the next line of
loop(). The host resumes in place instead of cold-booting, so the runtime
state just stays in RAM in the HiveState struct: the summary-window
accumulators, last-alert timestamps, the weight baseline, and the
audio_sampled flag. Nothing gets serialized to the Notecard, and setup()
runs only once per power-up. 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 drops into its published low-power idle
state (~8–20 µA per the Notecard datasheet). The firmware puts the HX711 to
sleep through its PD_SCK pin after each weight reading. The MAX9814 and SHT31-D
stay powered from +3V3_OUT while the host sleeps, because the rail stays up
in this design. Their quiescent draw is small, though, and audio sampling only
happens on one of the 96 daily wake cycles.
At power-up, notecardConfigure() also issues a one-time
card.transport
{"method":"wifi-cell-ntn"} so Notecard for Skylo prefers WiFi, then cellular,
then Skylo satellite. The fallback happens in the Notecard, and the firmware
never branches on which radio is live. NTN isn't enabled by default, so this
call is what makes satellite fallback possible at all. The Notecard saves the
setting in its own flash, so sending it once is enough. notecardConfigure()
also registers an
env.template that
lists every variable in §6, because over NTN Notehub only delivers the
environment variables named in that template.
The initial sync cadence is outbound: 1440 (daily), which keeps satellite
transmission costs down at sites that run over NTN. sync:true on alert Notes
skips this timer and forces an immediate cellular or NTN session. Following the
satellite best practices guide,
inbound is set to 10080 (one week), because each inbound satellite ping
costs approximately 50 bytes of the bundled 10 KB satellite allocation.
Environment variable changes take effect on the next scheduled inbound sync.
When you change report_interval_hr in Notehub, the firmware reissues
hub.set with outbound = report_interval_hr × 60 on the next wake after it
picks up the env var. That keeps the Notecard's transmission schedule lined up
with the local summary cadence. Setting report_interval_hr to 12, for
example, shortens the summary window and cuts the outbound sync interval to 720
minutes on the same wake cycle, with no manual hub.set needed.
Retry and error handling
- The first Notecard transaction,
hub.setinsidenotecardConfigure(), usesrequestAndResponse()so the code can inspect the Notecard'serrresponse field before treating the call as a success.first_bootonly gets cleared when the Notecard confirms it acceptedhub.setwithout an error, not just when the I2C transport finished. A configuration that was rejected or silently dropped never skips setup for good. - SHT31-D reads that return NaN stay out of the daily temperature and humidity
averages. The firmware keeps a separate
temp_valid_countof successful SHT31-D reads so failed reads don't pull the average toward zero. Iftemp_valid_countis zero at summary time (every read failed),sendSummary()callssafeAvg(), which reports−9999as a sentinel. That lets downstream analytics tell a total sensor failure apart from a valid near-zero reading. - HX711 reads that time out (no data-ready pulse within 500 milliseconds)
return
−1.0and are left out of the daily weight average. - The alert cooldown (
ALERT_COOLDOWN_MIN, 60 minutes by default) keeps a slowly drifting sensor from sending a new page every 15 minutes until someone corrects the threshold or the condition clears. - Audio reads that fail the hardware-plausibility checks get rejected
entirely.
readAudioFeatures()returnsfalsein three cases: the DC offset is outside the expected mid-rail band (a floating or disconnected A0 input reads near 0 or 4095), more than 30 % of samples in a window fall within 32 LSB of the ADC rail (a railed or badly clipping input), or the normalized RMS falls below the minimum signal threshold (a dead or shorted microphone capsule). On an invalid read,audio_anomalyisn't evaluated,audio_sampledis still set so it doesn't retry on every wake for the rest of the window, andzcr_avg,rms_avg, andpeak_avgreport−9999in the summary. That matches the sentinel convention for weight timeouts (−1.0read → excluded,safeAvgemits−9999) and temperature NaN reads. If−9999keeps showing up across consecutive daily summaries, there's a hardware failure (a disconnected wire, failed capsule, or corroded contact) that needs a physical inspection.
Key code snippet 1: compact template definition
Skylo NTN (satellite) operation requires the "format": "compact" field, and
it cuts the per-Note wire size by 3–5× (a 200-byte JSON Note becomes ~40–80
bytes in compact form, for example). port must be in the range 1–100. The
numeric format codes (e.g., 14.1, 12) describe the wire encoding, where
14.1 means a 4-byte IEEE 754 float and 12 means a 2-byte signed integer.
Notecard handles the encoding and decoding automatically, so there's no manual
work. The firmware reads the response and treats an err field as a failure.
defineTemplates() only returns true once this template and the
hive_alert.qo template (port 11, a string hint for alert plus floats for
value1 and value2) are both accepted. Until then first_boot stays set,
and loop() retries the setup on each wake before it reads any sensors or
adds any Notes.
J *req = notecard.newRequest("note.template");
JAddStringToObject(req, "file", "hive_summary.qo");
JAddNumberToObject(req, "port", 10);
JAddStringToObject(req, "format", "compact");
J *body = JAddObjectToObject(req, "body");
JAddNumberToObject(body, "weight_kg", 14.1); // 4-byte float
JAddNumberToObject(body, "weight_delta", 14.1);
JAddNumberToObject(body, "temp_c_avg", 14.1);
JAddNumberToObject(body, "humidity_avg", 14.1);
JAddNumberToObject(body, "zcr_avg", 12); // 2-byte signed int
JAddNumberToObject(body, "rms_avg", 14.1);
JAddNumberToObject(body, "peak_avg", 14.1);
JAddNumberToObject(body, "samples", 12);
JAddNumberToObject(body, "batt_mv", 12);
{
J *rsp = notecard.requestAndResponse(req);
if (rsp == NULL) return false;
const char *err = JGetString(rsp, "err");
if (err && *err) { notecard.deleteResponse(rsp); return false; }
notecard.deleteResponse(rsp);
}Key code snippet 2: two-pass streaming audio feature extraction
The firmware processes each 256-sample window in two passes, so the per-window
DC operating point comes from the actual samples instead of being assumed to
sit at the ADC midpoint (2048). The MAX9814's AGC shifts the quiescent DC level
from one installation to the next, and measuring it directly lets the ZCR
threshold carry over without per-board recalibration. ZCR also comes from the
measured wall-clock window duration (t1 − t0) instead of a nominal sample
rate, so it stays accurate whatever the MCU speed or analogRead() latency.
The excerpt skips the clip count and the per-window validity gate that sit
between the two passes (see Retry and error handling). PIN_MIC_LOCAL is A0,
and AUDIO_ADC_HALFRANGE (2048) normalizes each centered sample to the range
−1 to 1. If micros() didn't advance, dur_s falls back to the nominal 4 kHz
rate (AUDIO_SAMPLE_RATE_HZ).
analogReadResolution(12);
// ...
for (int w = 0; w < AUDIO_NUM_WINDOWS; w++) {
// --- Pass 1: collect raw ADC samples, sum for DC mean, count clips --
int16_t samples[AUDIO_WINDOW_SAMPLES];
int32_t dc_sum = 0;
// ...
unsigned long t0 = micros();
samples[0] = (int16_t)analogRead(PIN_MIC_LOCAL);
dc_sum = samples[0];
for (int i = 1; i < AUDIO_WINDOW_SAMPLES; i++) {
delayMicroseconds(AUDIO_SAMPLE_PERIOD_US);
samples[i] = (int16_t)analogRead(PIN_MIC_LOCAL);
dc_sum += samples[i];
}
unsigned long t1 = micros();
int32_t dc_offset = dc_sum / AUDIO_WINDOW_SAMPLES;
// ...
float dur_s = (t1 > t0) ? (float)(t1 - t0) * 1e-6f
: (float)AUDIO_WINDOW_SAMPLES / (float)AUDIO_SAMPLE_RATE_HZ;
// --- Pass 2: compute ZCR / RMS / peak with measured DC removed ----
uint32_t crossings = 0;
float sum_sq = 0.0f;
float peak_abs = 0.0f;
int32_t prev = (int32_t)samples[0] - dc_offset;
for (int i = 1; i < AUDIO_WINDOW_SAMPLES; i++) {
int32_t cur = (int32_t)samples[i] - dc_offset;
if ((prev < 0 && cur >= 0) || (prev >= 0 && cur < 0)) crossings++;
// Normalise to [-1, 1] using the ADC half-range as the divisor.
float norm = (float)cur / (float)AUDIO_ADC_HALFRANGE;
sum_sq += norm * norm;
float abs_n = (norm < 0.0f) ? -norm : norm;
if (abs_n > peak_abs) peak_abs = abs_n;
prev = cur;
}
zcr_accum += (float)crossings / dur_s;
rms_accum += sqrtf(sum_sq / (float)(AUDIO_WINDOW_SAMPLES - 1));
peak_accum += peak_abs;
}After the loop, dividing these sums by the number of windows that passed the
gate (at least 7 of the 12 in AUDIO_NUM_WINDOWS) produces the zcr_avg,
rms_avg, and peak_avg fields in hive_summary.qo.
Key code snippet 3: sleeping until the Notecard raises ATTN
The sketch's sleepUntilAttn() wrapper calls cxSleepUntilAttn(), which asks
the Notecard to hold ATTN low for the requested seconds, confirms the pin
went low, and puts the STM32 into STOP2. Because the host stays alive, the
firmware checks the result of the card.attn request instead of treating it as
fire-and-forget. setup() arms the wake once with cxSleepBegin(), and the
last line of loop() sleeps for sample_interval_min × 60 seconds. The HX711
already has D5, so the sketch defines CX_ATTN_PIN as D9 before including
cx_sleep.h.
// D5 is the HX711 data line, so the ATTN jumper lands on D9 instead.
#define CX_ATTN_PIN D9
#include "cx_sleep.h"
// ...
static void sleepUntilAttn(uint32_t seconds) {
// ...
if (!cxSleepUntilAttn(notecard, seconds, NULL, log)) {
// The Notecard didn't take the sleep request, or ATTN never went low
// (check the ATTN -> D9 jumper). Keep the sample cadence and retry.
// ...
delay(seconds * 1000UL);
}
}
// ...
// Wake from STOP2 on the ATTN rising edge (ATTN jumpered to D9).
cxSleepBegin();
// ...
sleepUntilAttn((uint32_t)sampleMin * 60u);8. Data Flow
Collected. Every 15 minutes, the device reads hive weight in kg (a 10-sample HX711 average) plus brood-box temperature (°C) and relative humidity (%). Once per summary window (daily by default, on the rollover wake), it takes the audio zero-crossing rate (counts/s), RMS energy, and peak amplitude from a ~0.75-second acoustic snapshot.
Accumulated. Readings get summed into per-metric accumulators in RAM, which survive STOP2 sleep. The Notecard only ever receives aggregated values and alert conditions, never raw samples.
Transmitted.
hive_summary.qo: one compact-template Note everyreport_interval_hr(24 hours by default). Each numeric field is the mean of its valid samples over the window, and a field with zero valid reads reports−9999. Theweight_deltafield is the difference between the day's first and last valid weight readings, so a swarm departure shows up in the summary right away even if no threshold alert fired.hive_alert.qo: sent immediately when a threshold trips, withsync:trueto force an immediate cellular or NTN uplink. After an alert type fires, it's suppressed forALERT_COOLDOWN_MIN(60 minutes).
Routed. Both Notefiles land in Notehub, and from there they go wherever the project's routes send them.
Triggers. Three rules fire alerts:
weight_drop: weight loss ≥weight_alert_kg_drop(2.0 kg by default) from the first valid reading of the current summary window to the most recent reading. The baseline resets with each summary, so the effective threshold window equalsreport_interval_hr. This is the main indicator for swarm departure (fast, over 1 kg/hour), theft, or gradual starvation.temp_anomaly: brood-box temperature outside thetemp_low_c–temp_high_cband (32–36 °C by default). It can mean chilling brood (late-fall cluster shrinkage), overheating (poor ventilation), or a queen loss that reduces the cluster's heat production.audio_anomaly: audio ZCR exceedsaudio_zcr_alert(1200 counts/s by default). A high ZCR means the colony's acoustic behavior has measurably changed from a settled colony's typical pattern. ZCR alone can't tell you the biological cause, whether that's defensive arousal, a disrupted colony, or environmental noise, so it takes a physical inspection. The firmware doesn't currently flag a sustained low ZCR on an otherwise healthy hive, since that would need a per-colony baseline model.
9. Validation and Testing
Expected cadence in steady state. A healthy, settled colony produces one
hive_summary.qo Note a day and no hive_alert.qo Notes. Plan on a short
tuning period after deployment to make sure the weight, temperature, and audio
thresholds suit that particular hive. A heavy honey super changes the weight
baseline, an aggressive strain may have a higher natural ZCR, and early-spring
colonies run cooler than peak-season ones.
Bench validation sequence. Before field deployment:
Key constraint for weight calibration. weight_kg in hive_summary.qo is
the mean of all valid readings accumulated across the entire summary
window, not an instantaneous reading. The platform load has to stay constant
and unchanged for the full length of every summary window you use for
calibration or tare. A change to report_interval_hr takes effect on the
next wake after the env var is applied. The firmware re-runs the expiry
test now − last_report_epoch ≥ reportHr × 3600 on every wake with the
current env var value, so a shorter interval can make the current window expire
right on that wake. Always start calibration from a known-clean window boundary
with the reset_state env var (see step 1b below).
-
Scale-factor calibration.
a. Shorten the window. In Notehub, set
report_interval_hrto1. Force an immediate inbound sync by sending{"req":"hub.sync"}from the blues.dev In-Browser Terminal (see §6 step 4). Without it, the weeklyinbound: 10080setting means the env var might not arrive for up to seven days. Wait for the Notecard to confirm the sync. The new interval takes effect on the next wake after the env var is applied.b. Clear any partial window in progress. The summary window lives in RAM, and only a power-cycle or reset clears it. To start from a known-clean boundary without touching the hardware, set
reset_stateto1in Notehub (see §6) and force an immediate inbound sync ({"req":"hub.sync"}). On the next wake the firmware clears all accumulators and setslast_report_epoch = 0, and the window gets re-anchored when the next validcard.timeresponse arrives. The reset is one-shot. The firmware silently skips later wakes that still readreset_state=1(before the next inbound sync delivers the updated value). Once the first 1-hour summary has arrived, which tells you a complete clean window went by, you can leavereset_state=1in Notehub or remove it. The window won't reset again.c. Empty-platform window. Put the empty load-cell platform (no hive body) on the load cell and leave it completely undisturbed. Wait for one complete 1-hour summary, then record
weight_kgfrom that Note asplatform_reading_kg.d. Known-mass window. Put a known reference mass (a 10 kg bag of flour or a calibrated test weight, for example) on the platform and leave it completely undisturbed for one full window. Record the resulting
weight_kgasreported_weight_kg.e. Compute the corrected scale factor:
new_hx711_calibration = old_calibration × (reported_weight_kg − platform_reading_kg) / known_weight_kgThe HX711 library computes
get_units() = raw_counts / scale, so raising the scale factor lowers the reported value, and lowering it does the opposite. If the reported net weight is too high, the ratio is above 1 andnew_calibrationgoes up, which brings the output down. If it's too low, the ratio is below 1 andnew_calibrationgoes down, which brings the output up.Example:
old_calibration = 2280,platform_reading_kg = 0.3, andreported_weight_kg = 13.3with a 10 kg reference. The net reported weight is 13.0 kg (30 % high), sonew_calibration = 2280 × 13.0 / 10.0 = 2964. Sethx711_calibration = 2964in Notehub and force an inbound sync. Then setreset_state = 1and force a second inbound sync to throw out the current partial window, since it holds readings taken under the old calibration factor. Before you move on, run one more full-window known-mass test with the platform load undisturbed and confirm the result is within ±0.5 kg. The reset fires once. You can leavereset_state=1in place or remove it, and you don't need to do anything else to prevent repeated resets. -
Platform tare (zero offset).
a. Once scale-factor calibration checks out, remove the reference mass and leave only the empty stand platform, undisturbed, for one full summary window. Record
weight_kgfrom that Note. That's the platform dead-load.b. Set
hx711_zero_offset_kgin Notehub to that value and force an inbound sync. Then setreset_state = 1and force a second inbound sync to start a clean verification window. From here on,weight_kgreadings cover only the hive body and its contents, referenced to zero with the empty stand. Confirm that the first full-window empty-platform summary reports approximately 0.0 kg (typically within ±0.2 kg for this load cell class). The reset fires once. You can leavereset_state=1in place or remove it, and later wakes won't repeat the reset.Important:
hx711_zero_offset_kgonly corrects the static dead-load. The firmware doesn't compensate for load-cell temperature drift, and alloy-steel shear-beam cells drift slightly with temperature. For seasonal outdoor deployments, re-check the offset once a season or after a big change in ambient temperature.Relative vs. absolute weight: If you skip the tare procedure,
weight_kgincludes the platform dead-load and doesn't represent true hive mass. Theweight_deltafield and theweight_dropalert are relative measurements, so they stay meaningful whether or not the tare offset is set. -
Restore the production interval. Set
report_interval_hrback to24(or your target production value) in Notehub and force a final inbound sync. Then setreset_state = 1and force one more inbound sync to start a clean first daily window. The firmware fires the reset once and ignores anyreset_state=1readings after that. You can leavereset_state=1in Notehub or remove it before field deployment. Either is safe, and the device won't reset its window on the first weekly inbound sync. -
Breathe on the SHT31-D sensor and check that the temperature and humidity fields rise in the next summary Note.
-
Speak clearly into the microphone and check that
zcr_avg,rms_avg, andpeak_avgshow up in the summary. Audio is only captured once per summary window, so either watch the debug output on the Notecarrier CX debug jack (ST-LINK serial, see §7) or rely on the shortened 1-hourreport_interval_hrbench run to confirm the values quickly. WithDEBUG_SERIALenabled inapiary_hive_monitor_helpers.h,[APP] Summary sentprints the sample count and all three audio feature values. Restore production defaults before field deployment. -
Alert pipeline validation. Two alert rules make reliable bench tests:
-
Temperature path (instant, no additional equipment). During indoor bench testing the SHT31-D reads ambient room temperature, typically 20–24 °C, which is already below the default
temp_low_cthreshold of 32 °C. So atemp_anomalyalert fires on the first wake cycle after commissioning without any configuration change. Use it to confirm the full alert path (host → Notecard → Notehub → route) works before you move on to field deployment. After that first alert, the 60-minute cooldown (ALERT_COOLDOWN_MIN) suppresses repeats, so reset the device or wait out the cooldown if you want a second confirmation. -
Weight-drop path (physical mass removal required). The
weight_droprule compares the current weight toweight_first_kg, the first valid reading of the current summary window, not to the previous 15-minute sample. If the platform mass hasn't actually changed since the window started, loweringweight_alert_kg_dropto a small value and waiting won't fire an alert. To test this rule deterministically, let the unit finish at least one full wake cycle soweight_first_kgis anchored. Then physically remove a calibrated mass from the platform that exceeds the configured threshold (for example, take off a 2 kg test weight whileweight_alert_kg_dropis at the production default of2.0). Aweight_dropalert should show up in Notehub within the next 15-minute sample cycle. Put the mass back and confirm later weight readings return to near the baseline before you call the test complete.
-
Using Mojo to validate power behavior. During bench commissioning, the
Blues Mojo coulomb counter sits inline between
the Sunny Buddy LOAD+ output and the Notecarrier CX +VBAT rail. Run a
STEMMA QT / Qwiic cable from Mojo's Qwiic port to the Notecarrier CX Qwiic
connector, and the Notecard can relay Mojo's cumulative mAh tally when you send
card.power requests from the blues.dev In-Browser Terminal. Here's the
expected current draw by phase:
| Phase | Figure | Source |
|---|---|---|
| Host + sensors active, weight + temp cycle (no audio) | ~15–30 mA, ~5–8 s/cycle | Whole-device estimate |
| Host + sensors active, weight + temp + audio cycle | ~20–40 mA, ~6–9 s/cycle | Whole-device estimate (audio adds ~5–30 mA × 0.75 seconds) |
| Notecard low-power idle (between sessions, host in STOP2) | ~8–20 µA, plus ~1–2 µA for the STM32L433 in STOP2 | Notecard datasheet; STM32L4 datasheet |
| Cellular session (BG95-S5 modem) | ~250 mA avg, with brief peaks up to ~2 A for a few ms on a 2G burst | Notecard datasheet |
| Skylo NTN satellite session | ~250 mA avg (same magnitude as a cellular session) | Notecard datasheet |
| Host in STOP2, Notecard low-power idle + Sunny Buddy quiescent | ~100–400 µA typical | Whole-device estimate |
The published Notecard figures come from the Notecard for Skylo datasheet. The whole-device estimates include the Notecarrier CX regulator and Sunny Buddy quiescent draw, so validate them with Mojo on your own assembly.
On a unit that's sleeping correctly, Mojo shows a brief 5–8 second active spike
every 15 minutes and a longer cellular spike once every 24 hours. If you see a
continuous draw over 20 mA instead, the host isn't entering STOP2. Check the
ATTN→D9 jumper on the header, and confirm the firmware was built with USB
support set to None (usb=none). If there's no cellular spike in 25 hours,
check the hub.set outbound cadence and confirm that the device has associated
with a Notehub project.
Since the unit runs on solar, the shape of the trace matters more than the absolute mAh number. The bench measurement still earns its keep as a regression test. If a firmware change doubles the sleep-phase current, the Mojo trace shows it right away, before the unit ever goes out to a remote yard.
10. Troubleshooting
| Symptom | Likely Cause | Check |
|---|---|---|
| No Notes appear in Notehub after 48 hours | Notecard isn't associated with the project, or the first-boot config failed | Make sure PRODUCT_UID is set correctly, and look in the Notehub console for any device events. Force a hub.sync from the terminal to trigger association. |
Continuous temp_anomaly alerts indoors | SHT31-D reading room temperature, which is below the default temp_low_c = 32 °C | That's expected during bench testing, and the alerts confirm the alert path works. Ignore them or lower temp_low_c during commissioning. They'll stop once the unit is deployed in a warm hive. |
| Weight readings are negative or too high | Calibration scale factor not set, or platform tare offset is wrong | Run the known-weight calibration procedure in §9 step 1. Confirm that hx711_calibration and hx711_zero_offset_kg are set as Notehub environment variables. |
| Weight doesn't change between summaries, or only changes erratically | Load cell disconnected, or HX711 powered down before sleep | Make sure all four load cell wires (E+, E-, A+, A-) are connected to the HX711. Check that the D5/D6 GPIO pins aren't shorted or held low continuously. Confirm the HX711 sleep command isn't being called too early. |
| Audio features always show -9999 in summaries | Microphone disconnected, amplifier unpowered, or MAX9814 output clipped/floating | Check the A0 connection from the MAX9814 to the Notecarrier CX, and make sure the MAX9814 Vin and GND are connected to the 3.3 V rail. Speak clearly into the microphone during the audio sample window, which happens on the first wake of each summary window and shows up in the debug output if DEBUG_SERIAL is enabled. |
| Mojo shows a continuous draw over 20 mA with no 15-minute pattern | Host never sleeps, because the ATTN→D9 jumper is missing or the firmware was built with USB CDC enabled | Confirm the jumper between ATTN and D9 on the Notecarrier CX header (§5) and that the build used USB support: None (usb=none, see §7). With the USB CDC stack enabled and no USB host attached, the host leaves STOP2 within a millisecond. Check the DEBUG_SERIAL output on the debug jack, where [APP] ATTN sleep failed means ATTN never reached D9. |
| Satellite (NTN) never transmits, even with a sync:true alert | Initial cellular/WiFi sync never completed, card.transport not set to an ntn method, or antenna/sky-view issue | The device has to complete at least one successful cellular or WiFi sync (which registers templates and downloads ephemeris) before NTN is enabled, and card.transport must be wifi-cell-ntn (notecardConfigure() sets it on first boot). Bring the unit into cellular coverage and force a hub.sync from the In-Browser Terminal. Once that sync completes, alerts with sync:true fall back to satellite when cellular isn't available. Make sure the Skylo-certified MAIN antenna is mounted outdoors with a clear view of the sky (the southern sky in the northern hemisphere). |
| Device claims to Notehub but doesn't pull environment variables | Inbound sync cadence is too long (10080 minutes = 1 week by default) | During commissioning, force an immediate inbound sync by sending {"req":"hub.sync"} from the Blues In-Browser Terminal. The device pulls environment variable changes on the next scheduled inbound or after a manual sync. §6 step 4 covers the commissioning settings. |
11. Limitations and Next Steps
This reference design covers a single hive with a single load cell, exposed PCB-style sensors, and a heuristic acoustic threshold. That's fine for a POC, but it isn't ready for an unattended multi-year orchard deployment yet. The list below lays out the limits that come with that scope. It starts with the Li-ion thermal limits that matter most for outdoor apiculture and ends with the production paths that turn this into a fleet-grade hive scale.
Simplified for the POC
Each of these was a deliberate call to keep the POC simple, from the Li-ion thermal limits down to the one-hive-per-device boundary. A production deployment will revisit them with a safer battery, a richer model, or another sensor.
Li-ion battery thermal limits: safety and reliability. Lithium-ion cells, the chemistry of the Adafruit #354 18650 pack specified in §4, must not be charged below 0 °C (lithium plating can cause an internal short) or above approximately 45 °C (faster cycle-life degradation and, at extremes, thermal runaway). The SparkFun Sunny Buddy (LT3652) doesn't use a battery-thermistor input in its default board configuration, so it doesn't gate charging on temperature. In practice, that means:
- Freezing mornings: The solar panel will try to charge the battery at ambient temperature. In apiaries that see sub-zero nights in spring or autumn, the charger pushes current into a cold cell. At best that degrades capacity, and in severe cases it's a safety risk. A production deployment should use a charger with NTC thermistor supervision or add a simple low-temperature charge-inhibit circuit.
- Sun-heated enclosures: A polycarbonate enclosure on a south-facing hive stand in direct sun can easily hit 50–60 °C inside on a summer afternoon, well above the safe charging ceiling. In production, use a metal enclosure with thermal mass and a sun shield or white enclosure finish, and check temperatures with a data logger before any long-term deployment.
- Capacity in cold weather: Li-ion usable capacity drops to 60–80 % at 0 °C, and further below freezing. The 4400 mAh reserve calculations in §4 assume room-temperature performance, so scale the estimated reserve down for cold-climate apiaries.
- Production alternatives: For year-round outdoor apiary deployment, consider a LiFePO₄ pack and charger/BMS combination rated specifically for your deployment climate. LiFePO₄ handles high temperatures better than Li-ion, but safe charging limits vary a lot with the specific chemistry, cell construction, and BMS design. Many packs still prohibit charging below 0 °C without an integrated heater or a low-temperature-rated BMS. Don't use chemistry-wide temperature numbers in place of the datasheet for the pack you plan to use. Pick a battery pack and charger whose rated charge and discharge temperature windows cover the full seasonal temperature swing at your deployment site, and verify those limits in the manufacturer's datasheet before you commit to a production build. For winter-dormant hives that don't need solar recharge, a primary lithium cell bank is another option.
Audio analysis is heuristic, not ML-based. Zero-crossing rate and RMS energy
are established acoustic features in beehive research, but the firmware applies
simple static thresholds instead of a learned colony baseline. The
audio_anomaly alert means the colony's sound has measurably changed from a
typical settled-colony signature, and it is not a diagnosis. The cause
could be defensive arousal, post-swarm disruption, environmental noise (nearby
equipment, traffic), or some other disturbance, and only a physical inspection
can sort out which. A naturally more vocal colony (Carniolan vs. Italian
strains, hot weather, nearby traffic) may fire false positives. A colony that's
genuinely quieter than the default threshold may never fire an alert, even
when conditions call for attention. You can tune this per hive with the
audio_zcr_alert env var, but doing it well takes a few weeks of observation.
More specific hypotheses about queenlessness, swarming pressure, or defensive
state need richer acoustic features, a learned baseline for that colony, and
physical verification before you can draw any conclusion.
Load cell calibration is manual; absolute weight requires a tare step. Both
the hx711_calibration scale factor and the hx711_zero_offset_kg platform
tare have to be set experimentally (§9 has the procedure). Without the tare
step, the weight_kg field includes the platform dead-load and doesn't
represent true hive mass. The relative fields (weight_delta and the
weight_drop alert) stay valid whether or not you've calibrated. The firmware
has no temperature compensation for the load cell, and alloy-steel shear-beam
cells drift slightly with seasonal temperature swings, which can add a small
systematic offset over the year.
Single load cell, single-end weighing. The design weighs the hive from one end, which is accurate as long as the load cell is centered under the stand. An uneven hive body (a tilted entrance board, or an added super on one side) can throw in a systematic reading offset. A proper hive scale uses 4 corner cells with a Wheatstone bridge, and this POC uses one cell to keep things simple.
Satellite (NTN) operation caveats. The same Notecard for Skylo runs the
same firmware everywhere, but when it falls back to the satellite link, the NTN
path behaves differently in ways you need to plan for. The device has to
complete at least one successful Notehub sync over cellular or WiFi before NTN
transmissions will work. Notecard needs registered templates, time, and
location data to find satellites efficiently, so a unit deployed straight into
a cellular dead zone won't transmit until someone brings it into terrestrial
coverage for a moment. Over satellite, a sync:true alert gets priority for
the next Skylo session, but finding a satellite and finishing the transmission
takes several minutes, not seconds. Each Note has to fit within the NTN
256-byte maximum, which the compact templates here do. Each inbound env-var
sync also uses ~50 bytes of the bundled 10 KB satellite allocation, which is
why inbound defaults to weekly. For the satellite link to work, the MAIN
antenna has to be outdoors with an unobstructed view of the sky (in the
northern hemisphere, the southern sky toward the equator). Skylo coverage
depends on region, so verify coverage for your deployment area (see satellite
best practices).
No audio recording. The firmware only computes features and never stores or transmits raw audio. You can't diagnose a specific anomaly from the cloud alone, so it takes a follow-up physical inspection. The feature scores narrow down the likely cause, but they don't replace the beekeeper's judgment.
Mojo is bench-only in this POC. The firmware doesn't read the Mojo's
LTC2959 coulomb counter over I2C. Mojo is only a bench instrument for
validating power draw during development. Adding a batt_mah field to the
daily summary would be straightforward.
Single hive per device. One Notecarrier CX manages one hive, so a yard with 50 hives needs 50 units. A reasonable production extension would be a multi-drop I2C bus with individually addressed sensors and a single Notecard handling the whole yard.
Production Next Steps
Once a yard has the basic hive scale running, these extensions turn it into a fleet-grade instrument. They're listed roughly from the accuracy gains you'd want first to the deeper analytics and fleet-update work.
Per-colony audio baseline learning records a rolling 30-day ZCR distribution for each hive and only alerts when a reading lands more than two standard deviations from that colony's own baseline, instead of using a fixed threshold.
A multi-cell weight platform with four corner cells in a full Wheatstone bridge delivers platform-grade accuracy and gets rid of tilt errors.
4-wire cable waterproofing pots the load cell cable exit and every external sensor penetration in marine-grade epoxy so they hold up for years outdoors.
A Varroa mite proxy is within reach. Some research groups have found spectral signatures in the 100–300 Hz band that correlate with high Varroa loads, and adding a narrow-band FFT bin to the audio features would surface that indicator with no extra hardware.
Outboard DFU through Notecard Outboard Firmware Update lets you ship new host firmware images to a whole yard fleet over cellular without a truck roll to each hive. That covers bug fixes, better audio scoring algorithms, new threshold logic, or support for more sensors. Notehub environment variables already handle operational thresholds and calibration values today, and ODFU covers the separate job of updating the compiled firmware logic itself.
12. Summary
The beekeeper who used to find out about a departed swarm three days after the fact now gets a daily weight-trend notification, plus an immediate alert the moment the hive loses 2 kg, wherever the colony happens to live in the orchard, meadow, or forest edge. Three sensors and a single Notecard for Skylo, with cellular, Skylo satellite, and WiFi on one module, turn the hive stand into a continuously monitored asset. It reaches cellular where there's a tower and the Skylo satellite network where there isn't, and there's nothing to swap. Sampling runs every 15 minutes, the summary goes out once a day, and threshold alerts skip the transmit timer entirely. Long-lived solar operation is realistic on the energy budget described in §4 and §11, but only after you work through the commissioning steps in §9: Mojo power validation, load-cell calibration, and a battery chemistry rated for the deployment climate. For a commercial beekeeper managing dozens of yards across a region, that's the difference between a minor nuisance and a significant economic loss, and it's the gap cellular-first IoT was built to close.