Introduction: Simple Wireless Temperature Sensor Updating Web Site With Electric Imp and Thermistor

[Edit 2014] The planner that was used by Electric Imp is no longer used and not available anymore.[/Edit]

This is a small project to get you started with the electric imp and a Thermistor so you can see how you can get temperature readings updating live on a web site. This instructable will address both the hardware and the web site along with all the parts in between. I've tried to keep it as simple as possible which means there are plenty of ways you can expand on it.

The first part will address the electric imp and how we connect a resistor and Thermistor to create a very simple circuit called a voltage divider and use the imp's built in analog to digital converter to take a reading and calculate what the temperature is. I'll also go into some detail on how the circuit and it's components work.

Step 1: How It All Fits Together and What You Need

We’re going to create a voltage divider network using a 10k resistor and the 4.7k Thermistor. The Imp will read the voltage between the resistor and thermistor. The Imp then does some calculations on this and stores the value. It does this 50 times every 5 seconds (or there about) to get an average reading. This average reading is sent to HTTP Request node (which lives in the Amazon Cloud) which in turn sends it to your web server.

Your web server receives this as parameters attached to the URL, logs it, and displays the result.

You're going to need the following:
electric imp
Sparkfun electric imp Breakout (April board)
Thermistor - NTC 4.7Kohms 2% Radial as the temperature sensor.
10k ohm Resistor 1% 1/4W
• Some connectors (I used):
   o Stackable Header - 1 x 10 – Standard as it allows me to plug into a breadboard, but also access from the top.
   o Break away headers - straight - brake off three pins for the jumper connection to select USB/Battery
• Some hookup wire (I use a 40P 80mm dupont wire, male-male terminated with pins and tear off what I need).
• And a breadboard to put it together on
• A USB cable for power.

I got all my parts from Electron Hobbies.

Step 2: Connecting the Bits Part 1

First let’s get the Sparkfun electric imp Breakout (April board) ready. I tend to use stackable headers for breakout boards like this as it allows me to plug into a breadboard plus have connectors on top of the breakout board as well (my view of best of both worlds). This is really a personal choice and you may want to use some other method.

As you can see in the first two pictures, I used the Stackable Header - 1 x 10 – Standard from Electron Hobbies. Note that you actually need 9 pins. Readily available sizes for these are 6 pin, 8pin and 10pin. So in this case there is one pin outside the board which is ok as it doesn’t interfere with anything.

Step 3: Connecting the Bits Part 2

The power. There are three ways to provide power to the Sparkfun electric imp Breakout (April board). The first and usually easiest is via a USB cable. To do this, I installed a 3 pin header to the  Battery/USB selection pads on the board. To make the 3 pin header, get a pair of cutters or a knife and cut 3 pins from a 40pin breakaway header. See the first picture of this step for the location and the second picture of the 3 pin header.

The third picture shows a standard jumper block from an old hard drive or you can purchase some from Electron Hobbies and another alternative, just wrap some fine wire around the two pins on the USB side.

Step 4: Connecting the Bits Part 3

You will notice one leg of the thermistor is connected to one leg of the resistor with these connected legs connecting to pin2 of the April board. You can use any of the Pin’s which are able to be used as analog in pins. See http://devwiki.electricimp.com/doku.php?id=imppinmux.

The other side of the resistor is connected to GND with the other side of the thermistor connected to the 3V3 pin.

A quick word on electric imp terminology. The device in this case the April board, the thermistor and the resistor with associated wiring is called an impee. (Yes, lower case 'i'). The SD card component is called an imp. The imp has the processor and 'brains' that controlls the impee (hardware device). See http://devwiki.electricimp.com/doku.php?id=glossary for more on the electric imp terminology.

Step 5: Commissioning Your Imp

Before you can use your imp, you need to commission it. See http://devwiki.electricimp.com/doku.php?id=commissioning for instructions. Note, you have done step 1 in the commissioning document already by working through the steps here in this instructable to this point.

