Skip to content

Particle Device Integration

alt text

Particle (now a part of Digi) provides embedded Wi-Fi and cellular devices along with connectivity and device cloud. This guide covers how to integrate Particle devices to Exosite using Particle's device cloud webhook functionality with Exosite's Device Cloud Federation support.

There are three ways to connect particle devices to Exosite. This document covers the Federation method. A reference Exosite library may be used for the direct device MQTT API method.

Choosing between these depends on multiple factors. Cellular vs Wifi, use of Particles native OTA functionaliy, requirements for bi-directional communication / control, low-power constraints, etc.

Exosite Particle Library using MQTT TLS: https://github.com/exosite-garage/particle_exosite_library

Federation Webhook Method

How It Works

Particle's device cloud natively supports Webhook functionality that may be triggered by Particle.publish() calls from device firmware running on the particle device (e.g. Photon, E SoMs, Argon, Boron, etc). This integration method leverages this functionality to send messages using the Exosite Cloud Federation API which uses CloudEvents. The specific Particle webhook integration intercepts those events sends a CloudEvent formatted event message via HTTP to an Exosite IoT Connector. Exosite receives the data and is routed to ExoSense asset data pipeline where it is stored and used for visualizations, insights, rules, and actions.

Using Exosite's Fedaration interface allows a device cloud to maintain authenticated connection and send data / events on behalf of the device cloud's devices. This differs from Exosite's direct Device APIs (MQTT and HTTP) which is a direct connection and authentication method between the edge IoT devices and Exosite.

Particle Device  →  Particle Cloud  →  Webhook  →  Exosite Federation IoT Connector  →  ExoSense

Prerequisites

  • An Exosite account with an ExoSense application deployed.
  • A Federation Exosite IoT Connector created and configured (guide)
  • A Particle account and at least one Particle device (Photon, Boron, P2, Argon, or similar)
  • The Particle device registered to a Particle product or to your developer account

federation connector template

Exosite Setup

1. Find Your IoT Connector Endpoint and Auth Token

From your Exosite account, navigate to your IoT Connector and note the Endpoint (FQDN). It has the form:

<connector-id>.c2.exosite.io

This is the host your Particle webhook will POST data to.

iot connector settings

You must also set a Cloud Auth Token as a secret string or have it generate one automatically. This is needed in the following step.

Auth Token Generation

Particle Cloud Setup

These instructions are using a Product in Particle, which allows the webhook cloud service integration to be used by all devices in that product.

2. Create a Secret for the cloud auth token

In the Particle Console, navigate to your product (or sandbox) and open Configuration → Secrets.

Create a new secret called BEARER and use the Auth Token from the Exosite IoT Connector you set above.

webhook template

3. Create two Webhook Integrations

In the Particle Console, navigate to your product (or sandbox) and open Integrations → + Add New Integration → Webhook.

This will be done twice, there will be two Webhook integrations, one for sending data to channels via data_in resource and one for sending channel details to the config_io resource.

create webhook

empty webhook

Configure the first webhook using the Custom Template interface and copy/paste in this template, ensuring you replace the <YOUR_FQDN> in the url key with the one from your Exosite IoT Connector (e.g. fffffywkjcxj40000.c2.exosite.io):

{
    "name": "Exosite Federation data-in",
    "event": "exo_data_in",
    "disabled": false,
    "secrets": [
        {
            "name": "BEARER"
        }
    ],
    "template": "webhook",
    "url": "https://<YOUR_FQDN>",
    "requestType": "POST",
    "noDefaults": true,
    "rejectUnauthorized": true,
    "unchunked": false,
    "data_url_response_event": false,
    "headers": {
        "Authorization": "Bearer {{{BEARER}}}",
        "Content-Type": "application/cloudevents+json; charset=utf-8",
        "WebHook-Request-Origin": "particle.io"
    },
    "body": "{\n  \"specversion\": \"1.0\",\n  \"id\": \"014234de-0818-47c4-9bc4-1cb0bdf0302f\",\n  \"source\": \"/particle/application/devices\",\n  \"time\": \"2022-06-29T12:10:18+02:00\",\n  \"type\": \"exosite.identity.data_in\",\n  \"subject\": \"{{{PARTICLE_DEVICE_ID}}}\",\n  \"datacontenttype\": \"application/json\",\n  \"dataschema\": \"#\",\n  \"data\": {\n    \"alias\": \"data_in\",\n    \"value\": \"{{{PARTICLE_EVENT_VALUE}}}\"\n  }\n}"
}

