Click here to Skip to main content
13,194,662 members (77,726 online)
Click here to Skip to main content
Add your own
alternative version


240 bookmarked
Posted 20 Dec 2014

Internet of Things: Programming IoT Devices, Web Services and IoT Clients

, 13 Jan 2015
Rate this:
Please Sign up or sign in to vote.
A complete walk through on IoT Device programming, Web Service programming and IoT Client programming.





Internet of Things – There are many articles to explain about “IoT” out there on the internet, so we are not going deeper into what it is. In a simple term “Internet of Things – Making devices work based on connected data through internet”. The data source can be from a sensor, a web server, another device, a mobile phone or any form of an electronic device which can receive/send data through internet. So “Internet of Devices” + “Internet of Data” = “Internet of Things”. Is it that simple? Yes, it is, as long as you are thinking of clicking a button from your office to switch off your TV at home. But, if you want to build a bunch of devices to act and react to each other based on data it receives from each other like robots moving around without hitting each other, automatic cars moving on roads without any accidents, it is not that simple, isn’t it? Rome was not built in a day. This article is to give you some bricks to start building now.

Back to Index

Internet of Things

IoT is all around data. Talking about IoT, there could be series of questions to ask like Who collects the data? Who process the data? What to do with the data? Where to store the data? Where to share the data? How to share the data? The answers will vary depending on the purpose of your goal to achieve with your IoT project. But, basically you need few of the following Things to achieve some answer to the above questions,

  • An IoT device – which can connect to internet
  • Internet connection (Ethernet or Wifi depends on the mode your device can connect)
  • A webserver to store/process/share data

We can purchase an affordable basic IoT device in the market today for few bucks. Internet connection is almost everywhere. A webserver will be the difficult part for learners/beginners or hobbyist who tries to do some level of home automation or a basic IoT project. So, this article will also explain how to use Google Drive for your IoT device as a data backend and also, will cover how to write a .NET webservice if you can afford a web server. Note that, you can configure and program your IoT device as your web server as well, but, that is not covered in this article.

Back to Index


We will try to make a couple of simple circuits for Arduino, one using Thermistor temperature sensor and another using Ultrasonic distance sensor. Arduino program will write the temperature data and safe locker door open/close data to Google Spreadsheet or a MySQL database on a webserver. A Raspberry PI module to capture a picture if a door movement detected by Arduino module and an another PI module to switch off/on an electric device based on Webservice feed.

What we are going to do with this data?

An Android application on a mobile displays the current temperature and raises a notification alarm message if the temperature goes beyond threshold. The same Android application will also have a switch on the UI to write a command data to Google Spreadsheet or MySQL database on a webserver which in-turn will be read by Raspberry PI Python module to switch on/off an electric circuit (a fan, heater or whatever). The Android app will also allow you to enable/disable safety locker door Alarm notification. If it is enabled, your phone will raise an alarm instantly if there is any door movement detected.

Note that, you can make your own changes to this simple design to make it more efficient. But, I keep it as is for this article purpose so that I can explain bits here and there. For example, connect the distance sensor to Raspberry PI module so that you can take some pictures as soon as you detect a movement.

Sample Project: Through this article and sample, you can make your own simple Safety Locker Security Module to get alarm notification on your mobile during a security breach of your Locker. Also, you can control your home electronic equipments remotely through your mobile.

Watch demo video here:

Back to Index

Topics Covered

This article will aim to guide you through the following topics

  • Arduino Device with ThinkSpeak
  • Arduino Device with Google Drive back-end support for collecting temperature data and door movement data.
  • Raspberry PI Device with Google Drive back-end support to turn on/off Electric device based on Arduino’s temperature data
  • Google AppScript programming for processing IoT data from Arduino
  • Android program to read Google drive and direct Raspberry PI device to control Electric device
  • .NET application to read Google drive and direct Raspberry PI device to control Electric device
  • A .NET based Webservice with MySQL database.

At end of this article, you will be able to,

  • Setup and write a program for Arduino device
  • Setup and write a Python module for Raspberry PI device
  • Setup and use ThinkSpeak services.
  • Write a simple Google Appscript to read and write to a Google Spreadsheet
  • Write a simple Android program to read and write to a Google Spreadsheet
  • Write a simple C# program to read and write to a Google Spreadsheet
  • Write a simple C# Web Service to serve device requests with MySQL database

Back to Index

Arduino Basics

Arduino - a small piece of hardware which can be easily programmed to make interactive projects. You need a basic C++/Java programming background to do programming with Arduino. If you are already a C++sian, you can jump into an Arduino project right away, if not, it is not too late, there are a huge list of articles around internet - search, read i++, c+=1, for, while, etc and come back to start Arduino.

To start with Arduino, first thing you need is an Arduino board and to make your Arduino device talk to internet you need another one board called "Ethernet Shield" or you can even buy both together as one pack with "Arduino Yun". I use Arduino UNO R3 board with an add-on Ethernet Shield as that is cheaper.  

An Arduino program mainly needs two functions setup() and loop(). setup() function is the first and one time runner whereas loop() as its name suggest is a cycler which keeps running repeatedly till reset or power off. So, that's it, you learnt Arduino. Really? I wish so, but, not there yet. There are interesting set of functions to learn and know like pinMode(), digitalWrite(), analogRead(), pulseIn() etc. And also there are Arduino libraries which you need to know depending on your project.

So, the basic scope of our Arduino setup is to read the room temperature/movement activity and write it over the internet. What we need to do this,

  1. Arduino microcontroller board 
  2. Ethernet shield board for internet connection.
  3. Internet connection with an Ethernet cable
  4. USB Cable Type A to B (normally comes with Arduino board)
  5. A bread board with some jumper wires
  6. 10K Ohms Thermistor (for Thermistor Circuit)
  7. 10K Ohms Resistor (for Thermistor Circuit)
  8. Ultrosonic distance sensor (for Door movement monitoring circuit)
  9. Arduino IDE

Download the free Arduino IDE from Arduino website and install on your computer. The IDE is simple and easy to use and it comes with loads of ready to use examples which you can choose directly from File --> Examples.

Setup your circuit and hardware:

I use 4 pin ultrasonic sensor in this example. You can get a 3 pin sensor in the market as well. Note that, the above picture doesn't show the Ethernet Shield. But, you need Ehternet Shield to make Arduino see the outside world. You don't need a special setup for it, just plug it on top of your Arduino board and start play with it.

