Use AI to Talk to Your Products with Notehub IQ and Attend a Live Demo on August 19th

Blues Developers
What’s New
Resources
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.io
Shop
Docs
Button IconHelp
Support DocsNotehub StatusVisit our Forum
Button IconSign In
Docs Home
What’s New
Resources
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.io
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 and 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_rightDocschevron_rightExample Appschevron_rightSampleschevron_rightControlling Device State and Receiving Acknowledgment

Controlling Device State and Receiving Acknowledgment

Introduction

This sample app demonstrates how to control a device from the cloud by storing the device's state in a DB Notefile. A web app writes the state it wants the device to have, the device applies that state, and the device then acknowledges the change by reporting the state it actually has — giving the web app a reliable way to show pending and completed changes.

An example of controlling device state in a web app

Wireless Connectivity with Blues

This sample app is built around the Blues Notecard and Blues Notehub.

The Blues Notecard is the easiest way for developers to add secure, robust, and affordable pre-paid wireless connectivity to their microcontroller or single-board computer of choice. Notecard is a System-on-Module (SoM) that combines pre-paid data, low-power hardware (~8μA-18μA when idle), and secure communications. It acts as a device-to-cloud data pump to communicate with the Blues cloud service Notehub.

Notehub is the Blues cloud service for routing Notecard-provided data to third-party cloud applications, deploying OTA firmware updates, and securely managing fleets of Notecards. Notehub allows for secure communications between edge devices and the cloud without certificate management or manual provisioning of devices.

General Information

System Hardware

You can use any Notecard and any host microcontroller to implement this article's pattern. However, this example will demonstrate using the following hardware.

ComponentPurpose
Blues Notecard Cellular WBGLWTWireless connectivity module enabling device-to-cloud data syncing.
Blues Notecarrier FCarrier board for connecting Notecard to an MCU.
Blues SwanExample host MCU.

List of Acronyms

AcronymDefinition
MCUMicrocontroller
SoMSystem-on-Module

Summary

An LED that a web app can turn on and off is device state: it has one current value, and both the device and the cloud need to read and change it. The hard part is that a cloud app can't just assume its changes took effect—the device might be asleep, offline, or mid-sync—so the app needs a way to know the state the device actually has, not just the state it last asked for.

The Notecard's DB Notefiles are built for exactly this. A DB Notefile is a database of Notes that is bidirectionally replicated between a Notecard and Notehub, where each Note has a name and a JSON body, and both sides can read and write it.

This sample stores the LED's state as two Notes in the Notecard's default vars.db Notefile, using the var requests:

NoteWritten byMeaning
led-desiredThe web appThe state the user wants the LED to have
led-reportedThe firmwareThe state the LED actually has — the device's acknowledgment

The flow works like this:

  1. The user flips a switch in the web app, which writes led-desired through the Notehub API and shows a pending indicator.
  2. The change syncs to the Notecard, and the firmware notices that led-desired no longer matches the LED.
  3. The firmware changes the LED, then acknowledges the change by writing the new state to led-reported, which syncs back to Notehub.
  4. The web app sees that led-reported now matches what it asked for — the acknowledgment — and clears the pending indicator.

Because each Note is written by exactly one side, the two sides never edit the same Note concurrently, so synchronization conflicts never come into play. And because the acknowledgment is the device's current state rather than a stream of one-off messages, any number of dashboards can control the same device and all of them converge on the truth.

