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;
}

12 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
    Replies
    1. i am trying to implement the same . Can you please share your implementation of this API ?

      Thank You in advance

      Delete
    2. Hey, I am sorry but I am no longer working on this project and I have lost most of the stuff but at the end I remember I had understood that it was better to move from C to Python...if you are interested I can share Python code...

      Alessandro

      Delete
    3. Sure ! I would like to refer python code for the same.
      please do share .

      Thank you.

      Delete
    4. import string
      import time
      import smtplib
      import math
      import httplib, urllib
      from email.MIMEMultipart import MIMEMultipart
      from email.MIMEText import MIMEText


      updateThingspeak = 15 # how many seconds to sleep between posts
      key = '************' # Thingspeak channel to update


      def updateTHINGSPEAK():
      while True:
      params = urllib.urlencode({'field1': 0.1, 'field2': 0.2, 'field3': 0.3,
      'field4': 0.4,'key':key })
      headers = {"Content-typZZe":
      "application/x-www-form-urlencoded","Accept": "text/plain"}
      conn = httplib.HTTPConnection("api.thingspeak.com:80")
      try:
      conn.request("POST", "/update", params, headers)
      response = conn.getresponse()
      conn.close()
      except:
      print 'connection failed'
      break

      while 1:
      updateTHINGSPEAK()
      time.sleep(5)

      Delete
    5. Please fix indentation since it has been wrongly formatted automatically by the browser/page

      Delete
  3. I am trying to implement the above program and getting following error
    /tmp/ccBKEEhS.o: In function `write_data':
    a.c:(.text+0x344): undefined reference to `Log'
    /tmp/ccBKEEhS.o: In function `UpdateThingSpeak':
    a.c:(.text+0x3e8): undefined reference to `Log'
    a.c:(.text+0x4c0): undefined reference to `Log'
    a.c:(.text+0x4d0): undefined reference to `Log'
    a.c:(.text+0x524): undefined reference to `Log'
    without making any changes to the program as it is

    ReplyDelete
  4. And if I replace Log with printf, following error is encountered
    a.c:10:6: error: conflicting types for ‘printf’
    void printf(char *format, ... );
    ^~~~~~
    In file included from a.c:2:0:
    /usr/include/stdio.h:364:12: note: previous declaration of ‘printf’ was here
    extern int printf (const char *__restrict __format, ...);
    ^~~~~~
    pi@raspberrypi:~/speak $

    Pls do let me know the issues here.

    Just starting with Raspberry

    Thanks in advance

    ReplyDelete
  5. Was able to overcome the issues.

    Working fine.

    Great.

    ReplyDelete
  6. Hi, nice job...
    Can you provide similar code to read data from Thingspeak?

    ReplyDelete