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

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

VFD Pump Predictive Maintenance

VFD Pump Predictive Maintenance banner
note

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

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

This project is a downtime prevention reference design for industrial centrifugal pumps. The pump's VFD (variable frequency drive) already knows a lot about how the pump is doing. This design reads that data over Modbus RTU with an industrial PLC (programmable logic controller) and sends it out through a cellular Notecard, so the pump can be monitored remotely and serviced before it fails.

1. Project Overview

The problem. Nearly every industrial centrifugal pump runs behind a VFD. Modern drives from ABB, Yaskawa, Danfoss, and Schneider all publish the pump's operating telemetry in Modbus holding registers: motor current, output frequency, output torque, drive temperature, runtime hours, and the active fault code. Almost nobody reads them. The VFD sits in the cabinet doing the work with all of that data on hand, and there's no network path to get it off the plant floor.

A failing pump rarely just stops. It usually warns you first. Motor current can shift at constant frequency as bearing drag, fouling, valve position, fluid viscosity, or impeller condition change the load on the drive. Transient electrical faults can cluster on a stressed contactor or in a cavitating hydraulic regime. Actual runtime can creep above the expected duty cycle as a downstream valve fouls and the pump runs longer to do the same work. None of these shifts is diagnostic on its own, but a maintenance team would happily act on any of them a week early. This device catches those anomalies and sends them out before a tank runs dry on a Saturday night. The Signal limitations note covers what this telemetry can and can't tell you.

Why Notecard. Pump rooms sit on isolated OT (operational technology) networks. Plant policy usually keeps corporate WiFi off-limits for instrumentation, and nobody is going to retrofit an OT-friendly access point for every pump. Cellular gets the data out without plant LAN credentials, VLAN provisioning, site WiFi, or a local gateway. Some sites will still want an OT/security review before they allow any wireless instrumentation, but the device itself never needs access to the plant network. Because Notecard bundles global cellular data and service, the same firmware and architecture work in every region. Just pick the Blues Wireless for OPTA cellular variant that matches where you're deploying. WiFi stays available as an opportunistic fallback for the rare site that can offer a rooftop AP, and the design stays cellular-first.

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.

Why OPTA. Wireless for OPTA was made for jobs like this one, where a real industrial PLC reads a real industrial Modbus device with no PC or gateway in between. The Finder OPTA mounts on a DIN rail, runs on 12–24 VDC, and takes both Arduino sketches and IEC 61131-3 PLC languages. That means it installs the same way at every plant, including the ones whose IT and OT teams will never agree on a network plan. Blues Wireless for OPTA adds cellular and WiFi to a device that already speaks the plant's language.

Deployment scenario. One OPTA with Wireless for OPTA clips onto the DIN rail next to the VFD inside the pump's electrical panel. RS-485 daisy-chains to the drive's communication port, and the antenna lead runs out through a cable gland to a magnetic-mount whip on the cabinet roof. Power comes from the 24 VDC panel supply that's already there. You don't need a PC, a gateway, or anything from the plant LAN.

2. System Architecture

System architecture: VFD ↔ Modbus RTU over RS-485 ↔ OPTA + Wireless for OPTA ↔ cellular ↔ Notehub ↔ customer cloud

Device-side responsibilities. Inside the pump panel, the OPTA's Cortex-M7 acts as the Modbus RTU client (master) to the VFD's server (slave). Once a minute it polls six holding registers through the onboard RS-485 transceiver. Between polls it keeps rolling hourly statistics (mean, peak, and count) in RAM and runs four anomaly-detection rules against them to decide, event by event, whether the plant needs to hear about what it just saw. When something is worth reporting, the host hands a JSON Note to the Notecard over I²C through the AUX connector on Blues Wireless for OPTA. The host never deals with modem AT commands, a session state machine, or raw sockets. The note-arduino library's JAdd* helpers build the request, and the Notecard handles everything after that.

Notecard responsibilities. The Notecard queues every Note the host writes and ships them on the hub.set outbound cadence (hourly by default). Anything tagged sync:true jumps the queue, and the radio comes up right away. On every inbound sync the Notecard also pulls environment variables. That's how a maintenance lead can retune thresholds, or even point the firmware at a different drive vendor's register addresses, from a browser without anyone visiting the panel.

Notehub responsibilities. Blues Notehub ingests and stores every event, and project-level routes forward them to your other systems. Each plant's VFDs may use different register addresses, so per-fleet environment variables are what let a single firmware image run an ABB plant in Ohio and a Yaskawa plant in São Paulo. Smart Fleets can group devices by vendor or by site.

