Scaling an IoT deployment? Join our webinar on May 28th where we dive into real-world scaling pain points and how to overcome them.

Blues Developers
What’s New
Resources
Blog
Technical articles for developers
Newsletter
The monthly Blues developer newsletter
Terminal
Connect to a Notecard in your browser
Developer Certification
Get certified on wireless connectivity with Blues
Webinars
Listing of Blues technical webinars
Blues.comNotehub.io
Shop
Docs
Button IconHelp
Notehub StatusVisit our Forum
Button IconSign In
Sign In
Sign In
Docs Home
What’s New
Resources
Blog
Technical articles for developers
Newsletter
The monthly Blues developer newsletter
Terminal
Connect to a Notecard in your browser
Developer Certification
Get certified on wireless connectivity with Blues
Webinars
Listing of Blues technical webinars
Blues.comNotehub.io
Shop
Docs
Guides & Tutorials
Collecting Sensor Data
Routing Data to Cloud
Building Edge ML Applications
Best Practices for Production-Ready Projects
Twilio SMS Guide
Fleet Admin Guide
Using the Notehub API
Notecard Guides
Guide Listing
Asset Tracking
Attention Pin Guide
Connecting to a Wi-Fi Access Point
Debugging with the FTDI Debug Cable
Diagnosing Cellular Connectivity Issues
Diagnosing GPS Issues
Encrypting and Decrypting Data with the Notecard
Feather MCU Low Power Management
Minimizing Latency
Notecard Communication Without a Library
Recovering a Bricked Notecard
Remote Command and Control
Sending Commands to DevicesReceiving Commands on a DeviceReceiving Commands on a Host
Sending and Receiving Large Binary Objects
Serial-Over-I2C Protocol
Understanding Environment Variables
Understanding Notecard Penalty Boxes
Updating ESP32 Host Firmware
Using External SIM Cards
Using JSONata to Transform JSON
homechevron_rightDocschevron_rightGuides & Tutorialschevron_rightNotecard Guideschevron_rightRemote Command and Control

Remote Command and Control

The Notecard's communication with its cloud backend, Notehub, is bidirectional. This means that in addition to sending local data from the Notecard to the cloud, you can also send data or commands in the opposite direction (cloud to device) using a pattern we call command and control.

Because the Notecard is a generic data pump, the commands you send to your devices can be as simple or as complex as you need them to be. For example you could tell a light to turn off, a robot to move left, or a machine to stop operating.

The Notecard allows you to send these commands remotely, giving you the ability to perform actions on devices located anywhere in cellular range. Let's look at how it works.

Sending Commands to Devices

The easiest way to send a command to a device is with the Notehub API's note.add request.

note

If you're new to the Notehub API, you'll want to read our Notehub API tutorial before continuing, as the tutorial shows you the basics of using the API, including how to generate a bearer token.

To use the note.add request you need to send a POST to the Notehub API's /projects/<projectUID>/devices/<deviceUID>/notes/<file> endpoint, where projectUID is your ProjectUID, deviceUID is the DeviceUID of a device within your Notehub project, and <file> is the name Notefile you'd like to use (e.g. data.qi).

The body of the note.add request can be any JSON object you'd like. For example, the curl command below shows how to send {"command": "on"} to a device.

curl -X POST
     -L 'https://api.notefile.net/v1/projects/<projectUID>/devices/<deviceUID>/notes/<file>'
     -H 'Authorization: Bearer <access_token>'
     -d '{"body": {"command": "on"}}'

Receiving Commands on a Device

Once you have Notes queued in Notehub, you next need to receive those Notes on your device. You can configure how often your device checks for inbound notes using the hub.set request's mode, inbound, and sync arguments.

For example, running the command below places a Notecard in continuous mode. Devices in continuous mode maintain a constant connection with Notehub, but also use considerably more battery to maintain the connection. Setting the request's sync argument to true ensures inbound notes sync as soon as they're detected on Notehub.

{
  "req": "hub.set",
  "mode": "continuous",
  "sync": true
}
J *req = NoteNewRequest("hub.set");
JAddStringToObject(req, "mode", "continuous");
JAddBoolToObject(req, "sync", true);

NoteRequest(req);
req = {"req": "hub.set"}
req["mode"] = "continuous"
req["sync"] = True

card.Transaction(req)
note

Check out our minimizing latency guide for additional information on sending low-latency commands.