Requirements

  1. Host firmware that can:

    1. Turn on and off an LED (the host's onboard LED is fine).

    2. Read and write variables in a DB Notefile.

  2. A web or mobile application that reads and writes the same variables using the Notehub API.

Technical Implementation

The full source code for this example is available on GitHub.

tip

Let AI write your firmware. Blues Expert MCP connects your AI coding assistant (Claude Code, GitHub Copilot, Cursor) directly to our API docs, providing live request validation and firmware best practices for Arduino, C, Zephyr, and Python. Install the Blues Expert MCP →

Firmware

This section covers the most important aspects of writing firmware that keeps a device's state in sync with the cloud. The firmware for this example uses the Arduino Notecard library, although you can implement this pattern using any of the Notecard firmware libraries.

hub.set

The hub.set request controls how a Notecard connects to Notehub. This example's firmware uses the following configuration.

J *req = notecard.newRequest("hub.set");
JAddStringToObject(req, "product", productUID);
JAddStringToObject(req, "mode", "continuous");
JAddBoolToObject(req, "sync", true);
if (!notecard.sendRequest(req)) {
  JDelete(req);
}
  • The "mode": "continuous" option places the Notecard in continuous mode, which has it maintain a constant network connection with Notehub.
  • The "sync": true option ensures changes made in Notehub — including changes to DB Notefiles like vars.db — sync to the Notecard as soon as Notehub detects them.

This configuration ensures the device will receive state changes as soon as possible, which is ideal for any device that needs to quickly respond to its users. However, this configuration does mean the device will use considerably more battery to maintain the connection to Notehub than it would with a periodic connection.

You can use this sample's pattern with devices in periodic mode. However, when in periodic mode your Notecard will not send/receive instantly, and instead those intervals will be determined by your configured inbound and outbound options.

Initializing the State on Startup

The firmware stores its state in two variables that live in the Notecard's default vars.db DB Notefile: led-desired (the state the user wants, written by the web app) and led-reported (the state the LED actually has, written by the firmware). At the end of setup(), the firmware makes sure both variables exist, and then forces an immediate sync so they appear in Notehub right away.

void setup()
{
  // ...Notecard and LED initialization shown above...

  // Make sure both state variables exist: led-desired for the web app
  // to write, and led-reported for the web app to read.
  initializeLedDesired();
  reportLedState();

  // Sync with Notehub immediately, so the variables appear there right
  // away rather than waiting for the next periodic sync.
  J *req = notecard.newRequest("hub.sync");
  notecard.sendRequest(req);
}

The initializeLedDesired() function creates led-desired with a default value if it doesn't exist yet (the {note-noexist} error), so the variable is always available for the web app to read and update. (reportLedState() is covered in Acknowledging Changes below.)

// Create the led-desired variable with a default value if it doesn't
// exist yet.
void initializeLedDesired()
{
  J *req = notecard.newRequest("var.get");
  JAddStringToObject(req, "name", "led-desired");

  J *rsp = notecard.requestAndResponse(req);
  if (notecard.responseError(rsp) &&
      NoteResponseErrorContains(rsp, "{note-noexist}")) {
    J *set = notecard.newRequest("var.set");
    JAddStringToObject(set, "name", "led-desired");
    JAddBoolToObject(set, "flag", false);
    notecard.sendRequest(set);
  }
  notecard.deleteResponse(rsp);
}

Reading the Desired State

To check what state the user currently wants, the firmware calls the var.get request in a loop.

bool ledState = false;

void loop()
{
  J *req = notecard.newRequest("var.get");
  JAddStringToObject(req, "name", "led-desired");

  J *rsp = notecard.requestAndResponse(req);
  if (!notecard.responseError(rsp)) {
    bool desired = JGetBool(rsp, "flag");
    if (desired != ledState) {
      notecard.logDebug(desired ? "Turning light on\n" : "Turning light off\n");
      digitalWrite(LED_BUILTIN, desired ? HIGH : LOW);
      ledState = desired;
      reportLedState();
    }
  }
  notecard.deleteResponse(rsp);

  // Wait one second before checking again.
  delay(1000);
}

A few things worth noting in this code:

  • The firmware only acts when the desired state differs from the LED's actual state, so processing the same value repeatedly is harmless.
  • Because the firmware reads a current value rather than consuming a queue of commands, a device that was offline doesn't replay every change it missed — it simply applies the latest state.
note

Polling var.get once a second is simple and works well for a device that's already in continuous mode. For battery-powered designs, the Notecard's attention pin can instead interrupt the host when a watched Notefile changes. See the Attention Pin Guide for details.

Acknowledging Changes

After changing the LED, the firmware acknowledges the change by writing the state the device actually has to a second variable, led-reported, using the var.set request.

void reportLedState()
{
  J *req = notecard.newRequest("var.set");
  JAddStringToObject(req, "name", "led-reported");
  JAddBoolToObject(req, "flag", ledState);
  JAddBoolToObject(req, "sync", true);
  notecard.sendRequest(req);
}

The request's "sync": true option tells the Notecard to synchronize the change to Notehub immediately, rather than waiting for the next outbound sync interval, so the web app learns about the change as quickly as possible.

Keeping led-desired and led-reported as two separate Notes is deliberate: the web app only ever writes led-desired, and the firmware only ever writes led-reported. Because each Note has a single writer, the device and the cloud never change the same Note at the same time, and synchronization conflicts can't occur.

Testing the Firmware

To test that the firmware is working correctly, change the led-desired variable in Notehub. The easiest way is through the Notefile explorer: open your device in Notehub, go to the Notefiles tab, open vars.db, and edit the led-desired Note's body to {"flag": true}.

Editing vars.db Notes in Notehub

tip

You can also make the same change with the Update DB Note Notehub API request.

curl -X PUT
    -L 'https://api.notefile.net/v1/projects/<projectUID>/devices/<deviceUID>/notes/vars.db/led-desired'
    -H 'Authorization: Bearer <access_token>'
    -d '{"body": {"flag": true}}'

You should see your microcontroller's LED turn on, and moments later the led-reported Note in the Notefile explorer should read {"flag": true}, marked as last updated by Notecard.

Result of vars.db update in Notehub

note

If you repeat this test to turn the LED off, you'll see led-reported's body become {} rather than {"flag": false}. This is expected: the Notecard omits empty values, including false booleans, when it serializes a variable—so an empty body is how var.set represents a false flag. Any code that reads variables needs to treat a missing flag as false, as the web app below does.

Web Application

This section covers the most important aspects of writing user interfaces that control device state. The web application for this example is a Next.js app that uses Notehub JS to communicate with Notehub.

Writing the Desired State

The main task of the user interface is to let the user change the device's state. To do so the example application uses a Switch component. When the user toggles the Switch, the UI sends the new desired state to the API route and shows a pending indicator.

<Switch
  onChange={updateLed}
  value={ledState}
  disabled={isPending}
  loading={isPending}
/>
const [isPending, setIsPending] = React.useState(false);
const [ledState, setLedState] = React.useState(false);

const updateLed = (checked: boolean) => {
  setLedState(checked);
  setIsPending(true);

  fetch("/api/led", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ flag: checked }),
  }).catch(console.error);
};

