Friday, November 20, 2015

ThingSpeak API for Internet of Things

There is much hype about the "Internet of Things" and the Raspberry Pi is increasingly used as an IoT controller. To describe it simply - if you have a web site accessible from anywhere that displays data from a device and/or allows control of  that device - then you are part of the Internet of Things.

I have a smart thermostat that allows me to control my heating and cooling system remotely from a smart phone app. This is one of the most commonly used IoT devices. I also have created web interfaces to some of the Raspberry Pi controlled devices in my home - alarm system, lights, sprinklers, hot tub, weather station, security cameras.

I programmed these web interfaces myself, but there are much easier ways that are actually more secure, and these use the services that support the Internet of Things. One of the easiest to use is ThingSpeak.com. Here are the steps to use this service.

Go to the ThingSpeak web site and click on "Get Started." Click on "Sign Up" to create an account and then log in. Now you can create a new "Channel" which includes 1 to 8 fields of data. Once created, the channel is assigned an API_Key which is required to update the channel. You can see this value by clicking on the "API Keys" tab.

I have created a channel named "Example" with a single data field. Data can be uploaded to this field by using the following URL:

https://api.thingspeak.com/update?api_key=JPISWIDBF2CERG24&field1=123

(Yes, that is the actual API key. Feel free to post your own values, but try to keep them between 1-1000 so the chart is usable.)

You can view the public view here:  https://thingspeak.com/channels/67033

Note that you can only update once per minute using this method.

Examples in a couple dozen languages, including C and Python are available here:
https://thingspeak.com/docs/examples

Here is an example of real data - temperature and fermentation rate from my home-brewing system.



I decided to create my own API since I had all the necessary code already. This API requires the CURL library to handle the web access. To install this:
sudo apt-get update
sudo apt-get install libcurl4-openssl-dev

Here is the API that I created for updating two fields.
/***********************************************************************
   Filename:   thingspeak.c
   send data to ThingSpeak IoT server
   
   Uses libcurl to send the data via HTTP
   
  13-Nov-2015   Ted Hale  created

************************************************************************/

/* system includes */
#include <stdio.h>
#include <stdlib.h>  
#include <stdarg.h>
#include <time.h>
#include <string.h>
#include <curl/curl.h>

// Note: this function can be replaced with printf if you like.
void Log(char *format, ... );

#define TRUE 1

// the URL structure to update ThingSpeak
// https://api.thingspeak.com/update?api_key=YOUR_CHANNEL_API_KEY&field1=7
char *URLtemplate = "https://api.thingspeak.com/update?api_key=%s&%s=%s&%s=%s";

// structure used by the libcurl write callback function
struct url_data {
    size_t size;
    char* data;
};
 
//=====================================================================
// write callback function needed by libCurl
size_t write_data(void *ptr, size_t size, size_t nmemb, struct url_data *data) {
    size_t index = data->size;
    size_t n = (size * nmemb);
    char* tmp;

    data->size += (size * nmemb);
    tmp = realloc(data->data, data->size + 1); /* +1 for '\0' */

    if(tmp) {
        data->data = tmp;
    } else {
        if(data->data) {
            free(data->data);
        }
        Log("wuthread> write_data Failed to allocate memory.\n");
        return 0;
    }

    memcpy((data->data + index), ptr, n);
    data->data[data->size] = '\0';

    return size * nmemb;
}

//=====================================================================
// upload data to ThingSpeak
int UpdateThingSpeak(char *api_key, char *f1, char *v1, char *f2, char *v2)
{
 int   error = 1;
 time_t   now;
 struct tm  *dt;
 char   url[256];
 
 CURL  *curl;
 CURLcode res;
 struct url_data response;
 
 time(&now);
 dt = gmtime(&now);
                         
 // build the URL string
 snprintf(url, sizeof(url)-1, URLtemplate, api_key, f1, v1, f2, v2);
 // guarantee null termination of string
 url[sizeof(url)-1] = 0;
 
 Log("UpdateThingSpeak> send: [%s]",url);
 
 curl = curl_easy_init();
 if (curl) {
  response.size = 0;
  response.data = malloc(4096); /* reasonable size initial buffer */ 
  response.data[0] = '\0';
  curl_easy_setopt(curl, CURLOPT_URL, url);
  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
  res = curl_easy_perform(curl);
  if(res != CURLE_OK)
  {
   Log("UpdateThingSpeak> curl_easy_perform() failed: %s\n",curl_easy_strerror(res));
   Log("URL is: [%s]",url);
   error = 1;
  } else {
   // ThingSpeak returns "0" for error, else returns sample number
   error = (strcmp(response.data,"0") == 0);
  }
  curl_easy_cleanup(curl); 
  free (response.data);
 } else {
  Log("UpdateThingSpeak> curl_easy_init failed\n");
  error = 1;
 }
 
 return error;
}

3 comments:

  1. Hi, how can I use your API? Can I see some examples? Thank you very much!

    ReplyDelete
    Replies
    1. The code listed in the post above will update a thingspeak feed with two variables. It needs to be modified for other configs

      Here is how to call it (you need your own key)

      sprintf(temp,"%d",someVariable);
      sprintf(temp2,"%f",someOtherVariable);
      UpdateThingSpeak("DFLGGKEFTRKREHKY", "field1", temp, "field2", temp2);

      Delete
  2. Ok, got it. I have customized your function UpdateThingSpeak for my needs, tested and everything work properly. Thanks so much! Cheers!

    ReplyDelete