If you do not have an iPhone, iPad or Android device as the commissioning instructions requires, don’t despair nor do I. Electron Hobbies have posted Fen Consultants Electric Imp Blink-Up app for Windows. (http://electronhobbies.com/wireless/110-electric-imp.html in the download tab). I used this with my HP Elitebook laptop which has an LED display and worked first time. Just remember to hold your thumb and finger over the exposed top and bottom of the Imp when doing it.

Step 6: Create the Firmware

The firmware is the program that runs on the hardware and controls it.

At this stage, I'm assuming you have logged into the electric imp planner (based on you have just commissioned your imp). The planner is at https://plan.electricimp.com/

You should be seeing a blank planner screen. This is your IDE or Integrated Development Environment. It has two parts, the graphical part  which is where you connect the various components to make an end to end application and the code part where you type your code. The first picture is the graphical screen.

You should now click on the "code" link in the black menu bar. This changes the window to show all of your code you have written along with a few Public firmware items as per the second picture.

Step 7: Open the Code Window

You will see a plus (+) top left just under the imp logo (first picture). Click on it. A dialog will open asking for a name. Enter a name for your program. Suggest call it ‘Wireless Thermistor’ and click ok. You should see what is in the second picture.

This is where we will enter the code we want the imp to run.

The buttons along the top have balloon text that shows when you hover your mouse over them. Take a moment to have a look.

Step 8: Code Description Line

The very first line of code should be a comment line describing the application. This is read and displayed on your code list page as the description next to the name. Do this now entering the following:

// Measure temperature in C using a simple thermistor

Step 9: Create the Sensor Class

We’re going to create a class to handle the sensor. I called mine ‘TermistorCelcius’. We enter this code just below where we put our description in the last step.

This class can be used for any of the Vishay range of thermistors that have the four constants for use with the extended Steinhart–Hart equation.

 class ThermistorCelcius
{
   analogPort = null; // gets assigned the hardware pin
   ROhms = null;      // Resistors resistance in ohms
   Rref = null;       // Thermistors resistance at 25C
   // variables to hold the constants specific to the thermistor
   // for the Steinhart–Hart equation
   A = null;
   B = null;
   C = null;
   D = null;

   // Value to say if the thermistor side of the
   // thermistor/resistor voltage divider is connected to GND
   ThermToGND = null;

   constructor(
       port,          // pin number thermistor connected to
       ResistorOhms,  // resistor value in ohms
       ThermistorOhms, // thermistor resistance @ 25C in ohms
       constA,        // constant provided from datasheet
       constB,        // constant provided from datasheet
       constC,        // constant provided from datasheet
       constD,        // constant provided from datasheet
       thermistorToGND)// 1 if thermistor side = GND
   {
       // The constructor of a class is called when the class is
       // initiated. Its variables are passed to the class at this
       // time.
       ROhms = ResistorOhms;
       Rref = ThermistorOhms;
       A = constA;
       B = constB;
       C = constC;
       D = constD;
       ThermToGND = thermistorToGND;

       // Provide a method to be able to use the same class
       // regardless of port.
       switch (port)
       {
           case 1:
               hardware.pin1.configure(ANALOG_IN);
               analogPort = hardware.pin1;
           break;
           case 2:
               hardware.pin2.configure(ANALOG_IN);
               analogPort = hardware.pin2;
           break;
           case 5:
               hardware.pin5.configure(ANALOG_IN);
               analogPort = hardware.pin5;
           break;
           case 7:
               hardware.pin7.configure(ANALOG_IN);
               analogPort = hardware.pin7;
           break;
           case 8:
               hardware.pin8.configure(ANALOG_IN);
               analogPort = hardware.pin8;
           break;
           case 9:
               hardware.pin9.configure(ANALOG_IN);
               analogPort = hardware.pin9;
           break;
           default:
               server.log("Invalid port specified.");
           break;
       }
   }

   function readTemp()
   {
       // get the current voltage the imp uses as Vref for the
       // analog to digital conversion
       local hwvolts = hardware.voltage();

       // Perform an analog to digital conversion and store the 16
       // bit value
       local data = analogPort.read();    

       if (data == null)  // if we didn't get anything,
       {
           server.log("Read failure");    // report a failure
           return false;                  // and return false
       }

       // convert the number from the ADC to a voltage
       local voltage = data * (hwvolts / 65535);  

       // depending on how you wired the 3.3V and GND on the
       // Resistor/Thermistor voltage divider, select the
       // appropriate formula to calculate the resistance of
       // the Thermistor.
       local ohms = null;
       if (ThermToGND == 1)
           // used with resistor on 3.3V and thermistor on GND
           ohms = ROhms/((hwvolts/voltage)-1);    
       else
           // used with resistor on GND and thermistor on 3.3V
           ohms = ((ROhms * hwvolts)/voltage) - ROhms;  

       // We're going to use an extended Steinhart–Hart equation to
       // calculate the temperature based on the calculated
       // resistance of the thermistor. We'll include the
       // conversion factor from Kelvin to Celsius
       local tempC = 1.0/(A + B * math.log(ohms/Rref) +
           C * math.pow(math.log(ohms/Rref), 2) +
           D * math.pow(math.log(ohms/Rref), 3)) - 272.15;
       return tempC;      // and return the temperature   
   }
}


Step 10: Add the Main Code for the Firmware

Next we add the main part of the code that uses the class. You add this below the text you have pasted (or typed) in the last step.

 // Set the constant values for the Steinhart–Hart equation as
// provided by the application from
// http://www.vishay.com/resistors-non-linear/ntc-curve-list/
local constA = 0.003354016; // A1
local constB = 0.00025698501802; // B1
local constC = 0.0000026201306709; // C1
local constD = 0.000000063830907998; // D1

// Instantiate the sensor with the required values
local tempSensor = ThermistorCelcius(2, 10000, 4700, constA, constB, constC, constD, 0);    

// Create the output port to send temperature readings
local output = OutputPort("Temp, Volts, Location", "table");

local iteration = 0;   // used to count the number of samples taken
local tempval = 0;     // store the accumulated value for averaging

// Capture and log a temperature reading every 30s
function capture()
{
   // Set timer for the next capture
   // in 0.1 seconds time, do the function 'capture'
   imp.wakeup(0.1, capture);

   // Output the value after collecting 50 samples and averaging
   // Get a temperature reading using the class above
   local temperature = tempSensor.readTemp();

   // If we have 50 samples, then reset counter and output
   // average value
   if (iteration == 50)
   {
       iteration = 0;
       // Create a table with the key:value pairs to send
       local dTable = {
           "temp" : tempval/50,
           "volt" : hardware.voltage(),
           "location" : "Above office switch"
       }
       output.set(dTable);
       server.show(format("Sent: 'temp':%3.1f,'volt':%3.2f", tempval/50, hardware.voltage()));
       tempval = 0;
   }
   else   // otherwise keep collecting samples
   {
       iteration++;
       tempval = tempval + temperature;
   }
}

// Register with the server
imp.configure("Wireless Thermistor", [], [output]);

// Start capturing temperature
capture();


Step 11: Start Your Impee and See It in Your Planner

At this point, we’ve created our code to handle the sensor and create an output. We now need to do something with this output. We do this in the planner of the IDE.

Make sure your impee is plugged in and turned on. Then click on “planner” link at the top.

The blue box you are seeing (with ‘BLANK’ in it) is an Impee. An Impee is your piece of hardware and it’s unique. There is no other one the same in the world.

Note that if by chance you delete this Impee from the planner (click on the Impee and click the ‘delete node’ button, the way to get it back is by power cycling your hardware. It will then come back.

Step 12: Tell Your Impee What Firmware to Run

You may have noticed I switched browsers (Firefox to Crome). The operation is the same in both these browsers. Note that at time of writing this article, Internet Explorer does not work with the planner.

You will notice your Impee has a button top right and a link bottom right. Click on the top right button and a dialog box will open with a drop down field. Click on the down arrow to open up the list.

You can see a list of various firmware both public and your own created firmware. Under ‘Your firmware’ you will see ‘Wireless Thermistor’ which is the firmware you have just created. Select it. You will see the name of your Impee change to ‘WIRELESS THERMISTOR’ and soon after that, you will start seeing the message ‘Sent: xx.x” (xx.x is some number in degrees).

Step 13: Somethings Wrong

With all the best laid plans, sometimes something isn't right. So take a quick stop here. Let’s say you are seeing numbers that are just plain and simple wrong. Example when it’s obviously in the 20 – 25C range but the Impee is showing -3.5, you have the voltage polarity across the thermistor/resistor not agreeing with what is in the code.

So a bit of an apology at this time. If you have followed the above, you should be seeing this issue. I did this on purpose so the exercise would force us to look at how we make changes to the firmware.

Two options, change it in hardware (ie swap the 3V3 and GND) or change in code by changing the last parameter of the class instantiate command from a 0 to a 1 or a 1 to a 0. Let’s do it in code for the sake of the exercise (even if it’s not wrong) from the planner screen, click on the edit link (bottom right of the Impee) and the code window will open.

You’re looking for the line which is around row 127:

local tempSensor = ThermistorCelcius(2, 10000, 4700, constA, constB, constC, constD, 0);    

Simply change the 0 on the end to a 1.

Now click on the save button (left one), then you need to click on the ‘Run code’ button (next one to the right from save). At the bottom of the code screen, you should be seeing server messages similar to what is in this picture.

Step 14: Add HTTP Request Node

Ok now we have our impee measuring temperature and displaying it in the planner. It’s not going any further yet. So let’s get it to update a web page. We don’t write any code to handle this. It’s all done by the electric imp Cloud. What we do is in the planner (click on planner) so you can see your impee.

We’re going to add a node. Do this by clicking on the ‘add node’ button (top left). A menu appears with various different nodes.

Look for the HTTP request node and click on the Add button. The menu will close and a green box will appear similar to your Impee box.

This is one of the magic things about the electric imp. All these things that used to be challenging for us to write are already done. We just add them.

Step 15: Create a Noodle Between Your Impee and the HTTP REQUEST Node

The top left will have the name HTTP REQUEST and top right will have the button to allow configuring the node. Grab it with your mouse and move it to the right of your Impee box.

That node, like all the other nodes run on the Amazon Cloud, not on the Imp itself. The Imp communicates via a secure channel to the cloud and the cloud performs the node functions. Only the code you create runs local on the imp.

To create the connection between your impee and the HTTP Request node, you click on the little + top right just outside of your impee box. Now move your mouse to hover over the HTTP REQUEST node and click again. Between the first click (of the +) and the second click on the node, a blue arrow should appear from your impee to the node. Once you have clicked on the HTTP REQUEST node box, the arrow turns black. This connection is called a noodle and is the secure connection from your imp to the cloud server.

Step 16: Configure HTTP REQUEST Node

To configure the HTTP REQUEST node, click on the configure button at the top right of the green node box. You need to enter a URL pointing to a file that will process the data. Also select ‘application/json’ as the Content Type. Don’t worry about the method, as the JSON data format doesn’t use it.

Note that the server should be visible from the Amazon Cloud, so it can’t be a private IP address (eg 192.168.1.2). If you don’t have a public web server, use a free dynamic DNS service like no-ip.org to create a DNS name for your public network address of your router and set up port forwarding on your router to get to a private network machine.

Step 17: Get Data to the Web Server

Now we have the Imp all working and sending data, we’d better create the required web pages to receive the data and display it. This example we will use a combination of php and some javascript to do this. The picture is a flow of how the various interactions work.

From this point, we're going to be creating text files locally for uploading to a web server. Create a folder on your hard drive and call it something unique. Maybe call it 'imp'. This is where you will save the files you create in these next steps.

Creating the files, you can use notepad or the MAC equivelant text editor. Or if you are really keen, you can download eclipse from http://www.eclipse.org/downloads/packages/eclipse-php-developers/heliosr which is an integrated development environment for php.

Step 18: Recieving at the Web Server

Create a file called JSON.php

JSON.php is a server based file that the imp cloud service calls, passing the data from your imp application to it as JSON formatted data. It’s function is to decode the data, log it to a log file (or a database), and save it in a format for easy display.

 <?php
// sript to recieve the URL Encoded JSON data from the IMP
// JSON string will look similar to:
//    {"value":{"volt":3.2780001163482666,"temp":24.332368850708008},"target":"300e600a779951c4","channel":1 }

function jsonToArray ($json) {
// Converts JSON object to an standard Array
$json = substr($json, strpos($json,'{')+1, strlen($json));
$json = substr($json, 0, strrpos($json,'}'));
$json = preg_replace('/(^|,)([\\s\\t]*)([^:]*) (([\\s\\t]*)):(([\\s\\t]*))/s', '$1"$3"$4:', trim($json));
return json_decode('{'.$json.'}', true);
}  

// Get the data into an array we can work with
$dArray = jsonToArray(file_get_contents('php://input'));

// extract each component into it's own variable for ease of
// reading. Note, this is not mandatory as it's really
// doubling up but can make code a little easier to read.
$target = $dArray['target'];
$channel = $dArray['channel'];
// note that the data you define and send in your Imp code is
// an array within the 'value' key/value pair.
$temp = $dArray['value']['temp'];
$volt = $dArray['value']['volt'];
$location = $dArray['value']['location'];

// create a date/time stamp
// see http://php.net/manual/en/timezones.php for supported timezones
date_default_timezone_set("Australia/Adelaide"); 
$dateTime = date('Y-m-d H:i:s'); //'Y-m-d H:i:s'

// We'll create a CSV format log file. Note, depending on update frequency, this file can become
// very large!!!! You could also change this to log the data to a mySQL server instead.
$fa = fopen("data.csv", 'a');
if(flock($fa, LOCK_EX))
{
 fwrite($fa, $target . "," . $channel . "," . $dateTime . "," . number_format($temp, 2) ."," . number_format($volt, 2) . "\n");
 fflush($fa);
 flock($fa, LOCK_UN);
}

// Create the file that can be read for live updates in a web browser.
$fw = fopen("latestImpData.txt", 'w');
if(flock($fw, LOCK_EX))
{
 fwrite($fw, $target . "|" . $channel . "|" . $dateTime . "|" . number_format($temp, 1) ."|" . number_format($volt, 2) . "|" . $location);
// fwrite($fw, file_get_contents('php://input'));
 fflush($fw);
 flock($fw, LOCK_UN);
}
?>

Step 19: Display the Data in Web Browser Step 1

So that covers getting the data to the Web Server. The next stage is getting it displayed in your web browser.

Create a new file called index.php and copy the following code into it.

Anything between a "<!--" and a "-->" is a html comment which the server and web browser will ignore.

 <<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>My mini weather station</title>

<!-- The above section would be omitted if you are adding this to an existing web page. The next bit of code needs to be between the <head> and </head> tags in your existing page. -->

<!-- include the jquery library from google to provide the JSON functionality -->
<script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js' charset='utf-8'></script>

<script type="text/javascript">

function displayLiveData()
{
 // Request the data in JSON format from the server
 var currentTime = new Date();
 $.getJSON('./getJSONdata.php?datatype=full&' + currentTime.getTime(), function(data) {
   var items = [];
 // work through the set of key/val pairs and tell the browser where to update
 $.each(data, function(key, val) {
  switch(key)
  {
   case "impID":
    document.getElementById("impID").innerHTML = val;
   break;
   case "chan":
    document.getElementById("chan").innerHTML = val;
   break;
   case "date":
    document.getElementById("date").innerHTML = val;
   break;
   case "time":
    document.getElementById("time").innerHTML = val;
   break;
   case "temp":
    document.getElementById("temp").innerHTML = val;
   break;
   case "volt":
    document.getElementById("volt").innerHTML = val;
   break;
   case "location":
    document.getElementById("location").innerHTML = val;
   break;
  }
   });

 });
}

onload = function ()
{
 // Set a time interval to do the update of text on the page
 t = window.setInterval("displayLiveData()", 5000);
};

</script>

<!-- if adding to an existing page, you would have pasted the above before the </head> tag in that page -->

</head>

<!-- Put this next code between the </head> and <body> tags in your existing page. This is some PHP script that reads the latest values to provide the initial display of data. Javascript doesn't display when page first loads. -->

<?php
// Read contents of latestImpData.txt and split values to
// variables for the initial display
$data = file_get_contents("latestImpData.txt");
$convert = explode("|",$data);
$impID = $convert[0];
$chan = $convert[1];
$datetime = $convert[2];
$temp = $convert[3];
$volt = $convert[4];
$location = $convert[5];
$dt = explode(" ",$datetime);
$date = $dt[0];
$time = $dt[1];
?>

<!-- end of the php reading of values part -->

<body>
<h1>My mini weather station</h1>
<p>This is a simple piece of code that reads the voltage from a Thermistor, converts to C and displays.</p>
<ul>
<!-- Display the list of data initially using php with Javascript taking over -->
<!-- after 5 seconds to continually update -->
<li>Imp ID: <span class=class="ajax" id="impID" style="font-size:15px"><?php echo $impID; ?></span></li>
<li>Channel: <span class=class="ajax" id="chan" style="font-size:15px"><?php echo $chan; ?></span></li>
<li>Imp Location: <span class=class="ajax" id="location" style="font-size:15px"><?php echo $location; ?></span></li>
<li>Date: <span class=class="ajax" id="date" style="font-size:15px"><?php echo $date; ?></span> (Adelaide/Australia)</li>
<li>Time: <span class=class="ajax" id="time" style="font-size:15px"><?php echo $time; ?></span> (Adelaide/Australia)</li>
<li>Temperature: <span class=class="ajax" id="temp" style="font-size:15px"><?php echo $temp; ?></span>C</li>
<li>Imp voltage: <span class=class="ajax" id="volt" style="font-size:15px"><?php echo $volt; ?></span>V</li>
</ul>
<p>You can see a full log since start of testing by <a href="data.csv">clicking here</a></p>
</body>
</html>

This file is a mix of html, php and javascript and displays a fairly basic page that updates the values every 5 seconds.

Step 20: Display the Data in Web Browser Step 2

This last file is the one called by the javascript in index.php. It reads latestImpData.txt (created by the JSON.php file) and sends the data as JSON data to the web browser for the javascript to read and display.

Creat a file called "getJSONdata.php" and copy the following code into it.

 <?php
// Read the data from latestImpData.txt file
$data = file_get_contents("latestImpData.txt");

// What should be in the latestImpData.txt file:
//         impID   channel date     time  temp volt location
//    300e600a779951c4|1|2012-12-27 10:39:06|24.9|3.28|Above office switch

// break up the data into chuncks based on the | character
$convert = explode("|",$data);

// now break up the date/time into seperate date and time
$datetime = $convert[2];
$dt = explode(" ",$datetime);

// create an array which will be sent to the JSON encoder
$json_array = array (
'impID' => trim($convert[0]),
'chan' => trim($convert[1]),
'date' => trim($dt[0]),
'time' => trim($dt[1]),
'location' => trim($convert[5]),
'temp' => trim($convert[3]),
'volt' => trim($convert[4])
);

// encode the array and display it for the
// javascript in index.php to read
echo json_encode($json_array);
?>

Note: if you run this from your web browser you will see how the data is formated and sent.

Step 21: Upload to Web Server and Test

Now it's time to put the three files you have just created on a web server. As said earlier, the web server has to be accessible from the Amazon Cloud. If you don't have a web server, the Amazon Cloud may be an option to play with as they provide free trial subscriptions. There are a few others like Microsoft. A web hosting account with GoDaddy is also very cheap. 

The other alternative is set something up on your local computer that will process php files. If you do this, you will need to set up some network address translation (NAT) on your router to forward requests on port 80 to your machine.

Turn on your impee, and go to your web address that you have set up and presto, a mini weather station on the cheap :)

SciStarter Citizen Science Contest

First Prize in the
SciStarter Citizen Science Contest

Instructables Design Competition

Participated in the
Instructables Design Competition