As you already know, an Arduino program needs two functions,

     //Initial setup code for your sketch

     //Your actual logic runs here repeatedly - main loop

Temperature Data:

Using the Thermistor Circuit above, let us start writing some code to get the temperature data from the circuit. As we use Analog pin 0 for reading the data, the code goes as below,

     double temperature=analogRead(A0);

A Thermistor resistor will give voltage output which passes through it based on its resistance value. So what we get in "temperature" variable will be just some voltage references. You need to do some mathematics to convert it to actual temperature value. There is a well known formula and function for that,

Steinhart-hart Formula: 1/T= A + B*ln(R) + C*ln(R)2 

where, T is the Temperature in kelvins, R is the resistance in Ohms. 

double Thermistor(int RawADC) {  //Function to perform the fancy math of the Steinhart-Hart equation
 double Temp;
 Temp = log(((10240000/RawADC) - 10000));
 Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp );
 Temp = Temp - 273.15;              // Convert Kelvin to Celsius
// Temp = (Temp * 9.0)/ 5.0 + 32.0; // Celsius to Fahrenheit - comment out this line if you need Celsius
 return Temp;

So the code goes like this now

double temperature=Thermistor(analogRead(A0)); // Celsius

Now we got the temperature value in Celsius. What now? How to write it to a Google sheet or a Web Server? Arduino provides libraries to connect to internet through Ethernet.  For our purpose we are going to use the EthernetClient class which is well enough to make a HTTP request to a web server.

