Mar

4

Dynamic Dials Disclosed

16 years ago, at the start of March | Leave a Comment

There’s been quite a bit of interest in my electricity usage dial like the one below so I’m going to go through how I did it with you. The first thing you’ll need is the XML/SWF Gauge and a web server with PHP. It’s possible to do this on a web server running Perl or usage dialASP but you’ll need to do that yourself. For this example I’m also using a Pachube account to grab the data. My feed, the one for my gauge on the top right, is 1498 but you will want to create your own account and start updating your own data. Using the gauge is as simple as a simple thing.

There are a number of things we need to get this simple display working,


1. The .SWF file

We’ll come to that at the very end of the article.


2. The gauge.html file.

Open your favourite text editor and cut and paste the following into it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<HTML>
<BODY bgcolor="#FFFFFF">
<OBJECT classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
        codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0"
        WIDTH="200"
        HEIGHT="100"
        id="gauge">
<PARAM NAME="movie" VALUE="/data/gauge.swf?xml_source=/getdata.php" />
<PARAM NAME="quality" VALUE="high" />
<param name="wmode" value="transparent">
<param name="allowScriptAccess" value="sameDomain" />
<EMBED src="/data/gauge.swf?xml_source=/getdata.php"
        quality="high"
        wmode="transparent"
        WIDTH="200"
        HEIGHT="100"
        NAME="gauge"
        allowScriptAccess="sameDomain"
        swLiveConnect="true"
        TYPE="application/x-shockwave-flash"
        PLUGINSPAGE="http://www.macromedia.com/go/getflashplayer">
</EMBED>
</OBJECT>
</BODY>
</HTML>

Save the file as gauge.html.


3. A data fetch PHP script.

Start a new document and cut and paste the following. Before you save the file as getdata.php make sure you change the $myFeedId and $myFeedStream values to match your Pachube feed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<?
$myFeedId=1498;
$myFeedStream=1;
$myURLPath='/getdata.php';
 
$url = "http://www.pachube.com/feeds/$myFeedId/datastreams/$myFeedStream/history.csv";
 
$now = gmdate('Y-m-d').'T'.gmdate('H:i:s').'Z';
 
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST , false );
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER , false );
curl_setopt($ch, CURLOPT_HEADER, 0);
 
ob_start();
$result = curl_exec($ch);
$resultStr = ob_get_contents();
ob_end_clean();
 
curl_close($ch);
 
if ($result) {
	$readings = split(",",$resultStr);
	$last=(($readings[count($readings)-1])/40);
	$prev=-90+(($readings[count($readings)-2])/40);
	$mval = (max($readings)/40);
	$tval=-90;
 
	header("Content-Type: text/xml\n\n");
?>
	<gauge>
		<update url='<?=$myURLPath;?>?m=<?=$mval?>' delay='909' delay_type='1' timeout='30' retry='9' /> 
<?
		RadialTicks( 100, 75, 60, 15, -90, -45, 12, 3, "33ff33" );
       		RadialTicks( 100, 75, 60, 15, -45, 0, 12, 3, "efe415" );
       	 	RadialTicks( 100, 75, 60, 15, 0, 45, 12, 3, "ef8b15" );
        	RadialTicks( 100, 75, 60, 15, 45, 94, 12, 3, "ff3333" );
?>
      		<text x='40' y='50' width='220' align='left' size='10' color='000000' alpha='20'><?=$now;?></text>
		<text x='150' y='100' width='120' align='center' size='24' color='000000' alpha='100'>Watts (W)</text>
 
       		<rotate x='100' y='75' start='-90' span='<?=$mval?>' step='3' shake_frequency='0' shake_span='3' shadow_alpha='15'>
	                <polygon fill_color='ff0000' fill_alpha='90' line_alpha='0'>
               		        <point x='97' y='5' />
       	        	        <point x='101' y='5' />
                        	<point x='102' y='63' />
               	        	<point x='96' y='63' />
	                </polygon>
			<rect x='94' y='87' width='10' height='10' fill_color='ff0000' fill_alpha='90' line_alpha='50' />
	        </rotate>
 
		<rotate x='100' y='75' start='<?=$prev?>' span='<?=$last?>' step='1' shake_frequency='95' shake_span='3' shadow_alpha='15'>
			<polygon fill_color='000000' fill_alpha='90' line_alpha='0'>
				<point x='100' y='10' />
				<point x='101' y='10' />
				<point x='101' y='63' />
				<point x='96' y='63' />
			</polygon>
			<rect x='94' y='87' width='10' height='20' fill_color='000000' fill_alpha='90' line_alpha='50' />
		</rotate>
		<circle x='100' y='75' radius='14' fill_color='000000' fill_alpha='50' line_alpha='0' />
	</gauge><?
}
 