If your project is more battery-conscious, you may wish to instead place your device in periodic mode. Devices in periodic mode check for inbound Notes at a given interval, which you can specify with the inbound argument (in minutes).

The request below tells the Notecard to check for inbound Notes every 60 minutes.

{
  "req": "hub.set",
  "mode": "periodic",
  "inbound": 60
}
J *req = NoteNewRequest("hub.set");
JAddStringToObject(req, "mode", "periodic");
JAddNumberToObject(req, "inbound", 60);

NoteRequest(req);
req = {"req": "hub.set"}
req["mode"] = "periodic"
req["inbound"] = 60

card.Transaction(req)
note

If you place your device in periodic mode, you can use the hub.sync request while prototyping, as it triggers a synchronization of Notes between your Notecard and Notehub, even if your device is in periodic mode.

Regardless of the configuration you use, once your device has received Notes you can check if any are present by running a note.changes request.

The note.changes request returns multiple Notes (if present), so you can use the request to process multiple commands at once.

{
  "req": "note.changes",
  "file": "data.qi"
}
J *req = NoteNewRequest("note.changes");
JAddStringToObject(req, "file", "data.qi");

NoteRequest(req);
req = {"req": "note.changes"}
req["file"] = "data.qi"

rsp = card.Transaction(req)

Resulting in the following JSON response:

{
  "notes": {
    "1:8572": {
      "body": {
        "command": "on"
      },
      "time": 1667855195
    }
  },
  "total": 1
}

If you want to work with one Note at a time, you can alternatively use the note.get request to retrieve the next Note waiting in the Notefile.

{
  "req": "note.changes",
  "file": "data.qi",
  "delete": true
}
J *req = NoteNewRequest("note.changes");
JAddStringToObject(req, "file", "data.qi");
JAddBoolToObject(req, "delete", true);

NoteRequest(req);
req = {"req": "note.changes"}
req["file"] = "data.qi"
req["delete"] = True

rsp = card.Transaction(req)

Resulting in the following JSON response:

{
  "body": {
    "command": "on"
  },
  "time": 1667855195
}

If no notes are available the note.get request returns a {note-noexist} error.

{ "err": "no notes available {note-noexist}" }
note

Both the note.changes and note.get requests allow to pass a delete argument, which controls whether to delete Note(s) after you retrieve them from the Notefile.

At this point you've now seen how to send commands to devices using the Notehub API, as well as how to receive those commands using the Notecard. To put everything together, let's look at an example of how you can use a host to receive commands from the Notecard, and then take action.

Receiving Commands on a Host

When implementing a command-and-control architecture you may want to receive commands on a host microcontroller or single-board computer. A host MCU makes it possible to receive commands and take action, for example using commands like {"command":"on"} and {"command":"off"} as a trigger for turning on and off a light, respectively.

We provide Notecard libraries that allow you to communicate with the Notecard on a wide variety of hosts.

In this section you'll see the steps you need to take using the Notecard libraries to implement a command-and-control architecture.

As an example, we'll show how to use the Notecard Arduino library running on a Blues Swan, but you can use the same steps to implement this architecture with any Notecard library and virtually any MCU.

Step 1: Configure Your Notecard

As a first step, remember that you must configure your Notecard so that it can receive commands. You can do this either in the In-Browser Terminal using the configuration below (remembering to substitute YOUR_PRODUCTUID_HERE) with your own value.

{
  "req": "hub.set",
  "product": "YOUR_PRODUCTUID_HERE",
  "mode": "continuous",
  "sync": true
}

Or you can set up your Notecard using one of the Notecard's libraries. For example, the code below shows how to set up a Notecard to immediately receive inbound Notes using the Notecard's Arduino SDK.

J *req = notecard.newRequest("hub.set");
JAddStringToObject(req, "product", "YOUR_PRODUCTUID_HERE");
JAddStringToObject(req, "mode", "continuous");
JAddBoolToObject(req, "sync", true);
notecard.sendRequest(req);

Step 2: Check for Inbound Notes

Once you have your Notecard set up, you next need to check for inbound notes. The most common way to do this check is with the note.changes or note.get requests discussed in the previous section.

As another option, you can also configure the Notecard to use an interrupt that allows you to use the ATTN pin to wake your host when you receive a new Note in a specified Notefile. If you're interested in that approach, check out Handling Notecard Interrupts.

Regardless of the approach you take, you'll need some logic that can parse the command you need out of the Notecard's JSON response. The code below shows an Arduino example that parses the note.get request's JSON and returns the command. (For example, if note.get returns {"body:{"command":"on"}) this function returns "on").