Routing to the cloud (high level). Notehub can route to HTTP, MQTT, AWS, Azure, GCP, Snowflake, and several other destinations. Route setup depends on your project, so see the Notehub routing docs for details.

3. Technical Summary

What you'll have when done: An OPTA and Notecard assembly mounted in a pump panel. It reads six telemetry registers from the VFD once a minute, reports hourly summaries to Notehub, and sends an alert right away whenever one of four anomaly rules trips.

Minimum steps (60–90 minutes, assuming site access and a calibrated VFD):

  1. Install Arduino core + libraries (Arduino Mbed OS Opta Boards, note-arduino, ArduinoModbus, ArduinoRS485) via Library Manager.
  2. Set PRODUCT_UID in the firmware, then compile and flash with arduino-cli (see §8. Build and Flash below).
  3. On Notehub: create a project, claim the Notecard, create one fleet, and set modbus_slave_id and modbus_baud to match your VFD's configuration.
  4. Wire the OPTA's RS-485 to the VFD's Modbus port, and confirm 120 Ω termination at each end of the bus.
  5. Power up and watch a serial monitor. card.status should report healthy, and the first vfd_summary.qo Note should reach Notehub within about two hours. The firmware writes it at the end of the first 60-minute report window, and it ships on the next hourly outbound sync.

Here is a sample Note this device emits:

{
  "f_hz_mean": 59.8, "f_hz_peak": 60.1,
  "i_a_mean": 12.4, "i_a_peak": 13.7,
  "trq_mean": 68, "trq_peak": 74,
  "drv_c_mean": 42, "drv_c_peak": 51,
  "run_min": 53, "stop_min": 7,
  "hrs_total": 18234,
  "fault_count_hour": 0,
  "last_fault": "0"
}

4. Hardware Requirements

PartQtyRationale
Finder OPTA Codesys (8A.04.9.024.832C)1Industrial PLC with an onboard RS-485 transceiver, DIN-rail mount, and 12–24 VDC supply, included in the Wireless for OPTA Bundle. For this project it runs Arduino firmware (see §8) that hosts the Modbus client and edge logic.
Blues Wireless for OPTA1Snaps onto the OPTA's expansion port and adds a Notecard Cell+WiFi (WBNAW) over I²C. Cellular coverage is regional, so pick the SKU that matches where you're deploying. The Notecard manages its own cellular session on supported carrier networks worldwide through its embedded global SIM.
External cellular antenna(s) w/ SMA, ~3m lead (e.g. SparkFun CEL-16432)1 required, 2 recommendedAt minimum, route the primary cellular antenna outside any metal cabinet, since rubber-duck antennas inside a steel panel won't work reliably. For the best LTE Cat-1 performance, route the diversity antenna outside too if the layout allows. SMA bulkhead leads through cable glands keep the IP rating intact.
Mojo1Coulomb counter for checking the per-session energy of the Wireless for OPTA + Notecard subsystem during commissioning. It stays on the bench and isn't deployed to the field.
24 VDC DIN-rail supply, ≥10W (e.g. MeanWell HDR-15-24)1Powers the OPTA and expansion. Most pump panels already have one, so you may not need to buy it.
120 Ω termination resistor1–2RS-485 termination at each end of the bus. The OPTA's onboard transceiver doesn't include a permanent terminator.
Shielded twisted pair, 22 AWG, RS-485 rated, ~1m1A → A, B → B, shield → drive ground. Length depends on the panel layout.
DIN rail, ~10 cm1Mount for the OPTA and expansion. The panel probably has one already.
note

Alternative hosts. The Arduino Opta RS485 and Arduino Opta WiFi also work, with no firmware changes. The Arduino Opta Lite has no onboard RS-485, so it won't work for this project.

The 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

Wiring: OPTA + Wireless for OPTA on DIN rail; RS-485 A/B/COM daisy to VFD with 120 Ω terminators at each end; antennas through cable glands; 24 VDC supply

warning