function RadialTicks ( $x_center, $y_center, $radius,  $length, $start_angle, $end_angle, $ticks_count, $thickness, $color ) {
 
	for ( $i = $start_angle; $i <= $end_angle; $i += ($end_angle-$start_angle)/($ticks_count-1) ) {
 
		echo "		<line x1='".($x_center+sin(deg2rad($i))*$radius).
			"' y1='".($y_center-cos(deg2rad($i))*$radius)."' x2='"
			.($x_center+sin(deg2rad($i))*($radius+$length))."' y2='"
			.($y_center-cos(deg2rad($i))*($radius+$length))
			."' thickness='".$thickness."' color='".$color."' />\n";
	}
}
 
?>

Copy the .swf file that you downloaded from the XML/SWF Gauge site and put it on your web server along with your gauge.html and getdata.php files. Point your browser at your website and the gauge.html page and bask in your glorious meter display.

I should point out that this is using the historic feed data from pachube which has a 15 minute lag. The reason for this is simply because it does not require authentication with the pachube servers. If you want to use your live feed you will need to change the url used and the management of the data returned as well as adding

1
curl_setopt($curl, CURLOPT_USERPWD, "username:password");

just before the ob_start(); on line 16. Replace username:password with your actual username and password, don’t forget the colon in between. When changing the url you might want to use the secure https rather than just http.



