Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions 55-demand-response-solar-battery-dispatcher/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ This reference application is intended to provide inspiration and help you get s

</Note>

This project is an [energy savings](https://blues.com/energy-savings/) reference design that gives a commercial solar + battery installation an independent cellular control channel, so the asset owner can dispatch the battery to discharge during expensive peak-rate hours, charge during cheap overnight hours, and curtail grid export when the utility calls a **demand-response** (**DR**) event, without depending on the building's IT network or a vendor's proprietary cloud portal. The device reads live operating state from the inverter and the battery's management system over the industrial bus they already share, and signals each one to enter the right mode at the right time — driven by a schedule or by live commands routed through Notehub.
This project is an [energy savings](https://blues.com/energy-savings/) reference design that gives a commercial solar + battery installation an independent cellular control channel, so the asset owner can dispatch the battery to discharge during expensive peak-rate hours, charge during cheap overnight hours, and curtail grid export when the utility calls a demand-response (DR) event, without depending on the building's IT network or a vendor's proprietary cloud portal. The device reads live operating state from the inverter and the battery's management system (BMS) over the industrial bus they already share, and signals each one to enter the right mode at the right time — driven by a schedule or by live commands routed through the Blues cloud service, Notehub.

**Scope.** This is a *mode enable / curtail* controller, not a power dispatcher. It closes and opens four dry-contact relays wired to digital control inputs the inverter and battery already expose; the field devices' own firmware then decides the actual charge, discharge, and export behavior — ramp rates, power limits, setpoints — based on their pre-commissioning configuration. The controller does not command power setpoints, verify that a requested dispatch profile was executed, or close any control loop around site load.

## 1. Project Overview

**The problem.** Commercial solar-plus-storage systems are a compelling economics story on paper: sell excess solar generation at peak rates, cover load from the battery so you don't draw from the grid during expensive peak windows, and participate in utility DR programs that pay you to curtail export or reduce load on short notice. The value is real, but realizing it requires a reliable, low-latency control channel between the utility signal and the physical system sitting on the roof or in the equipment yard.

That's where most deployments hit the wall. The inverter and **battery management system** (**BMS**) speak Modbus — an industrial serial protocol developed in 1979 that remains the lingua franca of commercial power electronics, and that bus sits in an outdoor equipment cabinet or a rooftop shed with no direct connection to the building's IT network. Even when WiFi does reach the equipment, the solar asset is often owned by a third-party **PPA** (power purchase agreement) provider who has no credentials on the building's network and no relationship with the building's IT team. The control channel that does exist — the inverter's local display, a proprietary cloud portal, or a laptop plugged in at the cabinet — isn't a control channel you can reliably automate, audit, or integrate with a utility DR program.
That's where most deployments hit the wall. The inverter and BMS speak Modbus — an industrial serial protocol developed in 1979 that remains the lingua franca of commercial power electronics, and that bus sits in an outdoor equipment cabinet or a rooftop shed with no direct connection to the building's IT network. Even when WiFi does reach the equipment, the solar asset is often owned by a third-party power purchase agreement provider who has no credentials on the building's network and no relationship with the building's IT team. The control channel that does exist — the inverter's local display, a proprietary cloud portal, or a laptop plugged in at the cabinet — isn't a control channel you can reliably automate, audit, or integrate with a utility DR program.

This project closes that gap. A Blues Wireless for OPTA expansion snapped onto an Arduino OPTA RS485 **PLC** (programmable logic controller) sits on the DIN rail inside the equipment cabinet. The OPTA reads the inverter and BMS over the RS-485 Modbus bus already present in every commercial solar installation, while the Notecard inside the expansion provides a cellular uplink that is completely independent of the building network and requires no IT coordination. When the utility issues a DR event, or when the local time-of-use (**TOU**) peak window opens, the cloud-side system routes a dispatch command to the device through Notehub. The OPTA closes or opens four relay outputs grid export enable, battery discharge, battery charge, and a DR-active indicator, typically within one to six minutes of the command arriving at Notehub (bounded by the 5-minute inbound sync cadence plus the next 1-minute sample cycle), then reports what it did and why.
This project closes that gap. A [Blues Wireless for OPTA](https://blues.com/products/wireless-for-opta/) expansion snapped onto an Arduino OPTA RS485 PLC (programmable logic controller) sits on the DIN rail inside the equipment cabinet. The OPTA reads the inverter and BMS over the RS-485 Modbus bus already present in every commercial solar installation, while the Notecard inside the Blues expansion provides a cellular uplink that is completely independent of the building network and requires no IT coordination. When the utility issues a DR event, or when the local time-of-use peak window opens, the cloud-side system routes a dispatch command to the device through Notehub. The OPTA closes or opens four relay outputs: grid export enable, battery discharge, battery charge, and a DR-active indicator, typically within one to six minutes of the command arriving at Notehub (bounded by the 5-minute inbound sync cadence plus the next 1-minute sample cycle), then reports what it did and why.

**Why Notecard.** The inverter and battery sit outdoors or in a rooftop equipment yard where WiFi is poor. More fundamentally, the solar asset is often owned by a PPA provider who doesn't have access to the building's network and can't request VPN credentials from a tenant's IT department. Cellular gives the asset owner an independent control channel that works identically whether the building is a strip mall, a warehouse, or a multi-tenant office park — no network forms, no AP to pair to, and no IT ticket to chase. That independence is the whole point: the asset owner needs to be able to send a discharge command at 4:57 PM regardless of what the building's network is doing.

Expand All @@ -38,7 +38,7 @@ This project closes that gap. A Blues Wireless for OPTA expansion snapped onto a

**Device-side responsibilities.** Inside the equipment cabinet, the OPTA's Cortex-M7 plays Modbus RTU **client** (master) to the inverter and BMS — the Modbus **servers** / slaves — polling four holding registers from each across the onboard RS-485 transceiver once a minute. Every cycle the host figures out which operating mode applies — `normal`, `peak_discharge`, `overnight_charge`, `dr_curtail`, or `low_soc_protect` — checks live SOC against the configured guards, and drives the four relays accordingly. It also peeks at the `dispatch.qi` [inbound Notefile](https://dev.blues.io/notecard/notecard-walkthrough/inbound-requests-and-shared-data/) on every cycle for anything the cloud has queued. Outbound telemetry and mode-change events ride to the Notecard over I²C through the Wireless for OPTA expansion's AUX connector — no modem AT commands, no serial buffers, no session state machine.

**[Notecard](https://shop.blues.com/products/notecard-cellular?utm_source=dev-blues&utm_medium=web&utm_campaign=store-link) responsibilities.** The Notecard holds the [Notes](https://dev.blues.io/api-reference/glossary/#note) the host writes, ships them on the configured [`hub.set`](https://dev.blues.io/api-reference/notecard-api/hub-requests/#hub-set) `periodic` outbound cadence, and checks Notehub for inbound `dispatch.qi` commands every five minutes. That five-minute window is what keeps dispatch latency comfortably inside the ten-minute response envelope most utility DR programs require. The same module pulls [environment variables](https://dev.blues.io/guides-and-tutorials/notecard-guides/understanding-environment-variables/) on each inbound sync, which is how a fleet operator retunes the TOU schedule, SOC thresholds, or Modbus addresses from a browser without ever opening the cabinet.
**[Notecard](https://shop.blues.com/products/notecard-cellular?utm_source=dev-blues&utm_medium=web&utm_campaign=store-link) responsibilities.** The Notecard queues the [Notes](https://dev.blues.io/api-reference/glossary/#note) (the JSON-based events), ships them on the configured [`hub.set`](https://dev.blues.io/api-reference/notecard-api/hub-requests/#hub-set) `periodic` outbound cadence, and checks Notehub for inbound `dispatch.qi` commands every five minutes. That five-minute window is what keeps dispatch latency comfortably inside the ten-minute response envelope most utility DR programs require. The same module pulls [environment variables](https://dev.blues.io/guides-and-tutorials/notecard-guides/understanding-environment-variables/) on each inbound sync, which is how a fleet operator re-tunes the TOU schedule, SOC thresholds, or Modbus addresses from a browser without ever opening the cabinet.

**Notehub responsibilities.** Events flow from the Notecard's embedded global SIM into [Notehub](https://notehub.io), which timestamps and stores every one and applies project-level [routes](https://dev.blues.io/notehub/notehub-walkthrough/#routing-data-with-notehub). On the cloud side, whatever orchestrates dispatch — a utility rate scheduler, an energy management system, a webhook bridge to the utility DR API — queues commands by posting a Note to the device's `dispatch.qi` Notefile via the [Notehub API](https://dev.blues.io/api-reference/notehub-api/api-introduction/); the Notecard pulls it in on the next inbound sync. [Environment variables](https://dev.blues.io/guides-and-tutorials/notecard-guides/understanding-environment-variables/) set at the [Fleet](https://dev.blues.io/guides-and-tutorials/fleet-admin-guide/) level carry the site's TOU schedule so the device can act autonomously — provided the Notecard has acquired a valid UTC time from at least one prior sync. On cold boot before that happens, `resolveMode()` sees `epoch = 0` and stays in `normal` rather than misapplying the schedule; TOU windows engage as soon as the clock is available.

Expand Down Expand Up @@ -133,7 +133,11 @@ The Blues hardware ships with an active SIM including 500 MB of data and 10 year

4. **Modbus device configuration.** Configure the inverter as Modbus slave ID `1` (default `modbus_slave_inv`), the BMS as slave ID `2` (default `modbus_slave_bms`). Use the `modbus_baud`, `modbus_parity`, and `modbus_stop_bits` environment variables to match the serial configuration required by your hardware (firmware default: 9600 baud, no parity, 1 stop bit, 9600 8N1). Consult the Modbus communication or RS-485 configuration section in the inverter and BMS vendor manuals to identify the correct slave IDs and serial settings for each device.

> **Hard constraint — single shared bus.** The firmware uses one RS-485 physical segment with **one set of serial parameters** (`modbus_baud`, `modbus_parity`, `modbus_stop_bits`) applied to both the inverter and the BMS. Both devices must be configured to identical baud rate, parity, and stop bits — they are polled sequentially on the same electrical bus. This is a commissioning prerequisite, not just a configuration choice: if your inverter and BMS ship with different default serial settings and cannot both be reconfigured to a common setting, this reference topology is unworkable as-is. In that case you would need a second RS-485 port or a USB-to-RS-485 adapter on a second UART, plus matching firmware changes to address each device on its own serial port. Verify serial-setting compatibility between your chosen inverter and BMS before committing to this wiring layout.
<Note>

**Hard constraint — single shared bus.** The firmware uses one RS-485 physical segment with **one set of serial parameters** (`modbus_baud`, `modbus_parity`, `modbus_stop_bits`) applied to both the inverter and the BMS. Both devices must be configured to identical baud rate, parity, and stop bits — they are polled sequentially on the same electrical bus. This is a commissioning prerequisite, not just a configuration choice: if your inverter and BMS ship with different default serial settings and cannot both be reconfigured to a common setting, this reference topology is unworkable as-is. In that case you would need a second RS-485 port or a USB-to-RS-485 adapter on a second UART, plus matching firmware changes to address each device on its own serial port. Verify serial-setting compatibility between your chosen inverter and BMS before committing to this wiring layout.

</Note>

### Relay output wiring

Expand Down Expand Up @@ -232,7 +236,7 @@ Five files in [`firmware/solar_battery_dispatcher/`](firmware/solar_battery_disp
| [`mode_helpers.cpp`](firmware/solar_battery_dispatcher/mode_helpers.cpp) | UTC time utilities, mode resolution (`resolveMode`), relay output control (`applyRelays`), and outbound Note emission (`sendTelemetry`, `sendModeEvent`) |

Dependencies:
- **Arduino Mbed OS Opta Boards** core (install via the Arduino IDE Boards Manager, search "opta").
- Arduino Mbed OS Opta Boards core (install via the Arduino IDE Boards Manager, search "opta").
- [`Blues Wireless Notecard`](https://github.com/blues/note-arduino) (`note-arduino` library). Install via the Arduino Library Manager or `arduino-cli lib install "Blues Wireless Notecard"`. See [note-arduino releases](https://github.com/blues/note-arduino/releases) for the latest.
- [`ArduinoModbus`](https://github.com/arduino-libraries/ArduinoModbus) and [`ArduinoRS485`](https://github.com/arduino-libraries/ArduinoRS485) — official Arduino libraries, install via Library Manager.

Expand Down Expand Up @@ -298,7 +302,7 @@ When a Modbus device becomes unreachable (inverter or BMS), all fields for that

Downstream consumers can filter on `-9999` to detect and alert on communication loss.

`dr_event.qo` is an untemplated Note emitted with `sync:true` on every mode transition — when `peak_discharge` kicks in at the start of the TOU peak window, or when a DR curtailment arrives from the cloud, or when a command expires and the system returns to normal. The immediate uplink ensures operators and downstream systems see the transition in under a minute. Example:
`dr_event.qo` is a Note emitted with `sync:true` on every mode transition — when `peak_discharge` kicks in at the start of the TOU peak window, or when a DR curtailment arrives from the cloud, or when a command expires and the system returns to normal. The immediate uplink ensures operators and downstream systems see the transition in under a minute. Example:

```json
{
Expand Down
Loading
Loading