If the format is correct, you should be able to click the Enable Integration button.

webhook template

After creating the webhook, you may edit it using the UI form or going back to the custom template view.

You should have a webhook that looks like the following screen shot, checking the full URL matches your FQDN of your connector:

webhook created

Now do the same thing, creating one more webhook integration, this one will be for sending configuration (config_io) to Exosite for use with ExoSense.

Use this template for this second Webhook, ensuring you replace with the same FQDN:

    "name": "Exosite Federation config_io",
    "event": "exo_config",
    "disabled": false,
    "secrets": [
        {
            "name": "BEARER"
        }
    ],
    "template": "webhook",
    "url": "https://<YOUR_FQDN>",
    "requestType": "POST",
    "noDefaults": true,
    "rejectUnauthorized": true,
    "unchunked": false,
    "data_url_response_event": false,
    "headers": {
        "Authorization": "Bearer {{{BEARER}}}",
        "Content-Type": "application/cloudevents+json; charset=utf-8",
        "WebHook-Request-Origin": "exosite.cloud.test"
    },
    "body": "{\n  \"specversion\": \"1.0\",\n  \"id\": \"014234de-0818-47c4-9bc4-1cb0bdf0302f\",\n  \"source\": \"/particle/application/devices\",\n  \"time\": \"2022-06-29T12:10:18+02:00\",\n  \"type\": \"exosite.identity.data_in\",\n  \"subject\": \"{{{PARTICLE_DEVICE_ID}}}\",\n  \"datacontenttype\": \"application/json\",\n  \"dataschema\": \"#\",\n  \"data\": {\n    \"alias\": \"config_io\",\n    \"value\": \"{{{PARTICLE_EVENT_VALUE}}}\"\n  }\n}"
}

You should now have two Webhook Integrations. These can be edited if you need to correct anything. The only item in each template that should be edited is the FQDN in the url key value.

webhooks ready

Particle Device Firmware

4. Publish Data from Your Device

Your device firmware should publish events using Particle.publish(); that match the two different Event Name configured in the two different webhooks, being exo_data_in and exo_config. The two webhooks will handle sending the data to Exosite.

Examples

  // send two channel's values to Exosite
  char json[255];  // published string in JSON format 
  sprintf(json,
    "{\\\"sig\\\":%0.2f, \\\"num\\\":%d}"
    , strength, num);

    Particle.publish("exo_data_in", json);
  // send Exosite config on boot up
  char configBuf[512];
  sprintf(configBuf,
    "{\\\"channels\\\":{"
      "\\\"sig\\\":{"
        "\\\"display_name\\\":\\\"Signal Strength\\\","
        "\\\"description\\\":\\\"\\\","
        "\\\"properties\\\":{"
          "\\\"data_type\\\":\\\"SIGNAL_STRENGTH_PERCENTAGE\\\","
          "\\\"data_unit\\\":\\\"PERCENT\\\","
          "\\\"precision\\\":0"
        "},"
        "\\\"protocol_config\\\":{"
          "\\\"report_rate\\\":300000,"
          "\\\"timeout\\\":600000"
        "}"
      "},"
      "\\\"num\\\":{"
        "\\\"display_name\\\":\\\"Number\\\","
        "\\\"description\\\":\\\"\\\","
        "\\\"properties\\\":{"
          "\\\"data_type\\\":\\\"NUMBER\\\","
          "\\\"precision\\\":0"
        "},"
        "\\\"protocol_config\\\":{"
          "\\\"report_rate\\\":300000,"
          "\\\"timeout\\\":600000"
        "}"
      "}"
    "}"
  "}"
  );
  Particle.publish("exo_config", configBuf);