char* getLastCommand()
{
  // To hold "on" and "off". Increase the size if you need to send longer commands.
  static char command[4];

  J *req = notecard.newRequest("note.get");
  JAddStringToObject(req, "file", "data.qi");
  JAddBoolToObject(req, "delete", true);

  J *rsp = notecard.requestAndResponse(req);
  if (notecard.responseError(rsp)) {
    notecard.logDebug("No notes available");
    command[0] = '\0';
  } else {
    J *body = JGetObject(rsp, "body");
    strncpy(command, JGetString(body, "command"), sizeof(command));
  }

  notecard.deleteResponse(rsp);
  return command;
}
note

If you're using note.changes you'll need some additional logic as the request can return multiple Notes in one response.

Finally, if you're not using interrupts you need to check for inbound notes in a loop. For our Arduino example this is straightforward as Arduino provides a built-in loop function. The code below checks for inbound notes every second.

void loop()
{
  char* command = getLastCommand();

  // Use the command (which we'll do in the next step)

  // Wait one second before looking for changes again
  delay(1000);
}

Step 3: Take Action

As a last step, now that you have your command you can take project-specific actions. For example, this is where you may want to turn on/off a light, move a robot in a given direction, or shut off a piece of hardware.

As one example, the code below shows how to use {"command":"on"} to turn on the Swan MCU's built-in LED, and {"command":"off"} to turn that same light off.

char* command = getLastCommand();

if (!strncmp(command, "on", sizeof("on")))
{
  notecard.logDebug("Turning light on");
  digitalWrite(LED_BUILTIN, HIGH);
}
if (!strncmp(command, "off", sizeof("off")))
{
  notecard.logDebug("Turning light off");
  digitalWrite(LED_BUILTIN, LOW);
}

This example's full code is below for your reference. Remember that although this implementation uses Arduino and the Swan, you can perform the same steps using any of the Notecard's libraries, and you can run your logic on virtually any MCU.

note

Refer back to the section on sending commands to devices to learn how to send the necessary Notehub API requests to test this example.

#include <Notecard.h>

#define serialDebug Serial
#define productUID "YOUR_PRODUCTUID_HERE"

Notecard notecard;

void setup()
{
  while (!serialDebug);
  serialDebug.begin(115200);
  notecard.begin();
  notecard.setDebugOutputStream(serialDebug);

  // Configure the Notecard
  J *req = notecard.newRequest("hub.set");
  JAddStringToObject(req, "product", productUID);
  JAddStringToObject(req, "mode", "continuous");
  JAddBoolToObject(req, "sync", true);
  notecard.sendRequest(req);

  // Initialize digital pin LED_BUILTIN as an output,
  // and ensure the light starts off.
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
}

char* getLastCommand()
{
  // To hold "on" and "off". Increase the size if you need to send longer commands.
  static char command[4];

  J *req = notecard.newRequest("note.get");
  JAddStringToObject(req, "file", "data.qi");
  JAddBoolToObject(req, "delete", true);

  J *rsp = notecard.requestAndResponse(req);
  if (notecard.responseError(rsp)) {
    notecard.logDebug("No notes available");
    command[0] = '\0';
  } else {
    J *body = JGetObject(rsp, "body");
    strncpy(command, JGetString(body, "command"), sizeof(command));
  }

  notecard.deleteResponse(rsp);
  return command;
}

void loop()
{
  char* command = getLastCommand();

  if (!strncmp(command, "on", sizeof("on")))
  {
    notecard.logDebug("Turning light on");
    digitalWrite(LED_BUILTIN, HIGH);
  }
  if (!strncmp(command, "off", sizeof("off")))
  {
    notecard.logDebug("Turning light off");
    digitalWrite(LED_BUILTIN, LOW);
  }

  // Wait one second before looking for changes again
  delay(1000);
}
Can we improve this page? Send us feedback
© 2025 Blues Inc.
© 2025 Blues Inc.
TermsPrivacy
Notecard Disconnected
Having trouble connecting?

Try changing your USB cable as some cables do not support transferring data. If that does not solve your problem, contact us at support@blues.com and we will get you set up with another tool to communicate with the Notecard.

Advanced Usage

The help command gives more info.

Connect a Notecard
Use USB to connect and start issuing requests from the browser.
Try Notecard Simulator
Experiment with Notecard's latest firmware on a Simulator assigned to your free Notehub account.

Don't have an account? Sign up