Receiving the Acknowledgment

To know when the device has actually applied a change, and to stay accurate when the state changes some other way, the UI polls the API route (and through it, the led-reported Note) every few seconds.

const getReportedState = () => {
  fetch("/api/led", { cache: "no-store" })
    .then((response) => response.json())
    .then((data) => {
      if (isPending) {
        // Waiting on the device: clear the pending indicator once the
        // device reports the state the user asked for.
        if (data.flag === ledState) {
          setIsPending(false);
        }
      } else {
        // Not waiting on anything: show whatever the device reports.
        setLedState(data.flag);
      }
    })
    .catch(console.error);
};

React.useEffect(() => {
  getReportedState();
  const intervalId = setInterval(getReportedState, POLL_INTERVAL_MS);
  return () => clearInterval(intervalId);
}, [ledState, isPending]);

This is also what keeps multiple interfaces honest. If two dashboards control the same device, or someone flips the LED with a physical button on the device itself, every UI polling led-reported converges on the device's actual state.

Expected Results

And with that, you should be good to go! Try running the web application, and you should see the switch change to a disabled and pending state, and become reenabled seconds later when the device reports that the change was applied.

An example of controlling device state in a web app

In your Notehub project's Notefile explorer, you should see the led-desired and led-reported Notes update every time you flip the switch.

Potential Issues

This example, while powerful, does have a few limitations.

One-Shot Commands

DB Notefiles represent state—a current value. If you need to tell a device to perform an action, especially one that isn't safe to repeat or skip (reboot, dispense an item, run a calibration), an inbound queue (.qi) is the right tool, because each command Note is delivered and consumed exactly once. See the Remote Command and Control guide for that pattern.

Error Handling

If the device can fail to apply a state change, the firmware can report that too—for example by writing error details into the Notefile with a note.update request that gives led-reported a richer body, such as {"flag":false,"error":"led-driver-fault"}. Your interface can then surface the error rather than waiting on a pending indicator that will never clear.

Polling

Both sides of this example poll: the firmware calls var.get once a second, and the web app calls the Notehub API every few seconds. That's simple and fine for a demo, but for production you may want the firmware to use the attention pin instead of polling, and the web app to use Notehub routes to push state changes to your backend rather than polling for them.

Additional Resources

  • Source code - The source code for this example.
  • Storing Device State with DB Notefiles - A guide to managing device state with DB Notefiles and the var API.
  • Remote Command and Control - A guide to sending one-shot commands to a Notecard from the cloud.
  • Notehub JS - The official library for working with the Notehub API in JavaScript.
  • note-arduino - The official Arduino library for communicating with the Notecard over serial or I2C.
Can we improve this page? Send us feedback
© 2026 Blues Inc.
© 2026 Blues Inc.
TermsPrivacy