Best way to use webhooks with thingspeak for a product

I have a product that uses webhooks to send data to thingspeak. I have multiple units and each one sends a single reading to it’s own individual thingspeak channel. When I use personal webhook integrations this is no problem because I can pick the device and add it’s api key and field but in the product webhook integration that feature is not available. All my units use the same code and I don’t really want to have to add the api key individually in the code for each one because then I can’t send out a firmware update to all the units if I decide to change something. Is there a way to make this work without having to add another service in between particle and thingspeak? If I could do unlimited personal webhook integrations this would be solved but I believe that there is a limit on those.

You don’t need these “semi-static” data fields hard coded.
You can have them stored on the device in EEPROM.

This sounds as if you were using multiple webhooks - on per device. This should also not be necessary as you can have add a field to the the event data and use that in the webhook to construct your target URL.

Can you point me to an example of how to add the api key to the EEPROM and how I would retrieve that key to publish it? I haven’t done that before. If I could do that I could insure that the data being published would be routed to the correct customer. My events are just routed to the thingspeak field that they should land in on the thingspeak channel (field1, field2). Each unit goes to an individual customer’s channel. Some customers have multiple units deployed so each additional one I just send to the next field.

Another option that I’ve used is to add (1) Photon that subscribes to the Event.
The Photon performs a Select/Case to determine each individual ThingSpeak’s Write Key from the Customer nodes. The Photon then sends the message to the correct ThingSpeak Channel/Field.

This method stores all the ThingSpeak Write Keys on (1) Photon, verses each Customer Node individually.

You can even have the Photon automatically assign the next “unused” ThingSpeak Channel to new Customers as it receives data from a new/unknown device…to maintain your customer list. That list would need to be copied to EEPROM and periodically published to you for offsite redundancy.

It’s cleaner to do this w/ something like NodeRed, but a Photon works too.

A very simple example

const uint32_t magicNumber   = 0xBEEFF00D;
const uint32_t eeBaseAddress = 0;
struct {
  uint32_t magicNumber;
  char     channelID[24];
} eeData;    
  
int setChannelID(const char* arg) {
  memset(&eeData, 0, sizeof(eeData)); // initialisze struct
  eeData.magicNumber = magicNumber;   // set magic number
  strncpy(eeData.channelID, arg, sizeof(eeData.channelID)-1);
  EEPROM.put(eeBaseAddress, eeData);
  return strlen(eeData.channelID);
}  

void setup() {
  Particle.function("setChannelID", setChannelID);  
  EEPROM.get(eeBaseAddress, eeData);
}

void loop() {
  static uint32_t ms = 0;
  if (millis() - ms < 5000) return; // act only every 5 sec     
  ms = millis(); 
  
  if (magicNumber == eeData.magicNumber) {
    char payload[64];
    snprintf(payload, sizeof(payload)
            , "{ \"ID\":\"%s\", \"field1\":%.2f }"
            , eeData.channelID
            , random(0, 1000) / 100.0 );
    Particle.publish("evt", payload);
  }
}

For above code your webhook definition could look something like this

{
    "event": "evt",
    "responseTopic": "",
    "url": "https://your.target.server.net/{{{ID}}}",
    "requestType": "POST",
    "noDefaults": true,
    "rejectUnauthorized": true,
    "headers": {
        "Content-Type": "application/json"
    },
    "body": "{ \"field1\":{{{field1}}}, \"ts\": \"{{{PARTICLE_PUBLISHED_AT}}}\" }"
}

For more complicated use-cases you could expand eeData to also store the format string for your channel and some more configuration data to tailor the payload sent to the channel as needed.
In that case I’d also add some checksum logic to ensure the data stored to and read from EEPROM is valid.

1 Like