DeviceNotesHelperRK
|
Utilities for using device notes from Particle devices
When you open a device in the console there's a section on the right for notes.
You can edit them using the Edit button and typically you use a textual description.
However, you don't have to. One interesting technique is to store structured data in JSON there. You can set or get the data from the device itself (using webhooks) and also get or set the data using the Particle Cloud API.
This repository contains a library for Particle devices and some sample code for using this from other places.
Some reasons you might want to do this:
Using functions and variables is usually easier, and if you have your own server, monitoring published events using webhooks or the Server-Sent-Events stream (SSE) is generally more efficient. However, in some limited use-cases, this technique is kind of handy!
The curl and node.js examples assume an environment variable AUTH_TOKEN
that contains a valid Particle authentication token. You can create one using the API or get on from the Web IDE Settings (gear icon).
In this case, the token is ab142050967cff79dc6586c82193978b3a62cab9.
For Mac and Linux:
For Windows:
You'll also need this access token to paste into your webhook.
This technique requires two webhooks, one to get data and one to set it. The easiest way is to edit the access_token, then upload it from the Particle CLI.
For example, in the webhooks directory:
If you edit the webhook again, make sure you use particle webhook delete
to delete the old one first! The particle webhook create
command always creates a new one, even if there is already one with the same event trigger, and both will trigger.
There's also a webhook for the DeviceNotesPut
command. You can omit this if you only read data from the cloud and never set the notes from a device.
You can also enter the webhooks manually into the Console web interface, if you prefer.
The library examples are the best way to see how to use the library. However, the underlying JsonParserGeneratorRK documentation may also be helpful.
There is also the helpful JSON code generator. Paste your JSON code into the box and click on a row in the parsed output and it will generate the code you need to access that element.
The 1-simple example shows how to read a single JSON value ("setting") from the cloud at boot. This can easily be extended to other things.
Here's a description of the the code:
Standard boilerplate stuff. The code works with threading enabled or disabled. The SerialLogHandler
is optional to display debugging info to the USB serial port.
It's common to allocate a single DevicesNoteHelper
object as a global variable.
You typically configure the DeviceNotesHelper
in setup()
. In this case, we set the maximum JSON object size (1024 bytes) and get the device notes once at boot. You can also get the data periodically using withGetPeriodic()
.
This is a C++11 lambda. You could put the code in a separate function if you prefer. The important part is that the code inside the { } gets run when the device notes are downloaded from the cloud.
In this example, we get the setting
key from the device notes (an int variable) and print it to the serial debug console.
Make sure you call deviceNotesHelper.setup();
from setup()
!
Make sure you call deviceNotesHelper.loop();
from loop()
!
The 2-counter example shows how to both get an object from the cloud, as well as update it from the device.
This technique can be used to save a small amount of data from the device in the cloud without needing additional servers or 3rd-party services, even when the device is offline.
For devices that are online all the time, Particle variables might be a better choice.
And at larger scales, having a server that stores the last value from Particle.publish using webhooks or SSE (server sent events) is far more efficient.
The object is limited to the size of a single publish, so 622 bytes.
Still, it's an interesting technique!
Standard boilerplate stuff. The code works with threading enabled or disabled. The SerialLogHandler
is optional to display debugging info to the USB serial port.
It's common to allocate a single DevicesNoteHelper
object as a global variable.
Some settings we use in our code and a forward declaration of the incrementCounter()
function.
Setting up the configuration. We have a maximum data size of a single publish because we set the data, so 640 bytes. We also get the data at boot.
The code in the { } gets executed whenever the data is received from the cloud. We track when that happened and increment the counter (see below).
Don't forget to call deviceNotesHelper.setup()
from setup()
!
This code periodically (every UPDATE_COUNTER_PERIOD
milliseconds) while connected to the cloud, increments the counter and saves the value to the device notes.
Don't forget to call deviceNotesHelper.loop()
from loop()
!
This code gets the counter
value that we previously got from the cloud and modifies the JSON to increment the value by 1.
Then it uses the deviceNotesHelper.putToCloud()
method to put the modified data to the cloud. It does a Particle.publish()
which is transformed to a Particle API call by a webhook.
The 3-print example is more or less like 1-simple, but it also shows how to pretty print the entire JSON object. Works with larger objects.
To decode the JSON data you use the JsonParser
object. It's passed into your callback.
Say you have this object:
You could read the value of t1 by using getOuterValueByKey and this code:
This also works for other data types:
You can also read values of nested objects, arrays, etc. but you probably won't need to do that with device notes.
Assuming your JsonParser
is in the variable jp
you then construct a temporary modifier object on the stack like this:
The most common thing to do is have a JSON object and you want to update the value, or insert the value if it does not exist:
If the input JSON was as above, then it would be changed to:
You can add int, long, float, double, bool, and const char * objects this way.
Updating an object will remove it from its current location and add it at the end of the object.
Another common function is appendArrayValue()
which appends to an array.
You can also use removeKeyValue()
and removeArrayIndex()
to remove keys or array entries.
To get the device notes you get the device information. You must have the device ID (in this example, 1e0032000447343138333038), as well as the access token as described above.
This returns a JSON object with the device information. In it, you might see this field with the notes set as above. It's the JSON, though it's a string, not actually a JSON object.
To set the value using curl is a bit awkward, but this changes the setting to 6:
When updating the data by curl you must include the whole data, not just the field you want to update. There is a possibility of update conflicts because of this, so this doesn't really take the place of using a proper database.