[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Tagged with:
March 4, 2009 18:08

Feb

13

Current Cost Capers

16 years ago, mid-February | 4 Comments

My Unbioctium Current Cost CC128 arrived on Thursday and although I’m not going to actually get a chance in install it for a couple of weeks I did have a play. The first thing to do once it was all up and paired with the base was get the output into my laptop. To do this you will need to buy the data cable or make up your own…

At first I tried to be smart with reading from the serial port, as that seems to be a bit unreliable I fell back on the old tried and tested method. I also decided from the outset that I was going to parse the XML that the the CC128 spat out rather than just filter the bits needed with regexp.

You will need to make sure you have the perl modules Device::SerialPort and XML::Simple installed. If you don’t have them then as root on your linux box do the following:

perl -MCPAN -e shell

then install each module, for example Device::SerialPort,

install Device::SerialPort

and here’s the script which is also available for download.Updated to make it more reliable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#!/usr/bin/perl
 
use Device::SerialPort qw( :PARAM :STAT 0.07 );
use XML::Simple;
 
$port = "/dev/ttyUSB0";
 
$ob = Device::SerialPort->new($port) or die "Can not open port $port\n";
$ob->baudrate(57600);
$ob->write_settings;
$ob->close;
 
# using XML::Parser speeds xml parsing up lots!
$backend = 'XML::Parser';
$ENV{XML_SIMPLE_PREFERRED_PARSER} = $backend;
 
# we use this to only do 1 iteration (or not)
$escape=0; 
 
open(SERIAL, "<$port");
 
while($escape <= 0) {
	sleep(2);
 
	while ($line = <SERIAL>) {
 
		# for debug
		#print $line;
 
		$isValid = (index($line,"<msg>") != -1);
 
 
		if (!$isValid) { last; }
 
		print "This data is".($isValid==1?"":" not")." valid\n";
 
		# force XML::Simple to see this as a string not as a file
		# since XML::Simple is stupid and needs to be shot
 
		$line = "<fakeTag>$line</fakeTag>";
 
		$isHistoric = (index($line,"<hist>") != -1);
 
		$nref = XMLin($line,forcearray => 0);
 
		$ref = $nref->{msg};
 
		# just for reference, show if data is historic or not
 
		print "This data is".($isHistoric==1?"":" not")." historic\n";
 
 
		if (!$isHistoric) {
 
	        	$dsb        = 0 + $ref->{dsb};
        		$recordTime = $ref->{time};
        		$ccname     = $ref->{src};
        		$temp       = $ref->{tmpr};
			$ch1watts   = 0 + $ref->{ch1}->{watts};
			$sensor     = 0 + $ref->{sensor};
			$id         = $ref->{id};
			$type       = 0 + $ref->{type};
 
 
			if (defined $ref->{whatever}) {
				# do something based on whatever
 
			}
 
        		print "This $ccname was born $dsb days ago as at $recordTime - temperature is: $temp :: Current Watts in use on channel 1 are $ch1watts :: Sensor is $sensor, with an id of $id and a type of $type\n";
 
			# for cacti you'd probably just want to output CC_Temperature:$temp CC_Watts1:$ch1watts 
 
			# insert data into db
 
			# if you want to exit after a 'good' iteration set this to 1 otherwise set it to 0 (or don't change it to 1 :) );
			$escape=1;
		} else {
			#process or ignore historic data
		}
	}
}
 
close(SERIAL);

and here’s some example output, remember that I haven’t actually connected the loop around my electrical cable as yet.

This data is not historic
This CC128-v0.11 was born 1 days ago as at 21:16:44 - temperature is: 23.7 :: Watts used on channel 1 are 0 :: Sensor is 0, with an id of 02835 and a type of 1

Historic data isn’t processed at the moment but it would be trivial to do. I’m not processing it since I don’t really see the point if you’re going to store the results in a database.



[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Tagged with:
February 13, 2009 9:38

Feb

4

One Wireduino made easy

16 years ago, at the start of February | 12 Comments

For a very long time now I’ve been playing with the Arduino boards but it wasn’t until I recently discovered some DS1820 1-wire chips in my parts box that I started even thinking about 1-wire stuff. Connecting 1-wire devices to the Arduino is, well, amazingly simple. Using the sample circuit from the datasheet we can see the connections are few and far between.

Please note that this is the corrected circuit, previously the VDD was not tied to ground. While the circuit would work it would only give reliable results on very short runs. This circuit allows longer runs to the sensor.

Coding this up is also made extremely easy with the OneWire library. I’ve put together a little application below that will read all of the 1-wire devices on a bus. If you press L in the Arduino IDE serial monitor it will list all the 1-Wire device id’s and if you press T you’ll get the id and temperature reading.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/*
Simple 1-wire device reading
 
Pass T over serial to get temperatures
Pass L to list 1-wire devices
 
*/
#include <OneWire.h>
#include <stdio.h>
 
#define CONVERT     0x44
#define READSCRATCH 0xBE
#define SKIP_ROM    0xCC
#define MATCH_ROM   0x55
 
OneWire  ds(10);  // The DS18S20 is connected on pin 10
int ledPin = 13;  // flash an led on 13 - we all like flashing lights
 
void setup(void) {
  // initialize inputs/outputs
  // start serial port
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);  //we'll use the led to output a heartbeat
}
 
void loop(void) {
  int incomingByte = 0;
  if (Serial.available() &gt; 0) {
		// read the incoming byte:
		incomingByte = Serial.read();
 
		// say what you got:
		//Serial.print("I received: ");
		//Serial.println(incomingByte, DEC);
                if (incomingByte == 84) {
                  getStuff();
                  Serial.print("\r\n^");
                } else if (incomingByte == 76) {
                   listDevices();
                }
	}
 
}
 
void listDevices(void) {
 
  byte addr[8];
 
  ds.reset_search();
 
  while(ds.search(addr)) {
 
     digitalWrite(ledPin, HIGH);   // sets the LED on
 
     if ( OneWire::crc8( addr, 7) == addr[7])   {
         if ( addr[0] == 0x10) {
            // Make sure it is a DS18S20 device
            char buffer[512];
            sprintf(buffer,"Id:%02x%02x%02x%02x%02x%02x%02x%02x\r\n",
                       addr[0], addr[1], addr[2], addr[3], addr[4],
                       addr[5], addr[6], addr[7]);
              Serial.print(buffer);
          }
      }
  }
 
    Serial.print('^');
}
 
void getStuff(void) {
  byte i;
  byte present = 0;
  byte data[12];
  byte addr[8];
  char buff[15];
  float real_temp;
 
  float temp_count;
  float read_temp; 
 
  ds.reset_search();
 
  while(ds.search(addr))  {
 
     digitalWrite(ledPin, HIGH);   // sets the LED on
 
    if ( OneWire::crc8( addr, 7) != addr[7])  { // Check CRC is valid
        Serial.print("CRC is not valid");
    }
 
    if ( addr[0] != 0x10) {   // Make sure it is a DS18x20 device
      Serial.print("Device is not a DS18x20 family device.");
    }
 
    ds.reset();    // Reset device
    ds.select(addr);     // Select device
    ds.write(CONVERT,1);   // Issue Convert command 
 
    delay(1000);     // maybe 750ms is enough, maybe not
    digitalWrite(ledPin, LOW);    // sets the LED off
 
    present = ds.reset();  // Reset device
    ds.select(addr);  // Select device
    ds.write(READSCRATCH);  // Read Scratchpad
 
    for ( i = 0; i &lt; 9; i++) {  // we need 9 bytes
          data[i] = ds.read();
    }
 
    if(OneWire::crc8( data, 8) == data[8]) {  // Check CRC is valid
 
      // CRC is ok
      // Divide the temperature by 2 - note the » and
      // « need to be replaced with double < and >
      // wordpress kills the server when you try to 
      // save otherwise.
      read_temp=((data[1]«8) | data[0]) » 1 ;
     // Convert to real temperature
      temp_count=float(data[7] - data[6])/(float)data[7];
      real_temp = ((float)read_temp-0.25)+temp_count;
 
       // Convert float to ascii
      tempToAscii(real_temp,buff);
      char buffer[512];
      sprintf(buffer,"%02x%02x%02x%02x%02x%02x%02x%02x %s\r\n",
                  addr[0], addr[1], addr[2], addr[3], addr[4],
                  addr[5], addr[6], addr[7], buff);
 
      Serial.print(buffer);
 
    } else    {
       Serial.println("CRC Failed");
    }
  }    
 
void tempToAscii(double temp, char *buff) {
  int frac;
  //get three numbers to the right of the decimal point
  frac=(unsigned int)(temp*1000)%1000;
  itoa((int)temp,buff,10);
 
  strcat(buff,".");
  //put the frac after the decimal
  itoa(frac,&amp;buff[strlen(buff)],10);
}

There’s also a copy of the .pde file that you can download if you feel more inclined. It’s not an exact copy, I removed the commented out xml for the above version. It’s worth pointing out that this circuit is using what is called parasitic power, which means your cable run should be shortish. If you want to use an external power supply then pin 3 (VDD) on the DS18S20 should go to your external supply. This would allow for much longer runs of cable. Below is an example graph generated by cacti with 2 DS1820’s connected the the arduino. As you can see the temperatures aren’t exactly the same but this is to be expected since the accuracy of the DS1820 is +/- 0.5 ° C

The breaks in the graph were caused my me removing the USB plug from the PC it was connected to so that I could make some minor alterations to the circuit.

Josh asked how I was getting the input for Cacti, here’s the perl script I use

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/perl
 
$output = `echo -en "T" >>/dev/ttyUSB0 && cat /dev/ttyUSB0`;
 
@lines = split(/\r\n/,$output);
$t=1;
 
foreach $line (@lines) {
 
       @spl = split(/\s+/,$line);
 
	$lc = @spl;
 
	if ($lc gt 1) {	
                $node=$spl[0];
		$val=$spl[1];
 
		print "Temperature_$node:$val ";
	}
$t++;
}

This outputs Temperature_x:n where x is the 1-Wire id and n is the temperature. It could probably do with tidying up and if you wanted to you could pass the 1-Wire id to the script to and only output the temperature for that particular 1-Wire device. A copy of the exported Cacti template can be downloaded, I’m hoping that you just need to import that into your Cacti after placing the script in the scripts directory. Just remember, under linux, if you unplug and replug in your Arduino you may need to set the tty port speed to 9600 (or whatever you put in your Serial.begin(); ). You can use the following command for that;

1
2
3
 
 
stty -F /dev/ttyUSB0 cs8 9600 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts

Make sure that ttyUSB0 is what your Arduino is connected to first. If not you will need to change it in the perl script and on the line above.



[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Tagged with:
February 4, 2009 21:46

Jan

12

WordPress tweetbacking

16 years ago, mid-January | 4 Comments

Tweetbacking, it sounds like some sort of deviant sport or something but isn’t. The idea behind Tweetbacks, similar to pingbacks, is that you can see who is commenting (tweeting) about your blog on Twitter. There are a number of plugins available for use in the end I went for Joost de Valk‘s version. While it worked nicely I decided that I really wanted to approve the tweets before they appeared in my blog so I modified the code a little. The diff is available for download. It allows you to enable and disable the automatic approval of Tweets form the plugin’s admin page. Hopefully Joost will add this option in the official version sometime.

Tweetbacks are saved in the comments table and can be displayed however you like for example, in my comment loop I have:

 
<?php 
if (strtolower($comment->comment_type) == 'tweetback') {
   $avatarurl = str_replace(
       "twitter:",
       "http://s3.amazonaws.com/twitter_production/profile_images/",
       $comment->comment_author_email
       );
       echo "<img align=middle"
              ." src='$avatarurl' border=none"
              ." width=40 height=40 alt=''>";
}
?>

A word or two of caution about the plugin. Personally I’ve removed the bit.ly url shortening link parsing because it just didn’t work properly – I ended up with bogus tweetbacks until I did. The other issue is with the date saving from the tweetback, it doesn’t work properly either giving dates in the 1970’s!!!



[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Tagged with:
January 12, 2009 22:09

Sep

22

Follow Me Wallpaper

17 years ago, at the end of September | 3 Comments

Last night I tried a little experiment on my jailbroken iPhone. Since I’d already started poking about playing with my own Winterboard themes and have been tracking my iPhone’s location for a while now, I thought I’d see what happened if I mixed the two. So, how about wallpaper that followed you? Follow Me It turns out that combining the two is relatively straight forward. Google now provide a mechanism for getting what they call static maps which will allow you to get an image centered on a set of coordinates. You can also add your own markers if you want.

There are some limitations on the number and size of the maps that you can retrieve so I employed a very simple caching mechanism to ensure I didn’t annoy Google. This certainly helps speed things up when testing and also enabled me to identify a small issue I was having.
Read more…



»crosslinked«

Tagged with:
September 22, 2008 11:50

« go backkeep looking »

Current Electricity Use (15min)


iPhone/Webkit RSS Reader

Links


Tags

1-Wire android api Apple arduino currentcost DDAR development DVD FIC freerunner G1 google Google Phone gphone gprs GPS hardware image image builds inspiration iphone jailbreak kiosk linux Mac monitoring Music neo 1973 Nokia openmoko opensource OSX Pachube personal qtopia rhubarb rikki Rio slimp3 slimserver software tracking Trolltech u-boot


Twitpic


Graphy Stuff






Nasty Spam Monkeys