// Include Particle Device OS APIs
#include "Particle.h"
// Let Device OS manage the connection to the Particle Cloud
SYSTEM_MODE(AUTOMATIC);

// Show system, cloud connectivity, and application logs over USB
// View logs with CLI using 'particle serial monitor --follow'
SerialLogHandler logHandler(LOG_LEVEL_INFO);

long unsigned int REPORT_RATE = 120; // Seconds report rate
long unsigned int lastPublish = 0; // Used for report rate

// setup() runs once, when the device is first turned on
void setup() {
  // Put initialization like pinMode and begin functions here

  delay(5000); //delay to provide a little time for cellular 

  Particle.publish("online");

  // send Exosite config on boot up
  char configBuf[512];
  sprintf(configBuf,
    "{\\\"channels\\\":{"
      "\\\"sig\\\":{"
        "\\\"display_name\\\":\\\"Signal Strength\\\","
        "\\\"description\\\":\\\"\\\","
        "\\\"properties\\\":{"
          "\\\"data_type\\\":\\\"SIGNAL_STRENGTH_PERCENTAGE\\\","
          "\\\"data_unit\\\":\\\"PERCENT\\\","
          "\\\"precision\\\":0"
        "},"
        "\\\"protocol_config\\\":{"
          "\\\"report_rate\\\":300000,"
          "\\\"timeout\\\":600000"
        "}"
      "},"
      "\\\"num\\\":{"
        "\\\"display_name\\\":\\\"Number\\\","
        "\\\"description\\\":\\\"\\\","
        "\\\"properties\\\":{"
          "\\\"data_type\\\":\\\"NUMBER\\\","
          "\\\"precision\\\":0"
        "},"
        "\\\"protocol_config\\\":{"
          "\\\"report_rate\\\":300000,"
          "\\\"timeout\\\":600000"
        "}"
      "}"
    "}"
  "}"
  );
  Particle.publish("exo_config", configBuf);
}

// loop() runs over and over again, as quickly as it can execute.
void loop() {
  // The core of your code will likely live here.

  int num = random(0, 100); // random number to simulate sensor reading, etc

  if(millis() - lastPublish > REPORT_RATE*1000) //Publishes Rate defined by REPORT_RATE
  {
    //Get Cellular Signal to send
    CellularSignal sig = Cellular.RSSI();
    float strength = sig.getStrength();

    char json[255];  // published string in JSON format 
    sprintf(json,
      "{\\\"sig\\\":%0.2f, \\\"num\\\":%d}"
      , strength, num);

      Particle.publish("exo_data_in", json);

      // Record when you published
    lastPublish = millis();
  }
}

Event Data Size Limit

Particle events support a maximum payload of 1024 bytes. Keep your data_in JSON compact. For large payloads consider splitting into multiple channels or reducing precision.

See the ExoSense Data IO Schema for the full list of supported data types and units for config_io.

Verifying the Integration

  1. Particle Console — Under your webhook integration, monitor the Logs tab to confirm the webhook is firing and receiving a 204 response from Exosite. log

  2. Exosite IoT Connector — Open your IoT Connector in the Exosite UI. Your Particle device should appear in the device list with a recent Last Seen timestamp. Clicking on this device, you will see the data_in resource and config_io resource have been written to. new device resources updating

  3. ExoSense — Navigate to your ExoSense application, go to Device Management, and claim the device to a group. You should see live channel data flowing in. claim device in ExoSense

  4. ExoSense - From here you can create an asset and begin building dashboards and data pipeline functionality.
    create an asset view asset configuration view asset dashboard

Debugging Webhook Failures

  • A 401 Unauthorized response means the Authorization header token is incorrect or the device identity does not match a provisioned device in the IoT Connector.
  • A 4xx response other than 401 typically indicates a malformed request body. Verify the data_in value is valid JSON and properly URL-encoded.
  • Check the Particle Console webhook logs for the raw request and response details.