Safety. VFD cabinets contain hazardous voltages even when the control wiring is low-voltage. Installation must be done by qualified personnel following site lockout/tagout procedures, the VFD manufacturer's instructions, and applicable electrical codes. This reference design is read-only over Modbus. It never commands pump start/stop or speed.

  1. Mount and power. Snap the OPTA onto the DIN rail, then snap Blues Wireless for OPTA onto the OPTA's right-hand expansion port. Join the two with the supplied solderless AUX connector, which carries the I²C lines the Notecard talks over. Wire 24 VDC from the panel supply to the OPTA's + and - terminals. As the Wireless for OPTA Quickstart points out, USB-C doesn't power the OPTA's outputs, so use the 12–24 VDC supply for anything beyond bench testing. Jumper the OPTA's +24V to the expansion's +24V terminal so both share the same supply.
  2. Antennas. Thread an SMA-female bulkhead lead through a cable gland for the primary cellular antenna and screw it onto the first antenna port on Wireless for OPTA. If the layout allows, do the same for the diversity port, which improves LTE Cat-1 performance at sites with marginal signal. The bundled rubber-duck antennas are for bench testing only, so don't count on them inside a metal cabinet.
  3. Modbus bus. Wire the OPTA's RS-485 terminals to the VFD's communication port:
    • OPTA A (+) → VFD A (+)
    • OPTA B (-) → VFD B (-)
    • OPTA COM (RS-485 GND) → VFD communication ground (check the drive's manual: ABB ACS580 calls it AGND, Yaskawa GA500 calls it SC, and Danfoss FC-302 calls it Common).
    • Put a 120 Ω resistor across A/B at each end of the bus. With one OPTA and one drive, that's two terminators total (one at each device).
  4. Drive configuration. Configure the VFD as a Modbus RTU server (slave), with the OPTA as the Modbus client (master). The firmware ships with slave ID 1, baud rate 19200, 8 data bits, no parity, and 1 stop bit (8N1). Real drives vary, so match baud, parity, stop bits, and slave address to however the VFD is set up, and override the firmware's defaults with the modbus_* environment variables on Notehub. You'll find these settings in parameter group ABB 5800–5805, Yaskawa H5-xx, Danfoss 8-3x, or Schneider Comm-1.x.
  5. Bench validation. During first-light testing, splice the Mojo inline between the 24 VDC supply and the Wireless for OPTA power input so it can measure the per-session energy of the expansion + Notecard subsystem. The OPTA itself is line-powered, so it isn't part of the measurement.

6. Notehub Setup

  1. Create a project. Sign up at Notehub and create a project. Copy the ProductUID and paste it into firmware/vfd_pump_monitor/vfd_pump_monitor.ino as PRODUCT_UID.

  2. Claim the Notecard. Power up the panel. The Notecard associates itself with your project on its first cellular session.

  3. Create a Fleet per plant. Fleets group devices for shared configuration and routing. The natural unit here is one fleet per plant. Pumps in the same plant usually share a VFD vendor and register map, so fleet-level environment variables can say "this plant's pumps all run ABB drives at register 0x0103 for output frequency."

  4. Set environment variables. In Notehub, go to Projects → your project → Fleets → your fleet → Environment. The defaults below are reasonable starting points. Any value you set in Notehub overrides the firmware default on the device's next inbound sync, and the register-address variables let one firmware image work with all four common drive vendors without a recompile.

    VariableDefaultPurpose
    sample_minutes1Minutes between Modbus polls.
    report_minutes60Minutes between summary Notes (vfd_summary.qo).
    modbus_slave_id1Modbus server (slave) address of the drive.
    modbus_baud19200Bus baud rate. Must match the VFD configuration.
    modbus_paritynoneParity setting: none, even, or odd. Must match the VFD.
    modbus_stop_bits1Stop bits: 1 or 2. Must match the VFD.
    vfd_profiledemo_contiguousPlaceholder naming the register-map convention this firmware build targets. The shipped firmware only implements demo_contiguous. See Limitations for the production path.
    reg_freq259Holding-register address (wire-level, 0-based) for output frequency. Demo firmware assumes 0.01 Hz units.
    reg_current260Holding-register address for motor current. Demo firmware assumes 0.01 A units.
    reg_torque261Holding-register address for output torque. Demo firmware assumes % of nominal (signed 16-bit).
    reg_drive_temp262Holding-register address for drive heatsink temperature. Demo firmware assumes °C (signed 16-bit).
    reg_runtime_hours263Holding-register address for cumulative runtime hours. Demo firmware reads a single 16-bit register.
    reg_fault_code264Holding-register address for the active fault code (0 = no fault). This is not a fault history log. See Limitations.
    current_alarm_factor1.20Fires load_anomaly when hourly mean current exceeds the rolling baseline by this factor while running.
    transient_fault_window_hours4Window for transient-fault clustering.
    transient_fault_count3Number of distinct fault transitions within the window at or above which transient_faults fires.
    drive_temp_alarm_c75.0Drive heatsink °C above which drive_overtemp fires.
    expected_run_hours_per_day12.0Expected runtime per day. Runtime drifting > 25% above this fires runtime_drift.
    note

    VFD register-map gotchas. The defaults above are only examples. Real VFDs differ in 0-based vs 1-based addressing (Modicon "40001" notation vs raw), per-register scaling (current may be 0.1 A, 0.01 A, or % of rated), signedness (torque and temperature are often signed), 32-bit values that span two registers (runtime hours often does, with vendor-specific word order), and whether a register holds the active fault code or a fault history log. The shipped firmware reads six contiguous 16-bit registers with hardcoded scaling, so production deployments need a vendor-specific firmware build that handles all of this properly.

  5. Configure routes. Add one route for vfd_event.qo (alerts: low-volume, real-time delivery to a CMMS or on-call endpoint) and a second for vfd_summary.qo (long-term storage: batched delivery to an analytics/historian system). Because the two Notefiles are split at the source, each one can go to a different destination with a different urgency, and the routes don't need any filter logic.

7. Firmware Design

Everything lives in one sketch: firmware/vfd_pump_monitor/vfd_pump_monitor.ino.

Dependencies:

  • Arduino Mbed OS Opta Boards core (install it from the Arduino IDE Boards Manager).
  • Blues Wireless Notecard (the note-arduino library). Install it from the Arduino Library Manager or with arduino-cli lib install "Blues Wireless Notecard".
  • ArduinoModbus and ArduinoRS485 (official Arduino libraries, also from the Library Manager).

Modules

ResponsibilityWhere
Notecard configuration (hub.set, templates)notecardConfigure, defineTemplates
Environment-variable fetch + clampfetchEnvOverrides (clampU32, clampF)
Re-init bus / re-issue hub.set on env changeapplyModbusSerialIfChanged, applyHubSetIfChanged
Modbus polling of six registerspollVfd, modbusReadOne
Rolling hourly statsRollingStats struct, accumulate
Edge logic (load anomaly, transient faults, runtime drift, drive overtemp)evaluateRules (with frequency-binned baselines and edge-triggered alerts)
Outbound Note emission (summary + event)sendSummary, sendEvent
Periodic cycle scheduler (no sleep, line-powered)loop()

Sensor reading strategy

The firmware pulls all six holding registers in a single Modbus transaction (requestFrom with HOLDING_REGISTERS, quantity 6, starting at the reg_freq address). One transaction puts 25 bytes on the bus instead of 90 and takes roughly 4–6× less time than six separate reads, depending on how quickly the drive responds. This assumes the six registers are contiguous. On drives where they aren't, override the reg_* variables one by one and the firmware falls back to per-register reads.

The polling cadence is sample_minutes (1 minute by default). For each sample, accumulate updates the rolling hourly windows for current, frequency, torque, and drive temperature. It keeps samples taken while the pump is running (frequency > 1 Hz) apart from samples taken while it's stopped, because a hot drive at 0 Hz means something different from a hot drive under load.

Event payload design

The device writes to two template-backed Notefiles. Templates give the summary and alert records a stable schema, store them on the Notecard as fixed-length records instead of free-form JSON, and keep the on-wire payload small. That adds up at 24 summary Notes per pump per day over a multi-year deployment. Real cellular data usage depends on sync cadence, signal conditions, routing behavior, and how often events fire, so check it against Notehub usage data before a production rollout.

vfd_summary.qo (hourly):

{
  "file": "vfd_summary.qo",
  "body": {
    "f_hz_mean": 59.8, "f_hz_peak": 60.1,
    "i_a_mean": 12.4, "i_a_peak": 13.7,
    "trq_mean": 68, "trq_peak": 74,
    "drv_c_mean": 42, "drv_c_peak": 51,
    "run_min": 53, "stop_min": 7,
    "hrs_total": 18234,
    "fault_count_hour": 0,
    "last_fault": "0"
  }
}

vfd_event.qo (immediate, sync:true):

{
  "file": "vfd_event.qo",
  "body": {
    "alert": "load_anomaly",
    "f_hz": 0,
    "i_a": 0,
    "v1": 59.8,
    "v2": 14.9,
    "v3": 12.3,
    "fault_code": 0,
    "hrs_total": 18234
  }
}

The event template carries eight fixed fields. When a specific sample triggers the alert, f_hz and i_a hold that sample's values (a Modbus failure event, for example, captures the last-known sample state). When an hourly aggregate triggers it, those two fields are zero and the relevant aggregates go into three generic numeric slots, v1 / v2 / v3, whose meaning depends on the alert type:

Alertv1v2v3
load_anomalyhourly mean frequencyhourly mean currentbin baseline current
transient_faultsdistinct fault transitions in windowwindow hoursunused (0)
drive_overtemphourly peak heatsink °Chourly mean heatsink °Cunused (0)
runtime_driftobserved daily runtime hoursexpected daily runtime hoursunused (0)
modbus_unreachableunused (0)unused (0)unused (0)

A production build could rename these to alert-specific fields (for example i_a_mean and i_a_baseline), but that costs a separate template per alert. The v1/v2/v3 shape keeps one shared template for every alert type, and it's what the demo firmware emits.

Power and sync strategy

The OPTA and expansion are line-powered (24 VDC), so the goal here is bus and bandwidth efficiency, not MCU sleep. The Notecard runs in hub.set periodic mode with outbound:60 and inbound:120 (minutes). Summary Notes pile up in the on-device queue and go out together in one session every hour. Alert Notes set sync:true and go out as soon as a session can be established after the trigger, usually within ~15–60 seconds.

Retry and error handling

  • The firmware retries a failed Modbus read up to 3× per cycle. If all three fail, it skips that sample and emits a separate modbus_unreachable event Note. A NaN never shows up in a payload, since JSON has no valid NaN literal and templated Notes validate field types. The event is rate-limited to once an hour so nobody gets buried in alarms while the drive is powered off for service.
  • The env.get request goes through notecard.requestAndResponse(), and the firmware checks for both a NULL return and an err field on the response before trusting the values. The other requests (hub.set, note.template, note.add) go out once with sendRequest(), and the firmware ignores the true/false result. A request the Notecard rejects isn't retried, and the error only shows up in note-arduino's debug output on the serial port. For an alert, that means a lost Note. The rule's edge trigger is already set, so the same alert won't fire again until its condition clears and trips again (or, for modbus_unreachable, until the hourly rate limit lets it through).

Key code snippets

Once at boot, notecardConfigure() sets the Notecard up for periodic sync (defineTemplates() registers the templates for both Notefiles before it). The cadence comes from g_report_minutes, which setup() reads from the report_minutes environment variable first, so the defaults work out to outbound 60 and inbound 120 minutes:

J *req = notecard.newRequest("hub.set");
if (req) {
    JAddStringToObject(req, "product", PRODUCT_UID);
    JAddStringToObject(req, "mode", "periodic");
    JAddNumberToObject(req, "outbound", g_report_minutes);
    JAddNumberToObject(req, "inbound", g_report_minutes * 2);
    notecard.sendRequest(req);
    g_last_hubset_outbound = g_report_minutes;
}

Polling the VFD takes one Modbus transaction that reads all six contiguous holding registers, which then feed the hourly stats. pollVfd() makes up to three attempts and returns false if they all fail, which is what triggers modbus_unreachable:

for (uint8_t attempt = 0; attempt < MAX_TRIES; attempt++) {
    if (isContiguousMap() &&
        ModbusRTUClient.requestFrom(g_modbus_slave_id, HOLDING_REGISTERS, g_reg_freq, 6)) {
        out.frequency_hz   = ModbusRTUClient.read() / 100.0f;
        out.current_a      = ModbusRTUClient.read() / 100.0f;
        out.torque_pct     = (int16_t)ModbusRTUClient.read();
        out.drive_temp_c   = (int16_t)ModbusRTUClient.read();
        out.runtime_hours  = (uint32_t)(uint16_t)ModbusRTUClient.read();
        out.fault_code     = (uint16_t)ModbusRTUClient.read();
        out.valid = true;
        return true;
    }
    // ...
}
return false;

Then there's the load-anomaly rule. Rising current at a comparable output frequency is one of the things maintenance techs watch for. The firmware sorts samples into 5 Hz frequency bins and tracks an EWMA-smoothed baseline current for each bin, so 30 Hz operation never gets compared against a 60 Hz baseline. The rule only runs when the hour had more than five running samples. The first such hour in a bin seeds that bin's baseline. After that, the baseline only moves while the alert isn't tripping, 5% of the way toward each new hourly mean. The alert is edge-triggered. It fires once on the rising edge and rearms when the bin's mean drops back below the threshold:

if (run > 5 && f_hz_mean > 1.0f) {
    const uint8_t bin = freqBin(f_hz_mean);
    const float baseline = g_current_baseline_by_bin[bin];
    if (!g_baseline_seeded[bin]) {
        // Cold-start for this frequency bin: seed from the first valid hour
        // we observed running in this bin.
        g_current_baseline_by_bin[bin] = i_a_mean;
        g_baseline_seeded[bin] = true;
    } else if (i_a_mean > baseline * g_current_alarm_factor) {
        if (!g_active_load_anomaly) {
            sendEvent("load_anomaly", nullptr, f_hz_mean, i_a_mean, baseline);
            g_active_load_anomaly = true;
        }
    } else {
        // EWMA update on the bin's baseline (alpha = 0.05 over hourly
        // samples) and rearm if we were active.
        g_current_baseline_by_bin[bin] = 0.95f * baseline + 0.05f * i_a_mean;
        g_active_load_anomaly = false;
    }
}

8. Build and Flash

Prerequisites: Arduino IDE (or arduino-cli on the command line) with the following installed from the Boards Manager and Library Manager:

  • Boards: Arduino Mbed OS Opta Boards
  • Libraries: Blues Wireless Notecard, ArduinoModbus, ArduinoRS485

Steps:

  1. Clone or download the repo and open firmware/vfd_pump_monitor/vfd_pump_monitor.ino in the Arduino IDE.

  2. Replace the placeholder value of PRODUCT_UID at the top of the sketch (com.your-company.your-name:vfd_pump_monitor) with your Notehub ProductUID:

    #define PRODUCT_UID  "com.your-company.your-name:vfd_pump_monitor"

    (Find it in Notehub under Dashboard → Project Settings → ProductUID, or see Finding a ProductUID.)

  3. Via Arduino IDE: Select Tools → Board → Arduino Opta, select the correct COM/tty port, then Sketch → Upload.

  4. Via command line (arduino-cli):

    arduino-cli compile --fqbn arduino:mbed_opta:opta firmware/vfd_pump_monitor/
    arduino-cli upload -p /dev/ttyACM0 --fqbn arduino:mbed_opta:opta firmware/vfd_pump_monitor/

    (Replace /dev/ttyACM0 with your OPTA's serial port. On macOS it's usually something like /dev/cu.usbmodem*.)

note

The Finder OPTA Codesys ships running the CODESYS runtime. The first time you upload Arduino firmware to it, you have to put it into bootloader mode yourself by double-tapping its RESET button, as described in the Wireless for OPTA Quickstart. That upload replaces the CODESYS runtime, and later uploads don't need the double tap.

  1. Open the Arduino IDE Serial Monitor (115200 baud) to check that the sketch boots. The firmware turns on note-arduino's debug output, so you'll see each Notecard request and response (note.template, env.get, hub.set) go by.

9. Data Flow

Data flow: poll 6 holding registers → rolling hourly stats with 5 Hz frequency bins and EWMA baselines → four anomaly rules → vfd_event.qo (sync) and vfd_summary.qo (hourly) → Notehub

Collected. Every sample_minutes: output frequency, motor current, output torque, drive heatsink temperature, cumulative runtime hours, and the active fault code (only the live fault register, not the fault history log).

Summarized. Every report_minutes (hourly by default): the mean and peak of each scalar, separate run and stop minute counts, total runtime hours, the number of distinct fault transitions seen in the hour, and the last non-zero fault code.

Transmitted.

  • vfd_summary.qo: once per report_minutes (24 Notes per day by default), queued and shipped on the Notecard's hourly outbound sync.
  • vfd_event.qo: right away when a rule trips, with sync:true so it skips the outbound interval.

Routed. Notehub sends vfd_event.qo to whatever real-time channel the operator uses (CMMS tickets, on-call paging, Slack, and so on) and vfd_summary.qo to long-term storage for trend analysis.

Triggers. Four rules fire alerts:

  • load_anomaly: hourly mean current exceeds the rolling baseline by current_alarm_factor while the pump is running. Lots of things can cause this (bearing drag, fouling, valve position, viscosity, debris, some impeller conditions), so treat it as an anomaly worth investigating, not a specific failure mode.
  • transient_faults: transient_fault_count or more distinct fault transitions within transient_fault_window_hours. The rule counts 0 → nonzero and code-changed events, not the number of samples taken while a fault is asserted.
  • runtime_drift: observed daily runtime exceeds expected_run_hours_per_day by more than 25%.
  • drive_overtemp: heatsink temperature exceeds drive_temp_alarm_c.

10. Validation and Testing

On a healthy pump in steady state, expect one summary Note per hour and no event Notes. The Notecard's card.status and hub.status requests make good smoke tests during commissioning.

Modbus first-light. Before you connect to the real drive, run the firmware against a software Modbus simulator (Modbus Mechanic, ModRSsim2, or similar) through a USB-to-RS-485 adapter wired to the OPTA's RS-485 terminals. Check that the six register reads match what the simulator is publishing.

Power validation with Mojo. Here's Notecard's published current envelope, from the Notecard low-power-design docs and the NOTE-WBNAW datasheet:

PhaseExpected current
Notecard idle (radio off, between syncs)~8–18 µA @ 5V
Modem active (cellular session)~250 mA average, with ≤2 A bursts during GSM transmit
WiFi active (when WiFi fallback engaged)~80 mA average

Splice the Mojo inline on the Wireless for OPTA power input and confirm three things: idle current between syncs sits in the published µA range, per-session energy lands in the few-mAh range for an hourly outbound sync, and total energy per day stays consistent from run to run. Keep in mind that you're measuring the whole expansion subsystem: the Notecard plus the expansion's onboard regulators and I²C glue. You'll only see the Notecard by itself if you physically isolate its VMODEM_P rail. Notecard also only reaches its lowest-power state when VUSB is absent and AUX_EN isn't held high. The Notecard low-power-design docs cover those gating conditions.

Fault simulation. The easiest way to simulate a fault is to drop current_alarm_factor to 1.01 in the Fleet's environment variables. That's the lowest value the firmware accepts, and it ignores anything outside 1.01–5.0. The next inbound sync pulls the new value, and the next hourly summary trips load_anomaly if that hour's mean current runs more than 1% over the baseline for its frequency bin. The event lands in Notehub as soon as a session comes up.

11. Troubleshooting

Notecard not claiming to the project.

  • Check that PRODUCT_UID in the sketch exactly matches the ProductUID on Notehub (Projects → Project Settings).
  • Confirm cellular signal. In the Blues In-Browser Terminal, run card.wireless and check bars and net.rsrp. LTE Cat-1 needs at least -100 dBm.
  • If you're deploying indoors or in a metal cabinet without an external antenna, the bundled rubber-duck antenna won't work. Thread the external SMA antenna through a cable gland and screw it onto the primary antenna port.

Modbus reads failing (firmware logs [modbus] read failed or no data in summary Notes).

  • Check the RS-485 A/B/COM wiring. A and B are easy to swap, and the drive won't respond if they're reversed.
  • Make sure a 120 Ω termination resistor is present at both ends of the bus. With one OPTA and one drive, that's two resistors total (one at the OPTA, one at the drive).
  • Check the slave ID and baud rate. In Notehub's Fleet Environment, confirm modbus_slave_id and modbus_baud match the VFD's configuration. After the next sync, run env.get in the terminal to confirm the Notecard pulled the new values, and hub.sync.status to see when that sync finished. The firmware applies them at its next hourly summary.
  • Before connecting to the real drive, test with a USB-to-RS-485 adapter and a software Modbus simulator (Modbus Mechanic, ModRSsim2) wired to the OPTA's RS-485 terminals, and make sure the firmware reads all six registers correctly.

First event not appearing in Notehub after 60+ seconds.

  • Check Notehub's Events tab, where every Note (summary and alert) shows up. If it's empty, the firmware hasn't established a cellular session yet. Wait for the first outbound sync (60 minutes by default) or power-cycle the OPTA to force a session sooner.
  • Confirm the Notecard has cellular coverage (see the card.wireless check above).
  • If you need a test alert, drop current_alarm_factor to 1.01 (its minimum) in the Fleet Environment. Once a sync pulls the new threshold, the next hourly summary can trigger load_anomaly (see Fault simulation above). The alert posts as soon as a session comes up, typically 15–60 seconds after that summary.

Environment variables not taking effect.

  • The Notecard fetches environment variables at least once per inbound sync interval (120 minutes by default), and the firmware reads them at its next hourly summary. To force an immediate fetch, power-cycle the OPTA, or set the _sync_inbound_mins environment variable to 1 while you're commissioning.
  • Make sure the variables are set on the correct Fleet, not at the project level. In Notehub, that's Projects → Project Name → Fleets → Fleet Name → Environment.

Antenna placement considerations.

  • The rubber-duck antennas that come with Wireless for OPTA are for bench testing only. They won't hold reliable LTE Cat-1 coverage inside a metal pump cabinet.
  • Route the primary antenna outside the cabinet through a cable gland. If the layout allows, route the diversity antenna (second SMA port) outside too, for better performance where signal is marginal.
  • Keep the primary antenna at least 2 m away from high-power electrical equipment (motor starters, VFDs) to cut down on RF interference.

12. Limitations and Next Steps

A reference design meant for thousands of different drive configurations has to be clear about what it does and doesn't claim. The biggest question is what VFD telemetry can actually tell you. Everything else is the usual list of scope choices that get refined once a real fleet starts deploying.

Signal limitations. VFD telemetry is valuable, but it isn't a substitute for full pump instrumentation. Current, torque, speed, temperature, and active fault codes can spot abnormal patterns. Without context like suction/discharge pressure, flow, vibration, or a known duty cycle, though, they can't reliably tell a worn impeller from a clogged suction strainer, a closed discharge valve, cavitation, bearing drag, increased fluid viscosity, debris, or a process change. Treat every alert this firmware sends as an early maintenance indicator, not a diagnosis.

Simplified for this reference design

Beyond the signal limits above, the firmware deliberately keeps its scope narrow in a few places. A real fleet deployment will extend the design at each of them.

Vendor-specific register addresses, scaling, signedness, and word counts. The defaults describe a made-up contiguous map. Each VFD vendor publishes its own Modbus map, so commissioning a real plant means looking up the actual addresses, scaling factors (current may be 0.1 A, 0.01 A, or % of rated), signedness (torque and temperature are often signed), word counts (runtime hours are often a 32-bit value across two registers with vendor-specific word order), and addressing convention (0-based wire-level vs. 1-based / Modicon "40001" notation). The shipped firmware reads six contiguous 16-bit registers with hardcoded scaling, so production builds need vendor-specific firmware, one build per vfd_profile.

Active fault code only, not fault history log. The firmware reads the active-fault register once per cycle. Reading a vendor-specific fault log, which is usually a multi-register block with circular-buffer semantics, is left for a future version.

Single drive per OPTA. The firmware reads one slave ID. A pump room with N drives needs either N OPTAs or a firmware extension that round-robins across slave IDs on the same RS-485 bus.

Heuristic thresholds. Four threshold-based rules catch common load-anomaly, transient-fault, runtime-drift, and overtemp patterns. They won't catch every failure mode, and they'll sometimes fire on harmless conditions like a seasonally hotter room or a deliberate process change. In production, tune the thresholds for each pump after a baseline period.

No Modbus writes. The firmware only reads. Writing setpoints to the drive (start/stop, speed reference, and so on) is out of scope on purpose. That's a different safety conversation, one that involves E-stop wiring, lockout/tagout, and functional safety certification.

No host firmware updates wired up. Notecard Outboard Firmware Update supports STM32H7 (the OPTA's MCU family), but it needs AUX wiring that Blues Wireless for OPTA doesn't break out today. For now, host firmware updates are local-only via USB-C.

Production Next Steps

Once a real fleet starts deploying, the scope choices above turn into the roadmap. Here they are, roughly from most immediately useful to most infrastructure-dependent.

Vendor-specific vfd_profile builds come first: ABB ACS580, Yaskawa GA500, Danfoss FC-302, and Schneider ATV, each with the right register addresses, scaling, signedness, and word handling baked in.

Per-pump baseline learning makes detection a lot sharper. With rolling 30-day current-vs-frequency tables stored in flash, the firmware can trigger load_anomaly against the learned curve instead of a static factor.

Fault history log readout captures the events leading up to a trip, not just the active fault. It's vendor-specific and usually spans multiple registers with timestamps.

Multi-drive support through round-robin polling and per-slave-ID summaries lets a single OPTA cover an entire pump room.

A vfd_command.qi inbound Notefile would let a service tech request a diagnostic dump on demand.

Wiring ODFU to the OPTA's BOOT/RESET pins is the furthest out. Once the hardware path exists, it enables over-the-air host updates across the fleet.

13. Summary

The maintenance team that used to find out about a struggling pump from a dry tank on a Saturday night now gets a CMMS ticket on Tuesday afternoon about a load anomaly, a creeping drive temperature, or a cluster of transient faults. Six Modbus registers, one cellular session an hour, and four anomaly rules turn telemetry the VFD already had into early warnings. A plant electrician can clip the device onto a DIN rail in fifteen minutes, and it never touches the corporate network.

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
    • Power and sync strategy
    • Retry and error handling
    • Key code snippets
  • 8. Build and Flash
  • 9. Data Flow
  • 10. Validation and Testing
  • 11. Troubleshooting
  • 12. Limitations and Next Steps
    • Simplified for this reference design
    • Production Next Steps
  • 13. Summary

Resources

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