The internet connection is a two end process, Client and the Server. So, you need an identity for your device and you need to know the identity of the web server you are going to connect to. First, get an IP address for your device (if it is your home network, just use an unassigned IP address in your local range like, and assign a MAC address for your device. Just make sure your MAC address is not a duplicate on your own network.

     IPAddress ip(192,168,1,176); 
     byte mac[] = { 0xAE, 0xDC, 0xBB, 0xEA, 0xFE, 0xED };

Now, you need to know the other side identity, thats the web server. In our example, we are going to use, why not To keep the answer short, the Ethernet library cannot make https/SSL request. But Google requires SSL request, so, we use's  ThingHTTP app function to redirect our request to Google server. We will see how to do it in detail later. Let us get back to the identity of the web server. You can specify the server identity directly by name or IP address

     char server[] = ""; 
//  or
//   byte server[]  = { 184, 106, 153, 149 };

You can find the ip address of a web server by just doing a ping test.

So, how will be the setup(), and loop() functions look like with Ethernet enabled,

void setup() {
  if (Ethernet.begin(mac) == 0) {
    Ethernet.begin(mac, ip);
void loop()
  double temperature=Thermistor(analogRead(A0));
  String data = "data=" + String(temperature, DEC);
  EthernetClient client;
  if (client.connect(server, 80)) {
    String content=data;
    // Make a HTTP request:
    client.print("POST /apps/thinghttp/send_request HTTP/1.1\n");
    client.print("Connection: close\n");
    client.print("X-THINGSPEAKAPIKEY: your_thingspeak_key\n");
    client.print("Content-Type: application/x-www-form-urlencoded\n");
    client.print("Content-Length: ");
    client.print("\n\n");          // end of http header
    if (client.available()) {
      char c =;
    delay(6*10000); // wait for a minute before next update
  else {
    //connection failed. there is a way to retry, check WebClient example

Is it not simple to understand? As you know, setup() is an initializing function, so it runs only once, where we initialize our Ethernet connection through Ethernet.begin(). Then the loop function gets the temperature data, creates a EthernetClient object, makes a request to connect to the Server through port 80. Rest of the code deals with the HTTP headers. This is a one way connection, we don't expect a response from the server. We get the temperature data and pass it to the web server through url parameter. client.print() function writes data to the connected server. client.available() function returns true if the server connection still available. function reads the response from the server and finally client.stop() function closes the connection with the server. You can check the WebClient example code available with Arduino IDE for more understanding.

We will discuss about ThingSpeak in a little time. Before that let us look into Ultrasonic side of the code as well. For understanding, the below code snapshot just shows the Ultrasonic distance sensor part excluding Ethernet and Temperature codes.

const int pingPin = 8;
const int trigPin = 7void setup() {
  pinMode(trigPin, OUTPUT);
  pinMode(pingPin, INPUT);
void loop()
  long duration, cm;
  digitalWrite(trigPin, LOW);
  digitalWrite(trigPin, HIGH);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(pingPin, HIGH);
  cm = duration / 29 / 2;  // to centimetres
  // Assume a locker door and the sensor is inside the locker facing the door
    Serial.print("Door Opened! Trigger an alarm! Call the Robot to hit the intruder!!!!");

The above code partly again from Arduino examples. Only difference is, this example uses 4 pin Ultrasonic sensor which has a triggering pin additionally. Four pin sensor needs a trigger to start. There are some interesting functions used in this example which will be more common in usage on most of your Arduino projects.

pinMode(pin, mode): Defines the usage of the pin on this program. INPUT = for reading input signal, OUTPUT = for writing a signal to a pin (light an LED).

digitalWrite(pin, value): Enable/Disable 5v power on the specified pin; LOW=0v, HIGH = 5v

pulseIn(pin, value): waits for the given pin to go HIGH or LOW as specified under value parameter and returns the duration of HIGH/LOW state of the pin. Ultrasonic sensor sends the sound signal and receives it back and the duration is measured by time and the distance is calculated based on sound speed of return within that duration.

Serial.begin(), Serial.print(): This will be helpful if you dont have a display hardware and for debugging purposes. Press Ctrl+Shift+M on Arduino IDE (or Tools->Serial Monitor), you will get a window on which you can see Serial.print() printing data from your device.

That's it!! Arduino part is almost ready, what you need is write the Door alarm data to web as well, in the similar way as temperature data.

Back to Index

ThingSpeak & ThingHTTP is an opensource solution for Internet of Things products which can be used for data logging, data processing, data forwarding etc. Programmers can make use of it to connect their devices over the internet. Oh! are we talking about a web service? Yes we are. ThingSpeak is a free open data solution for IoT devices. Then why we are talking about Google Spreadsheet/Web Services? Yes, correct why we need to? But, there is a company you work for, will ask you to build a web service for their own web server or you may want to do your own home automation system, not just with data but to enhance it with your super-powered App Script to process the data on your own way. There will be too many advantages when you manage your own services, isn't it?

Now, our goal is to use ThingSpeak to redirect our data to Google. Here, we need to know two things. First, how to do ThingSpeak? What Google url you are going to access? The second question will be answered in a short time under Google Scripts topic. Below are the steps to create a ThingSpeak account and ThingHTTP service. Visit for the other services understanding which is not covered here.

  1. Register/Login to
  2. Goto Apps menu and click on "New ThingHTTP" button
  3. List of entries to be filled to create a ThingHTTP
    • Name: A Name for your ThingHTTP (Eg: "MyArduinoGoogle")
    • URL: Google URL to be redirected 
    • Method: POST
    • Content-Type: application/x-www-form-urlencoded 
    • HTTP Version: 1.1
    • Body: temp=%%data%% (use & for multiple entries)
  4. Click on "Save ThingHTTP" button

Its done, you have your ThingHTTP ready to redirect your Arduino request to Google. What is %%data%%? Scroll up to the Arduino program we discussed earlier and check this line,

String data = "data=" + String(temperature, DEC);

%%data%% is kind of variable declared in your ThingHTTP app which will be replaced by your own data when a HTTP request is made to ThingHTTP with a POST parameter.

Eg: replace <your_api_key> with your actual api key you get from ThingHTTP.{your_api_key}&data=5.234

This request will be redirected to Google URL as,{your_script_unique_id_here}/exec?temp=5.234

Well, you got your ThingHTTP setup now. What's next? How to get the Google App Script URL? Not a big deal, all you need to know is javascript, rest will be some Google API classes and stuffs. Right! Let us jump into it now.

Back to Index

Google AppScript

Google Appscript is a little powerful scripting option available with Google Apps which will let you write your own script and access from other applications (Google Apps or other web services). As google account is available with almost everybody who uses internet today, this may be the best place to store your data for your own devices. As of today, Google's drive option has 15GB of data limit across all your Google Apps which is sufficient for students, hobbyist or simple home automation projects. 

To start your Google AppScript now, go to and select a blank project or "Script as Web App". Note that Google AppScript UI options are subject to change over the time by Google Developers. So, we are just going to understand how to write a script. You can always play with your account's google script and learn.

Basically, there are two functions you need to understand, doGet() and doPost(). If you know the difference between GET and POST request to a web server, this will be easy to understand. As your Google AppScript will be accessed through URL, doGet() will get called if the request was a GET request and doPost() will get called if the request was a POST request. Let us take an example,

function doGet(){
    return HtmlService.createHtmlOutput("Hello Google!");

this will show "Hello Google!" on your browser when gets called through GET request. How to get the URL? Once you've written your script publish it using Publish option and save a revision with description, then choose how to execute your script and who has access to your script. For your device to access your script choose the options as below,

Choose anonymous option as you dont have an option to login to Google account to authorize your script on your Arduino device. Keep the url safe as this will get called anonymously, so anybody have the url can log data to your script. Notice the message which says "You need to authorize the script....". This is just before you deploy the url to public, run the script once to get one time authorization confirmation on your screen.

Right! You got the URL. Make sure always, that you copy the exec url and not the dev url. What is that? when you copy your final URL, check the end of the url, if it ends with "exec" thats a shareable URL or if it ends with "dev" it is your development URL which is not for sharing. Nothing big here, just rename dev to exec if you copied the wrong one.

Passing QueryString parameter to Script:

QueryString parameter can be passed to your app script url as normal as how you do with other URLs. For example,{your_script_unique_id_here}/exec?temp=5.234

Now, to access this value in the script

function doGet(e){
    return HtmlService.createHtmlOutput("Temperature Value : " + e.parameter.temp);

Notice the "e" parameter added to doGet() function which receives querystring parameters from your URL and the value can be accessed using e.paramter.temp where "temp" is the querystring parameter name on the URL. Run this script now, you will get "Temperature Value : undefined"!  But why "undefined"? Make sure you pass the querystring parameter through the URL which will not be there by default to avoid "undefined".

Accessing Spreadsheet:

Our aim is to write our device data to spread sheet. So, to write to a spreadsheet, first create a spread sheet on Google drive and save it with a name. By doing that you will get a spread sheet id on the spread sheet url.

Now, let us start writing some code to access the spreadsheet through Google Script,

var SPID="your-spreadsheet-id";
function doPost(e) {
  return doStuff(e);

function doGet(e){
  return doStuff(e);

function doStuff(e){
  var sheet  = SpreadsheetApp.openById(SPID).getSheetByName("COMMAND");
  if(sheet == null) return HtmlService.createHtmlOutput("No Sheet");
  sheet.getRange(2, 1).setValue(e.parameter.temp);
  return HtmlService.createHtmlOutput("OK");

Simple few lines of code to write to spread sheet, right? There is one small, but important thing you need to understand here. When you make POST request to your URL, the request will fail if there is no doPost() function written to serve the POST request. So, make sure you handle both request on your script. Remember, on ThingHTTP, we are using POST request. If you really want your script to respond differently to both the requests, write seperate code for each request. For eg: if you want to show a graph with temperature data if the request was GET from a browser, do it in doGet() and make the doPost() stuff to do storing data. So, now we know how to write a script to write to Google sheet, shall we quickly see how to make a Google site page to display our Lock status data with auto refresh? Here we go...

Back to Index

Google Site Dashboard

Google Site is a free service provided by Google for its account holders. So, if you have a Google Account, you can own your own personal website. To create your site, visit and click on "Create" button, give a name to your website and choose a template, click "Create" to get your website. Now, we are going to display our Iot device data on the created website to have a dashboard for our IoT device, something like this,

Demo Website:

Google Site is easy to create and easy to program as well. As you already know how to write a Google Script, now it is going to be more easy to use Google Script on your Google Site. Click on Settings icon and go to "Manage Site" option, then click on "App Scripts". Initially, you will not have any scripts there, so click on "Add new script" button to create one and then select a "Blank Project", refer to Google Scripts attached to this article for the complete script. Your Google script is going to create a HTML page and return it to your website. After writing the script, go back to the created website, click on "Edit Page", then Click on "Insert --> AppScript" and choose your appscript. This will attach your written appscript to your website. When your site is loaded, your inserted appscript will make a call to doGet() function to get the HTML page. So the script code will look like this,

function doGet() {
  return HtmlService.createHtmlOutputFromFile('IotPage');

createHtmlOutputFromFile() function will look for the given html file and will load it on your website. What is going to be in IotPage.html? HTML page will be static, how we are going to make it update when there is a door security breach? For that, we are going to use JQuery/Javascript and an  asynchronous client-side JavaScript API called "". So your HTML page is going to be dynamic by doing the below code,

<script src=""></script>

<div id="dstatus"> Lock status goes here... <div>

function onSuccess(data) {
   // "data" carries the return value of getMyData() function call
   if(data !=null){
      if(data[0][4] == 0)
         $("#dstatus").html("DOOR OPENED");
         $("#dstatus").html("DOOR CLOSED");
// auto refresh to update sheet data on the dashboard 
function update() {
    $.ajaxSetup({ cache: false });  // to save from an IE bug/issue
    window.setTimeout(update, 5000); // every 5 secs

JQuery in the above code is very straight forward and simple to understand, $("#dstatus") is the <div> object by its "id" and ".html()" updates its content. update() function calls setTimeout() to trigger itself every 5 secs. calls the server side function getMyData() and on successful return withSuccessHandler() calls onSuccess() function with the returned data. So getMyData() is going to be our key place to write our script to read spreadsheet data. 

function getMyData(){
  var dsheet = SpreadsheetApp.openById(SPID).getSheetByName("COMMAND"); 
  return dsheet.getRange("A2:G2").getValues();

This function returns the complete range "A2:G2" as a two dimentional array which we receive in onSuccess() function in "data" variable.

Watch this video for complete walkthrough on "How to do it?" and refer to the attached code for script details,

Youtube video:

Here is a quick information about showing an image on your Google Site from your Google Drive. Upload your image to a public folder on your Google Drive (share the folder as public first). Note down the unique id of your uploaded image file ( you can get it from the url by opening the image in new window) and use the image id as below,


Back to Index

Android Client



You can use Ecllipse or Android Studio IDEs for your Android development - both are freely available. I use Android Studio as that is the dedicated IDE for Android development. You can download Android Studio from Follow the instructions to install and setup Android Studio on your computer/laptop. Download/Update your SDKs through "SDK Manager" as necessary. If you are going to use your own mobile for testing your app, make sure you connect your USB cable to your computer/laptop and enable "Developer Options" from Settings--> General. If you dont see "Developer Options" on your phone, it may be hidden (from KitKat version onwards), so goto Settings-->About-->Build Number and tap it 7 times to get "Developer Option" visible.

Android Basics

If you want to write an Android program, you must know what is Activity and View. For simple understanding, Activity class in Android is similar to your main() function in C/C++/Java - having just main() function will do something without a window/UI. After main() function you will have to call UI functions to create a window for your application, that is View here. Below is a simple example of how an Activity lifecycle looks like,

public class MyMainActivity extends Activity {
    protected void onCreate(Bundle savedInstanceState) {

    protected void onStart() {

    protected void onRestart() {

    protected void onPause() {

    protected void onStop) {

    protected void onDestroy() {


Anything you can see on your Android phone screen is from a View. So view is nothing but a UI. Android studio will offer you templates when you start a project which will create a default Activity and View based on your selection. Once you created a project, you can find and edit your View under (your project)-->app-->src-->layout folder (in xml format). Another important one to remember for an Android program is your AndroidManifest.xml file. You might have noticed when you install an application from Google AppStore, you will be prompted to approve the application's required permissions list. That list comes from this file, this is to tell your Android OS what are all the services you are going to use in your application (like INTERNET, STORAGE, etc). This file is not just for that alone, most of your application configuration have to be declared here. AndroidManifest.xml is one of the most common thing people forget/miss and spend time on other area to fix the problem.

Well, I recommend to you to have a look around for some tutorial to get some basics on Android programming further. Our goal is to have an app with some buttons and a service running in the background to check data changes in spreadsheet. For this, we need two improtant entries on our AndroidManifest.xml file,

<uses-permission android:name="android.permission.INTERNET"/>

under <manifest> to tell Android that our application needs internet access, and

<service android:enabled="true" android:name=".IoTDemoService" />

under <application> to tell Android that our application runs as Service as well.

We need to make Service and UI application talk to each other to make the app bit efficient so that the Service will handle Spreadsheet data and the UI will act only as UI. There are different ways to do it, a common way is using a static message handler or a private database. I am going to use a small private Sqlite database table which is inbuilt with Android by default to make this article simple and easy for understanding.

Android Service is similar to the "Service" we know in Windows world. It runs in background without an UI and runs forever. Below code shows an example of a Service lifecycle,

public class IoTDeviceService extends Service {
    public IBinder onBind(Intent intent) {
    public void onCreate() {
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    public void onDestroy() {

onStartCommand() will start running when the startService() function called. On our UI app, we call startService as below to start our Service to work with Google spreadsheet.

startService(new Intent(this, IoTDeviceService.class));

Accessing Google Spreadsheet

To access Google Spreadsheet service, below libraries need to be added to your project.

Install Google Play Service through SDK Manager. Open build.gradle under your "app" folder from the project explorer window. Note that there may be more than one build.gradle file in your project (one at the root folder), make sure you choose the file under your app. Add the following line under dependencies block,

compile ''

Not just that, go back to AndroidManifest.xml and declare the below entry under <application> element,

<meta-data android:name=""

            android:value="@integer/google_play_services_version" />

Download gdata java client and guava libraries,

GData Java Client:

Guava Library:

Copy gdata-client, gdata-core, gdata-maps, gdata-spreadsheet, guava jar files to you project's "lib" folder (Project Explorer window on your Android Studio IDE). After copy, right click on each jar files and click on "Add as library" to add them to your app as libraries. Save your project and click on "Sync project with Gradle Files". 

Note that, to use Google spreadsheet, we need a google user account, so we will have to ask our app user to select an user account during start,

String[] accountTypes = new String[]{""};
Intent intent = AccountPicker.newChooseAccountIntent(null, null,
        accountTypes, false, null, null, null, null);
startActivityForResult(intent, REQUEST_CODE_PICK_ACCOUNT);

Once user selected an account to use, we will have to get a token for the selected user account, but this needs user permission,

String token=
                   "oauth2:", null);

This will give a notification to the user to authorize our app to access Google Service using his/her Google account. User has to authorize before we proceed further. Once we received the token, our app is all set to go. Below code snap is to enumerate spreadsheets under the selected user account,

SpreadsheetService s =new SpreadsheetService("IoTDeviceService", "https", "");


SpreadsheetFeed feed;
feed = s.getFeed(SPREADSHEET_FEED_URL, SpreadsheetFeed.class);
List<SpreadsheetEntry> spreadsheets = feed.getEntries();

String sheets = "";
// Run through all of the spreadsheets returned
for (SpreadsheetEntry spreadsheet : spreadsheets) {
    Log.d("IotDeviceServie", spreadsheet.getTitle().getPlainText());
    String title =spreadsheet.getTitle().getPlainText();

Well, rest of the Google Service code deals with how we switch on/off heater/cooler, enable/disable alarm, read temperature, door status from IoT device.

Note that, this sample application IoTDeviceClient looks for a spreadsheet named "TestData" and a sheet with name "COMMAND" in selected user account's Google Drive. The "COMMAND" sheet should look like below,


Important Note: The Android client project code attached with this article is only for learning purpose. The code is not optimized and it is not designed to work as a product. This is just to show how to access Google Spreadsheet through Android app.

See it in Action: Take a carton box and open it on both sides. Measure the distance between two sides of the box in centimeters. Update your Arduino sensor code to keep that measurement as the alarm threshold. Setup your Arduino on one side of the box and keep the Ultrasonic sensor facing the other open side of the box (considered to be our door). Close the door side of the box and power up your circuit. Now, Open/Close the door side of the box, if you have installed the test app on your Android mobile, you must have received a notification already on your mobile about Open/Close activity on your door.

Back to Index

C# IoT Device Client

Let us try one Windows client for our IoT Device. 

Setting up Project

Create a C# Windows Form project and download Google Data API SDK from the following link,

Add the following files to your Project Reference,

  • Google.GData.Client
  • Google.GData.Extensions
  • Google.GData.Spreadsheets

Accessing Google Spreadsheet

Thats it, you are ready to do Google API programming on C#. The method of accessing spreadsheet is going to be very much same as earlier with some little differences. First we need to create a project in and then create Client ID for the project (under APIs & Auth--> Credentials). This will give you a client id, a client secret and a redirect uri.

Now, we need to get the Authorization URL for user permission request,

parameters.ClientId = CLIENT_ID;
parameters.ClientSecret = CLIENT_SECRET;
parameters.RedirectUri = REDIRECT_URI;
parameters.Scope = "";

string authorizationUrl = OAuthUtil.CreateOAuth2AuthorizationUrl(parameters);

User has to use this URL on a browser window to approve and get a access code for our application from Google. This access code will be used to get an access token and a refresh token. These tokens will give our application access to Google Spreadsheet of the user.

parameters.AccessCode = accessCode;
string accessToken = parameters.AccessToken;
requestFactory = new GOAuth2RequestFactory(null, "MySpreadsheetIntegration-v143455", parameters);
myService = new SpreadsheetsService("MySpreadsheetIntegration-v143455");
myService.RequestFactory = requestFactory;

Now, we are authorized to use Google Apps with the tokens. 

SpreadsheetQuery query = new SpreadsheetQuery();
SpreadsheetFeed feed = myService.Query(query);
foreach (SpreadsheetEntry entry in feed.Entries)
    // your sheets in your Google Drive

Refer to the project code attached to this article for more detail.

So far, we did an Arduino circuit and an Arduino program to collect temperature data and door open/close status, created a spreadsheet to store our data and written an Android program, a C# program to read the data from Google spreadsheet and had some fun on testing it as well. What's next? Let us try writing some data back to another IoT device called Raspberry PI.

Back to Index

Getting Started with Raspberry PI B+

Raspberry PI B+ is a well known credit card sized computer board which can be programmed to control your externel devices/equipments or can be used as your mini computer. When you get your fresh Raspberry PI board, you will have to install your Linux based OS called Raspbian (the latest one at the time of writing this article) on your board using a Micro SD card (for B+). The OS can be downloaded from

Setting up Raspberry PI

Put your OS card on the board, connect keyboard/mouse, Ethernet cable and a display to start PI. Once your OS installation process is complete, just do few terminal commands to get your OS updated and to get ready for your Python programming,

[__u__]Updates & Upgrades:
sudo apt-get update
sudo apt-get upgrade

Python Package Manager
sudo apt-get install python-pip

Google API Client for Python
sudo pip install --upgrade google-api-python-client

GData for Python
sudo apt-get install python-gdata

Geany - Python IDE on Linux for Windows Programmers
sudo apt-get install geany

To use Python PI Camera Module
sudo apt-get install python-picamera

Geany will be best if you are just out of Windows programming and trying to do python on Linux. But Geany will not work on terminal. If you use Putty or any other terminal to connect to your PI board remotely, then Geany will not be your choice. Instead, you can use "nano" editor from your terminal or if you want to do remote connection with UI, try tightvncserver. We are going to use nano from terminal.

$ nano

Note that, if you use nano you will have to specify the below line as your first line of your program, if you want to run your module directly from terminal.


This will tell your Linux system what interpreter to execute your program. Once you finish your program, the following line will change your program module as executable (still you can edit your program thou) and this is an one time command for your file.

$ chmod +x

Accessing Google Spreadsheet

Ok, let us connect to our Google Spreadsheet again, now through our Python module. We are going to use the same method as we did for C# program.  First get the authorization URL to get user permission,

OauthToken = gdata.gauth.OAuth2Token(client_id=MCLIENT_ID,
scope= MSCOPE,

print OAuthToken.generate_authorize_url()

Then use the verification code to get access and refresh tokens,

rtoken= OAuthToken.refresh_token

Now, the tokens are the key to access Google Spreadsheet, let us use it. 

OAuthToken = gdata.gauth.OAuth2Token(client_id=MCLIENT_ID,

client = gdata.spreadsheets.client.SpreadsheetsClient()

The above code authorized Spreadsheet "client" object using the token. So, the client object can be used to access Google sheets now on. We need to read the cell values which represent switch flags (Door Open, Heater Switch and Cooler Switch). So, let us access the cells,

cquery = gdata.spreadsheets.client.CellQuery(2,2,5,7)
cells = client.GetCells(SPID, WID, q=cquery)

dooropened = cells.entry[0].cell.input_value
heaterswitch = cells.entry[1].cell.input_value
coolerswitch = cells.entry[2].cell.input_value

SPID is the spreadsheet id which we discussed in Google Appscript topic. What is WID in the code? It works normally as 0 based index (0, 1 , 2, ....). But it is acutally a bit related to the "gid=" which you see in your spreadsheet (worksheet) URL, but not actually that gid valule. It is a bit tricky part to understand, you may need to refer to some of the links in references section of this article which I referred for a solution. WID is an unique ID given for your sheet similar to the gid value. You can find the wid of the sheets if you run this code below and search for "id" tags, you will find something similar to 'od6', 'od7', 'od5', etc., at the end of the URLs and thats the id we are talking about. Normally, 'od6' will be the 1st sheet of your spreadsheet. But, if your delete the first one and the next will not take 'od6'. client.GetCells(SPID, WID, q=cquery) uses this wid to choose your sheet. As I mentioned earlier, it works with index as well. The links which I mentioned gives a formula to calculate wid from gid and vice versa.

print client.GetWorksheets(SPID, auth_token=OAuthToken)

Note that, CellQuery parameters takes (row min, row max, col min, col max) parameters, GetCells takes (spreadsheet id, worksheeet id, cell query).

Well, we got the cell data which can now direct our Raspberry PI GPIO pin to switch ON/OFF electric devices through a Relay module. Remember that, Raspberry PI B+ is not just about GPIO pins, it is much more than that, we are just focusing on GPIO pins for our article purpose.

Controlling GPIO Pins:

We have got the swtich ON/OFF data from spread sheet, now passing this to Raspberry PI GPIO pins is all we need to do to achive our goal.

Here is a bit of information about Relay module, Relay module is our interface switch between our low voltage programmed circuit board and the high voltage outside world. The small pins as you can see in the above picture are our triggering pins, make it HIGH/LOW to switch ON/OFF your electric supply through Relay. Three screw points on the other side of each Relay module are our high voltage point. Those three are NO, COM, NC pins, that is Normal Open, Common, Normal Close respectively. The symbol on your relay showing connected tells you which pin is Normally Closed. When you supply 5v to the other end of Relay, the Common pin will switch its connection to Normal Open. Ok, thats a bit of electronic stuff, let us come back to our programming stuffs.

Note that, to use GPIO pins, the logged in user needs hardware access permission. I am going to use root permission to the "pi" user. To do that, login to root using "su" command and open /etc/passwd file using nano editor

$ nano /etc/passwd

find pi user and change pi:x:1000:1000 to pi:x: 0: 0 and press Ctrl+O, Enter to Save, then Ctrl+X to exit


Similar to Arduino pin setup, you need to setup your GPIO pins to use for your relay module. Below is the code to show you how to setup your GPIO pins,


GPIO.setup(HEATERPIN, GPIO.OUT, initial=0)
GPIO.setup(COOLERPIN, GPIO.OUT, initial=0)

# reset of our gdata code
# and....

        if heaterswitch == '1':
            print 'HEATER ON'
            GPIO.output(HEATERPIN, 1)
            print 'HEATER OFF'
            GPIO.output(HEATERPIN, 0)
        if coolerswitch == '1':
            print 'COOLER ON'
            GPIO.output(COOLERPIN, 1)
            print 'COOLER OFF'
            GPIO.output(COOLERPIN, 0)

Note that GPIO.setmode() function is to setup how the pin numbers are going to be used in our program. GPIO.BOARD tells that the pin usage is as per the Board pin sequence. GPIO.BCM tells that the pin numbering will be as per CPU (ie., GPIO pin numbers). Use either, based on how you understand your board pins.

Our program sets board pins 12 & 16 for OUTPUT (GPIO.OUT) to control heater and cooler. initial=0, makes the initial value of the pin to LOW. GPIO.output() function controls HIGH/LOW value of the pin, the second parameter of the function can be 1/0 or GPIO.HIGH/GPIO.LOW or true/false.

Always make sure you cleanly exit the program by calling GPIO.cleanup() function if you use GPIO pins in your program. If not, you may not get access to those pins on your next run. To make sure you cleanly exit even during an interrupt (like keyboard interrupt) or a crash, keep your code within a try/except block.

Note: The python program attached with this article is just a terminal program - not a daemon/background program. So if you run it on your PI device, it will run infinitely until you press Ctrl+C to interrupt it. See Links & References section below for a link on how to make Python program run as Background.

Usage: Create anonymous access Google Script using GoogleAppScriptForPI.txt (Refer Google AppScript section for details). Create a Google sheet named "URL" and use the sheet id in the script. Update your Python module (with Google Appscript Authorization) with your sheet id. Run your Python module. By the time your module asks for Verification code, your "URL" sheet would have been updated with Authorization URL. So, run your Google Appscript from your browser to authorize and get verification code.

Watch this video for "How it works":

See it in Action: Connect an AC volt electric device (a table fan or table lamp) to the Relay module. Take your mobile with our Android IoT Client. Switch ON/OFF heater/cooler switch on your mobile app and see your Fan and lamp response to it.

Disclaimer & Warning: Using Relay module involes high voltage connections. So, don't do it unless you know what you are doing, otherwise you will be even risking your life for it. Or making a small mistake may even make your device or electronic equipment unusable. This article or author holds no responsibilty for any such things. Do whatever you do with your device/current on your own risk. My recommendation is to take help of an Electrician to check your connections or just use LEDs to test.

Back to Index

Camera & Upload

So far, we made couple of IoT devices communicating to a mobile device user through Google App Webservice. What if you want an IoT device react to another IoT device action. What to do if you want your PI device to take a picture when Arduino device sends a Door Open notification. With all we have done so far, this is not going to be a difficult task, is'nt it? Just program your Python program to capture a picture when the dooropened spreadsheet cell turns to "1". Though I am not going to cover this topic in detail, this will be easy to understand and use in your project. Do some search yourself for Python Multipart file upload using urllib2 library. Below is the simple code to capture a picture through your PI camera.

import picamera
# do your gdata code
# and .....
        camera = picamera.PiCamera()
        if dooropened == '1':
            print 'SECURITY BREACH: DOOR OPENED'
            GPIO.output(DOORPIN, 1)  # if you want any physical action like raising an alarm sound
            camera.capture('dooropened.jpg')  # capture an image of the location near your door
            print 'DOOR STATUS CLOSE'
            GPIO.output(DOORPIN, 0)


Back to Index

Windows .NET Webservice

If you can afford a web server or if you are ready to use your laptop/computer as your webserver, writing your own webservice will be the best choice to manage your data between devices. Create your simple web service by opening Visual Studio IDE (I use 2010) --> New Project. Choose "ASP .NET Web Service Application" template from the "New Project" window. If you can't find "Web Service" template on your list, it may be the .NET version choice on the top, select the right version (I used 3.5). Finally, click OK. That's it! your Webservice is ready. Is it? Yes, you can just press "F5" and test your web service "HelloWorld()" running on your localhost web page. It is easy to learn .NET webservice and there are too many articles around about it. So, let us jump to what we want to do with it.

We need our Arduino program to post some data to our Web Service. Isn't it? Let us do that. Modify your HelloWorld function in your Webservice like this,

using System.Data.Odbc;
// :
// :
// :

String connectionString = "DRIVER={MySQL ODBC 5.1 Driver};SERVER=localhost;DATABASE=IotDatabase;USER=root;PASSWORD=root;OPTION=3;";

public string ArduinoData(String temp, String doorData)
        using (OdbcConnection IotDataCon = new OdbcConnection())
            IotDataCon.ConnectionString = connectionString;
            // Insert or update your single record based on your need    
            String query = "UPDATE IotDatabase.COMMAND SET TEMPERATURE='" + temp +
                              "', DOOROPENED=" + doorData + " WHERE ID=1;";
            OdbcCommand cmd = new OdbcCommand(query, IotDataCon);
    catch (OdbcException exp)
        // handle your Odbc exception
        return "NOK";
    return "OK";

Note the [WebMethod] just above the ArduinoData() function. That tells your Service that ArduinoData() function needs to be published to web. You can also write normal functions or classes which you dont need to be published on your Web Service program. Now, compile your webservice, go to your Project Explorer and right click on your Project folder, choose "Publish". Note that your Visual Studio need to be launched as "Administrator" for publishing to IIS folder. Publish wizard can create a folder for you if the given folder not exist (IotDemoWebService here). The below screeshot shows how to do it, make sure you do a Release build for your final version.

After publishing it, go to your IIS Manager (Control Panel --> Administrative Tools) and you can see your Webservice folder under "Sites" --> "Default Web Site". Right click on your webservice folder and "Convert to Application", then "OK". All right! you have created your own web service. Now make a call to it from another machine (your Arduino device).

char server[] = "yourwebserver";

data = "temp="+String(temperature,DEC)+"&doorData="+String(st,DEC);

client.print("POST IotDemoService/IoTDemoService.asmx/ArduinoData HTTP/1.1\n");
client.print("Host: youwebserver\n");
client.print("Connection: close\n");
client.print("Content-Type: application/x-www-form-urlencoded\n");
client.print("Content-Length: ");
client.print("\n\n");          // end of http header
if (client.available()) {
  char c =;

If you do a POST to your webservice,

POST IotDemoService/IotDemoService.asmx/ArduinoData HTTP/1.1
Host: yourwebserver
Content-Type: application/x-www-form-urlencoded
Content-Length: 21


you will get a response like below, 

HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: 86

<?xml version="1.0" encoding="utf-8"?>
<string xmlns="">OK</string>

You can do the same for your Raspberry PI module as well.

Back to Index

Links & References

Demo Links:
Video: Door Security Demo
Video: How to build your Google Website for IoT Dashboard
video: Demo on Raspberry PI sample works with Google Appscript
Video: Raspberry PI Speech Recognition and Controlling Fan through Relay - A Relay connection Demo
IoT Demo Website on Google Site

Reference Links:
Android Studio - Android Development IDE
OAuth2 Authentication For Devices
Arduino IDE
Start your Google Script
Start your Google Site
Google GDATA Java Client Library - Android
GUAVA Library - Android
Google GDATA Library - C#
Google Developer Console - Home
Raspberry PI Downloads - Home
Google Spreadsheet GID to WID - Stackoverflow
Raspberry PI Pin Test
Python File Upload
Python Script Run as Background
A comparison table to basic statements of the programming languages used in this article,

Back to Index


1. Codeproject Smilies: While writing this article, I couldn't able to make this pi:x: 0: 0 appear on this article page properly as it was always showing pi:x:0:0. I like them thou, but I dont want them there. Then I had to use spaces to make it appear.

2. Initially the door security idea was just referring to any door, later I changed it to safety locker door after watching a thriller movie on which the villains used safety locker mobile alert system.

3. I was using two spreadsheets for testing the device alternatively. There was a time, I spent around 3 hours when the device was not responding to mobile command, the 3 hours of time was just to find out I was using wrong spreadsheet id.

4. I use LEDs mostly to check signals. One of my LEDs went down without letting me know. As it was working earlier, I was looking for problem in other area for some time until I found the little dead man on my bread board.

Back to Index


Number of devices collecting data and variety of devices collecting data are increasing rapidly day to day. When you plan to put your device on internet, security to your data and device takes more priority. For example, on this article Arduino is passing data to Google Spreadsheet on a pulicly shared URL without any security whereas RaspberryPI is using OAuth2 authentication for communication.

Raspberry PI or Arduino device can act as Web Servers themself. So, you don't need a separate Web server running for your devices as they can do it by their own. Raspberry PI can be programmed to stream video to your mobile device when there was a security breach detected, so that you can see what is going on right away from your mobile. Sky is the limit for your imagination in IoT world. Keep exploring and do innovations. Hope this article helped you understand IoT space to some level.



This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Naren Neelamegam
Software Developer
United Kingdom United Kingdom
Naren started coding during 1999 with FORTRAN, then COBOL, PASCAL, C, C++, VC++ ..... C#, Java, ASP so on, till today. He claims himself as techie who loves coding, but he is not even sure which technology or platform he loves, most of the time Windows, some time WinCE, some time Linux, nowadays Android and embedded platforms. He can do some query stuffs with Oracle, SQL Server, MySQL. He strongly believes that C/C++ is a must for all programmers, "if you know C/C++, you can do programming on any language". He is an electronic gadget guy who likes to buy small gadgets all the time, at least he will do window shopping on a gadget shop. His interest always have been with Automation in any form, call it a little automated program sitting in his Laptop or a home automation program runs on his mobile. Apart from coding, he likes to do...???

You may also be interested in...


Comments and Discussions

Questiongoogle spreedsheet is not work no change colors on switchs Pin
Member 1302105025-Feb-17 2:14
memberMember 1302105025-Feb-17 2:14 
QuestionUnable to get raspberry authorization code Pin
Thinageran Rajendran12-Feb-17 6:16
memberThinageran Rajendran12-Feb-17 6:16 
QuestionBeginner: Regarding Arduino uno board Pin
Member 1275540321-Oct-16 4:04
memberMember 1275540321-Oct-16 4:04 
QuestionPlease help me with Google Site Dashboard Pin
Walter Valverde14-Aug-16 7:02
memberWalter Valverde14-Aug-16 7:02 
GeneralMy vote of 5 Pin
Walter Valverde14-Aug-16 3:57
memberWalter Valverde14-Aug-16 3:57 
QuestionIOT Website/ Dashboard Pin
Member 1216772717-Mar-16 2:32
memberMember 1216772717-Mar-16 2:32 
QuestionMy Vote +5 Pin
D V L6-Nov-15 22:52
professionalD V L6-Nov-15 22:52 
QuestionThis is Iot? Pin
Marlon Tiedt5-Oct-15 13:55
memberMarlon Tiedt5-Oct-15 13:55 
QuestionFacing Problem with ThingHTTP integration with Google Scripts Pin
Member 1166763313-May-15 19:49
memberMember 1166763313-May-15 19:49 
AnswerRe: Facing Problem with ThingHTTP integration with Google Scripts Pin
Naren Neelamegam13-May-15 23:55
memberNaren Neelamegam13-May-15 23:55 
GeneralRe: Facing Problem with ThingHTTP integration with Google Scripts Pin
Member 1166763315-May-15 9:43
memberMember 1166763315-May-15 9:43 
GeneralRe: Facing Problem with ThingHTTP integration with Google Scripts Pin
Naren Neelamegam15-May-15 10:34
memberNaren Neelamegam15-May-15 10:34 
GeneralRe: Facing Problem with ThingHTTP integration with Google Scripts Pin
pheath2261-Dec-15 16:46
memberpheath2261-Dec-15 16:46 
QuestionIoT shared standards Pin
Nic_Roche15-Jan-15 9:59
memberNic_Roche15-Jan-15 9:59 
QuestionRelay Module Pin
Ranjan.D9-Jan-15 5:29
mvpRanjan.D9-Jan-15 5:29 
AnswerRe: Relay Module Pin
Naren Neelamegam9-Jan-15 5:56
memberNaren Neelamegam9-Jan-15 5:56 
GeneralRe: Relay Module Pin
Ranjan.D9-Jan-15 6:36
mvpRanjan.D9-Jan-15 6:36 
GeneralRe: Relay Module Pin
Naren Neelamegam9-Jan-15 12:04
memberNaren Neelamegam9-Jan-15 12:04 
GeneralRe: Relay Module Pin
Ranjan.D9-Jan-15 16:02
mvpRanjan.D9-Jan-15 16:02 
QuestionGreat article Pin
Nmklotas8-Jan-15 8:39
memberNmklotas8-Jan-15 8:39 
AnswerRe: Great article Pin
Naren Neelamegam8-Jan-15 8:58
memberNaren Neelamegam8-Jan-15 8:58 
QuestionPrintable version Pin
ggtours8-Jan-15 3:49
memberggtours8-Jan-15 3:49 
AnswerRe: Printable version Pin
Naren Neelamegam8-Jan-15 4:21
memberNaren Neelamegam8-Jan-15 4:21 
GeneralRe: Printable version Pin
ggtours8-Jan-15 4:33
memberggtours8-Jan-15 4:33 
GeneralRe: Printable version Pin
ggtours8-Jan-15 20:28
memberggtours8-Jan-15 20:28 
GeneralRe: Printable version Pin
Naren Neelamegam8-Jan-15 23:16
memberNaren Neelamegam8-Jan-15 23:16 
GeneralMy vote of 5 Pin
Kamran Mackey7-Jan-15 9:39
professionalKamran Mackey7-Jan-15 9:39 
GeneralRe: My vote of 5 Pin
Naren Neelamegam7-Jan-15 12:55
memberNaren Neelamegam7-Jan-15 12:55 
GeneralMy vote of 5 Pin
Agent__00729-Dec-14 21:03
professionalAgent__00729-Dec-14 21:03 
GeneralRe: My vote of 5 Pin
Naren Neelamegam30-Dec-14 0:49
memberNaren Neelamegam30-Dec-14 0:49 
GeneralMy vote of 5 Pin
Diwakar Kumar24-Dec-14 19:44
memberDiwakar Kumar24-Dec-14 19:44 
GeneralRe: My vote of 5 Pin
Naren Neelamegam25-Dec-14 1:33
memberNaren Neelamegam25-Dec-14 1:33 
GeneralNice Pin
Pooja Brsk23-Dec-14 1:54
memberPooja Brsk23-Dec-14 1:54 
GeneralRe: Nice Pin
Naren Neelamegam23-Dec-14 8:31
memberNaren Neelamegam23-Dec-14 8:31 
GeneralVery Cool Pin
Kevin Priddle22-Dec-14 4:22
staffKevin Priddle22-Dec-14 4:22 
GeneralRe: Very Cool Pin
Naren Neelamegam22-Dec-14 4:26
memberNaren Neelamegam22-Dec-14 4:26 
NewsDemo Videos of this Article Pin
Naren Neelamegam21-Dec-14 7:51
memberNaren Neelamegam21-Dec-14 7:51 
QuestionWorking demo Pin
Member 1124431020-Dec-14 8:02
memberMember 1124431020-Dec-14 8:02 
AnswerRe: Working demo Pin
Naren Neelamegam20-Dec-14 8:17
memberNaren Neelamegam20-Dec-14 8:17 
AnswerRe: Working demo Pin
Naren Neelamegam20-Dec-14 11:37
memberNaren Neelamegam20-Dec-14 11:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.171018.2 | Last Updated 14 Jan 2015
Article Copyright 2014 by Naren Neelamegam
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid