2009
11.12

An arduino, a DB18S20 one-wire temperature sensor and an LDR (Light Dependent Resistor) are hooked up with some resistors and an ethernet shield to make a datalogger and webmonitor.

 Arduino temperature data logger

Notes :

  • for this setup the connections are made on the ethernet shield instead of directly on the arduino. Since Fritzing misses a library with external shield I used the arduino, the pinout stays the same either way.
  • check the DS18B20 datasheet for the specifications on hooking up the sensor in parasite or powered mode

Schematics :

temp light monitor tiny bb Arduino temperature data logger

The arduino reads the sensors every 10 seconds and passes the data to a php script on your website; which then stores the data in a text file. A second script generates a line chart of the collected data.

tempgraph.php Arduino temperature data logger

22 hours of temperature data

I started from Cyberspice’s code and changed the arduino code to make use of the DB18S20 and the ethernet shield.

A variable was added to the upload php script in order to store the light intensity in addition to the temperature.

The graphing script was altered to also display the minimum temperature, the maximum temperature, the current temperature and the average temperature. A raster was added for a better interpretation of the plotted data and a green line is showing the average temperature  troughout the chart.

You need to alter your ip, domain and path as I explained in the post Arduino Wireless motion detector so I won’t go into those details here.

The arduino sketch :

#include <OneWire.h>
#include <string.h>
#include <Ethernet.h>

int count = 0;
OneWire ds(8);
int HighByte, LowByte, TReading, SignBit, Tc_100, Tf_100, Whole, Fract;
int Tcount = 1;
int W[6];
int F[6];
int photocellPin = 0;     // the cell and 10K pulldown are connected to a0
int light;     // the analog reading from the sensor divider

byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 
  192, 168, 1, 36};
byte server[] = { 77, 222, 78, 32};
// String buffer
char buffer[256];

Client client(server, 80);

void setup()
{
  Ethernet.begin(mac, ip);
  Serial.begin(9600);

  delay(1000);

  // Serial.println("connecting...");


}

void loop()
{
  byte i;
  byte present = 0;
  byte data[12];
  byte addr[8];

  if ( !ds.search(addr)) {
    ds.reset_search();
    return;
  }

  ds.reset();
  ds.select(addr);
  ds.write(0x44,1);         // start conversion, with parasite power on at the end

  delay(1000);            

  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // Read Scratchpad

  light = analogRead(photocellPin);  
  Serial.print("light intensity = ");
  Serial.print(light);
  Serial.print("\n");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
  }

  LowByte = data[0];
  HighByte = data[1];
  TReading = (HighByte << 8) + LowByte;
  SignBit = TReading & 0x8000;  // test most sig bit
  if (SignBit) // negative
  {
    TReading = (TReading ^ 0xffff) + 1; // 2's comp
  }
  Tc_100 = (6 * TReading) + TReading / 4;    // multiply by (100 * 0.0625) or 6.25

  Whole = Tc_100 / 100;  // separate off the whole and fractional portions
  Fract = Tc_100 % 100;

  if (SignBit) // If its negative
  {
    Serial.print("-");
  }
  Serial.print(Whole);
  Serial.print(".");
  if (Fract < 10)
  {
    Serial.print("0");
  }
  Serial.print(Fract);
  Serial.print(" C\n");

  if (client.connect()) {
    Serial.println("connected");
 sprintf(buffer, "HEAD /path/to/script/newtemp.php?count=%d&temp=%d.%d&light=%d HTTP/1.1", Tcount, Whole, Fract, photocell);
 client.println(buffer);
 client.println("Host: yourdomain.com");
 client.println("Connection: close");
 client.println();
 client.stop();
 Tcount++;
 }
 else {
 Serial.println("connection failed");
 }
 delay(60000);  // wait 1 minute before sending new data
}

The upload script  ‘newtemp.php’ :

<?php
 
if ($_SERVER['REQUEST_METHOD'] == 'HEAD') {
 
	$fields = explode("&", $_SERVER['QUERY_STRING']);
	$values = array();
 
	foreach ($fields as $field) {
		$keyval = explode("=", $field);
		$values[$keyval[0]] = $keyval[1];
	}
 
	$file = fopen('./tempdata.txt','a+');
	if ($file) {
		fwrite($file, time() . ':' . $values['count'] . ':' . $values['temp'] . ':' . $values['light']);
		fwrite($file, "\n");
		fclose($file);
	}
}
 
?>

The graphing script ‘tempgraph.php’ :

<?php
 
define('WIDTH', 1000);
define('HEIGHT', 520);
define('FONT', 1);
 define('FONT2',2);
 
// Get the temperature data
$data = array();
$file = fopen("./tempdata.txt","r");
if ($file) {
	while (!feof($file)) {
		$line = trim(fgets($file));
		if (strlen($line)) {
			$fields = explode(":", $line);
			$keyval = array();
			$keyval['time']        = $fields[0];
			$keyval['count']       = $fields[1];
			$keyval['temperature'] = $fields[2];
			$keyval['light'] 	   = $fields[3];
			$data[] = $keyval;
		}
	}
 
	fclose($file);
}
 
// Get the number of data points
$datapoints = count($data);
 
// Lines are chronological
$mintime = $data[0]['time'];
$maxtime = $data[$datapoints - 1]['time'];
 
// Temperatures need to be processed.
$mintemp = $data[0]['temperature'];
$maxtemp = $data[0]['temperature'];
 
foreach ($data as $datapoint) {
	$mintemp = $mintemp < $datapoint['temperature'] ? 
	           $mintemp : $datapoint['temperature'];
	$maxtemp = $maxtemp > $datapoint['temperature'] ? 
	           $maxtemp : $datapoint['temperature'];
	$avgtemp += $datapoint['temperature'];
	$curtemp = $datapoint['temperature'];
}
 $avgtemp = round(($avgtemp / $datapoints),2);
 
// Get the axis dimensions.  Round up and down to the nearest
// degree C and hour.
$lowtime  = intval($mintime / 3600) * 3600;
$hightime = (intval($maxtime / 3600) + 1) * 3600;
$difftime = $hightime - $lowtime;
 
$lowtemp  = intval($mintemp);
$hightemp = intval($maxtemp) + 1;
$difftemp = $hightemp - $lowtemp;
 
// Create the image
$image = imagecreate(WIDTH, HEIGHT);
if ($image) {
	$background = imagecolorallocate($image, 255, 255, 255);
	$black      = imagecolorallocate($image, 0, 0, 0);
	$red        = imagecolorallocate($image, 255, 0, 0);
	$blue       = imagecolorallocate($image, 0, 0, 255);
	$green		= imagecolorallocate($image, 0, 204, 5);
	$grey		= imagecolorallocate($image, 210,210,210);
 
	// Draw the axes
	imageline($image, 20, 20, 20, 485, $black);
	imageline($image, 15, 480, 980, 480, $black);
	imageline($image, 15, 20, 20, 20, $black);
	imageline($image, 980, 480, 980, 485, $black);
	
	// Draw top horizontal line
	imageline($image, 20, 20, 980, 20, $grey);
	
	// Draw right most vertical line
	imageline($image, 980, 20, 980, 479, $grey);
	
	
	for ($i = 3600; $i < $difftime; $i += 3600) {
		$x = 20 + (($i * 960) / $difftime);
		// Draw ticks on horizontal axis
		imageline($image, $x, 480, $x, 483, $black);
		// Draw horizontal guides
		imageline($image, $x, 479, $x, 20, $grey);
	}
	$temp = intval($lowtemp);
	for ($i = 1; $i < $difftemp; $i++) {
	    $y = 480 - (($i * 460) / $difftemp);
		// Draw ticks on vertical axis
		imageline($image, 17, $y, 20, $y, $black);

		// Draw vertical guides
		imageline($image, 21, $y, 980, $y, $grey);
		$temp = intval($temp) + 1;
		$ytemp = intval($y) - 5;
		//if($temp % 2 == 0){
			imagestring($image, FONT, 0, $ytemp, $temp . 'C', $black);
		//}
	}
	

 
	// Draw the labels
	imagestring($image, FONT, 8, 490, date("H:i", $lowtime), $black);
	imagestring($image, FONT, 970, 490, date("H:i", $hightime), $black);
	imagestring($image, FONT, 0, 475, $lowtemp . 'C', $black);
	imagestring($image, FONT, 0, 15, $hightemp . 'C', $black);
 
 
	// Draw min, max and avg
	imagestring($image, FONT2, 700, 430, "Cur. temp : " . $curtemp . " °C",$black);		
	imagestring($image, FONT2, 700, 450, "Avg. temp : " . $avgtemp . " °C",$green);	
	imagestring($image, FONT2, 850, 430, "Min. temp : " . $mintemp . " °C",$blue);
	imagestring($image, FONT2, 850, 450, "Max. temp : " . $maxtemp . " °C",$red);

	// Draw avg line
	
	$avgtemp = 480 - ((($avgtemp - $lowtemp) * 460) / $difftemp);
	imageline($image,20, $avgtemp, 980, $avgtemp, $green);
	
	// Draw the temperature points
 
	// Position of the first point
	$prevx = (($data[0]['time'] - $lowtime) * 960) / $difftime;
	$prevy = (($data[0]['temperature'] - $lowtemp) * 460) / $difftemp;
 
	// Draw line from previous point to current point
	for ($i = 1; $i < $datapoints; $i++) {
		$x = (($data[$i]['time'] - $lowtime) * 960) / $difftime;
		$y = (($data[$i]['temperature'] - $lowtemp) * 460) / $difftemp;
		imageline($image, $prevx + 20, 480 - $prevy, $x + 20, 480 - $y, $red);
		$prevx = $x;
		$prevy = $y;
	}
 
	
	// Finally time and date stamp
	$generated = 'Generated: ' . date("r");
	imagestring($image, 
	            FONT,
	            WIDTH - 15 - (imagefontwidth(FONT) * strlen($generated)),
	            HEIGHT - 15, 
	            $generated,
	            $blue);
 
	// Output the image
	header('Content-Type: image/png');
	imagepng($image);
 
	// Destroy it
	imagedestroy($image);
}
 
?>

I will soon post some improvements on the scripts :

  • averaging 6 readings ( 1 minute ) to 1 datapoint to get rid of the noise
  • let the script fill one text file for each day, so you can look at a history over several days
  • split up data into different charts
  • last but not least, trying to add the S65 in the game : showing the current sensor readings or a graph

 Arduino temperature data logger

Please leave a comment or send me a link to show your arduino projects!

Back to homepage

11 comments so far

Add Your Comment
  1. Looks great !

  2. And would it be possible to connect 2 temprature sensors ? Inside and Outside temprature ?

    That would be handy.. :)

    Just starting with the whole Arduino programming…..

  3. It is definitely possible to connect 2 or more ds18B20’s in series.

    I was planning on doing this myself when the winter kicks in. I’m renting a house which has double glazing except on 2 doors connecting a hallway. The amount of cold radiation coming from these glass surfaces is really huge. Last winter we found a cheap solution : putting heavy curtains in from of both doors. Thus creating an air buffer and blocking draft and cold radiation. I was positively surprised on the amount of cold it stopped from entering or rather heath it stopped from getting out.

    This year I’m improving the insulation by adding a layer of bubblewrap over the glass leaving about an inch of air between the glass and the bubblewrap.

    So it’s

    outside ~~ |glass| ~~ |bubblewrap| ~~ |curtain| ~~ inside

    I’ll be using 4 ds18B20’s to measure the Tout, Tin and the 2 temperatures in between and a wifi shield to send the data to a website.

    Tout ~~ |glass| ~~ T1 ~~ |bubblewrap| ~~ T2 ~~ |curtain| ~~Tin

    Here’s a howto on how to connect db18S20’s in series : link

    Feel free to post links to your projects.

  4. Thanks for the link! I am going to take a look tomorrow. Now I have to get some sleep… :)
    It it midnight overhere.

    I have already build some simple things with an Arduino, but mostly with sample codes from other people. I am more a electronics guy, but I am gettitng some where with the code stuff. Wt some copy paste work I have made a Arduino to switch on the central heating of a house oer the internet. :) Now I am trying to also get the temprature information back. I am experimenting wit a LM35 temprature sensor, because the One Wire stuff is still al little to difficult.

    But hey, the bst thing abouta Arduino is that you can always change it very fast and simple. So the next experimet wil be a 18S20 sensor.

    I am very curious how the temprature differences are going to be with your next insulation project. I think the differencescan b very huge, fun to watch :)

    What kind of Wifi shield are you going to use ? I still have everything with a cable… :(

    Thanks for your time !

    Remco ( The Netherlands)

  5. This is such a cool project.

    I’ve just started (not finished yet, hopefully out by April) a similar one that does the following:

    – Arduino logs data
    – A Processing win app graphs it and sends the data to
    http://www.pachube.com for online graphs // they do all the php stuff for me

    Your project does it in a neater way. The only problem is I know nothing about php. :-(

    p.s. any idea what caused all those mini spikes in your graph?

    student scott from south africa

  6. I am just a rookie to the Arduino. But, I was just wondering.

    ‘newtemp.php’, ‘tempgraph.php’

    Are these files being stored on the arduino’s memory or is it running on a standalone computer?

  7. @ Geo

    The files are stored on a local computer running an Apache web server (like XAMPP) or on your website running on a remote webserver.

  8. @ boolScott

    Hi,

    and thanks.

    First off I started from Cyberspice’s code so the largest part of php was already written. I didn’t know any php before and by just looking at the code I could easily add some variables and alter it to fit my needs.

    The spikes in the graph are caused by using too many datapoints (and the way to represent them) and the ‘inacurracy’ of the temperature sensor. When a temperature of 19.4 degrees is measured the sensor will register anything from 19.0 to 19.8 over a period of time. A more correct way to get rid of the spikes is to do some more calculation and make the graph using exponential smoothing or moving average. This way the spikes will even out and the graph will become more fluent.

    If you have any pachube stuff you would like to share, go ahead!

  9. Very Cool! and exactly what I needed.

    Awesome Work.

  10. Hi,

    the spikes in the graph are caused by wrong interpretation by printing in the buffer:

    sprintf(buffer, “HEAD /path/to/script/newtemp.php?count=%d&temp=%d.%d&light=%d HTTP/1.1″, Tcount, Whole, Fract, photocell);

    If the temperature is 20.05 °C you print in the buffer 20.5° (and the spike comes up)
    Change ‘temp=%d.%d’ to ‘temp=%d.%02d’ and it works fine.

    I had the same Problem – so I think that is the solution.

  11. Awesome code! I was trying to run this but I found that my data file was missing points. I saw that ‘count’ was one of the fields written to the file and it jumps from 2 to 4 and skips over 3 for example. Has anyone one else encountered this?

Please leave these two fields as-is:
Prove you are human by reading this resistor:
0Ω+/- 5%

0
0
1
2
3
4
5
6
7
8
9

0
0
1
2
3
4
5
6
7
8
9

0
0
1
2
3
4
5
6
7
8
9

5
5
10
20

Match the sliders on the left to each color band on the resistor.

Click Here for a new resistor image.

If you'd like to learn more, read about resistor color codes here.