Particle Device Integration¶

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.
- MQTT Device API - direct form devices
- HTTP Device API - direct from devices
- Federation API - using Particle's Webhook device cloud functionality
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

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.

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.

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.

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.


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.

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:

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.

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¶
-
Particle Console — Under your webhook integration, monitor the Logs tab to confirm the webhook is firing and receiving a
204response from Exosite.
-
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_inresource andconfig_ioresource have been written to.

-
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.

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

Debugging Webhook Failures
- A
401 Unauthorizedresponse means theAuthorizationheader token is incorrect or the device identity does not match a provisioned device in the IoT Connector. - A
4xxresponse other than 401 typically indicates a malformed request body. Verify thedata_invalue is valid JSON and properly URL-encoded. - Check the Particle Console webhook logs for the raw request and response details.