Adding Open Flash Charts to my home monitoring

Share

As mentioned previously, I’ve built a home temperature monitoring system on top of the beer fridge controller that Doug and I built over Christmas. I later added a hygrometer and whole of house power measurement using a Current Cost. There is also a simple server which provides a UI for the system, which previously used PNG graphs generated from Google’s Chart Server API.

I was checking out Jon’s car hacking the other day, and was impressed by his flash graphs. However, as best as I could tell he’s using a commercial flash charting tool, whereas I wanted something open source. I dug around and found Open Flash Charts which was exactly what I wanted.

Why flash charts by the way? I wanted a richer presentation than I could get with PNG, and I am unaware of a way of doing interactive graphs with HTML5 apart from writing massive amounts of javascript. I look forward to someone educating me about an alternative, but until then I will view flash graphing as a punishment for all those overly smug iPad users out there.

So, first off here is an example of a flash chart. This one is power usage at my house from Friday, compared with Wednesday:


Open Flash Charts is actually really simple to use. First off there is some javascript to load the flash component:

    <script type="text/javascript" src="http://www.stillhq.com/local/swfobject.js"></script>
    <script type="text/javascript">>
    swfobject.embedSWF(
      "http://www.stillhq.com/local/open-flash-chart.swf", "2010_04_23_2010_04_21_Watts", "600", "400",
      "9.0.0", "expressInstall.swf",
      {"data-file":"http://www.stillhq.com/json/2010.04.23;2010.04.21/Watts"}
    );
    </script>
    

This javascript relies on a couple of resources being available on your server, which I’ve put into a directory called local. You find these files in the Open Flash Chart .zip file, although you could just snarf them from my server if you want.

Then all you need to do on the HTML side is include a div with the right id where you want the graph to go. For this post, that looks like this:

    <div id="2010_04_23_2010_04_21_Watts"></div>
    

Then you just need to write the JSON which represents the graphs content. That’s well documented on the Open Flash Charts site, but you can find the JSON for my graph at http://www.stillhq.com/json/2010.04.23;2010.04.21/Watts if you want to see it.

Share

Home power measurement

Share

I’ve been spending some quality time with a Current Cost CC128 and my existing home sensor network. So far I’ve discovered that I use quite a bit of power, and that I can remotely monitor how many times a day my wife makes a cup of tea. Some example data:

You can see that it was relatively cool compared with days a few weeks ago today. That’s more obvious in the graph showing the last two weeks though:

However, it was quite humid today:

Which is why we didn’t have the evaporative cooler on, just the fan. That doesn’t seem to really affect our power usage, which really needs more analysis:

The 500 watt minimum power draw makes me unhappy. You can see over a week it never goes away:

Share

The Beer Fridge saga continues

Share

Since the last update on the beer fridge, we’ve had to do some murdering of the original PCB to get it to fit in a case. In addition, we’d failed to take into account the startup power draw for the compressor (5 amps), and had to upgrade the relay we’re using from a 3 amp solid state relay. Luckily Doug had the beefier relay just sitting around in his shed. That means we’ve lost our opto-isolation because the new relay is a simple mechanical one, but we have a relay coil doing the same thing now. A new PCB will make the world a lot neater, which will be nice. Oh, we’ve also been mentioned on Hack a Day, which has generated some interesting comments on their site.

I’ve observed that sometimes the relay doesn’t turn on, even though the arduino thinks it has done so. An example can be seen here:

(Note that graph is a Google chart server image, generated by a simple visualization program I wrote in python. If you’re that way inclined, the visualization software is in my public SVN repository).

At first we thought this was a software problem with comparing ints to floats, but it continued to happen even after we fixed that bug. You can see in the graph above that during testing the compressor was “turned on” (i.e. the arduino thinks it has activated the relay), but the temperature continued to rise. A visual inspection showed that in fact the relay hadn’t turned the compressor on, and power cycling the controller (and therefore the relay) didn’t help. However, the simple expedient of hitting the relay with a screw driver handle fixed the problem, which made me think that Doug had given me a bodgy relay. This got me thinking about if it would be possible to unstick a relay in software though — its pretty easy to detect this case (temperature continues to rise despite the compressor being). I’m thinking that perhaps if you cycled the inputs over and over you might be able to get the relay to unstick.

Detecting this case in software might be generally useful anyway though… Ignoring stuck relays, it would also tell you when someone had left the door open for example. It would be nice to detect this case and then give up on the compressor for a few minutes before trying again. I wrote a quick implementation of this “door open detection” and gave it a try, but I’ve now given up on it as being too fiddly. I found problems such as when the outside world is heating up rapidly (summer mornings for example), the compressor can’t make a big enough difference to the internal temperature of the fridge, but you still really want the compressor on because something is better than nothing.

In the end it turned out the problem with the relay was that the arduino wasn’t providing enough current to pull the relay contact in reliably. This was fixed with the addition of a transistor to the board, which is yet another thing to add to the next PCB.

One of the big advantages of a microprocessor based custom thermostat is that I can implement new features that wouldn’t normally be present in a fridge. Some of these features might turn out to be bad ideas, but I view the beer fridge as a bit of an experimentation platform.

Since my extra Dallas 1820s have arrived from ebay, I also added a sensor inside the freezer. This is interesting because the freezer isn’t getting anywhere near the FDA recommended temperature (-17.7 Celsius). I tweaked the code yet again to turn on the compressor if either compartment is over temperature, but this resulted in the fridge running much too cold — it was close to freezing point in fact. I think this is probably a hardware problem with the freezer, and given its a beer fridge I’ve just written “this is not the freezer you are looking for” on the door and gone back to only monitoring the temperature of the fridge compartment. Here’s a graph of my experiments with the freezer yesterday:

Another recent software tweak is a start up delay for the compressor in order to reduce the risk of back pressure damage to the compressor. Thanks Murray! This comes with yet another LED which indicates that the compressor is currently disabled.

I think this project is nearly ready to start showing people the code. The code is also getting big enough that cutting and pasting it into blog posts is starting to get annoying. So, here are a few links:

  • The code for the arduino. This should be self explanatory, except for saying that we’re using a nuelectronics.com ethernet shield, not the standard one.
  • The visualization software. You want temperature.py to scrape the web server on the arduino and push stuff into a MySQL database, and then server.py is a simple python web server that provides the UI for visualization. The UI isn’t very good at the moment, but I shall improve it soon.
Share

Beer fridge controller 0.3

Share

Last night Doug made up the first cut of the PCB for the beer fridge controller mentioned in previous posts, and we fitted the arduino to it. There wasn’t much in the way of software changes, apart from changing the pin that the compressor runs on.

You can see here that we’ve mounted both the arduino and the Ethernet shield onto the PCB — this is just temporary until we get the PCB right. The black rectangle at the front right is a 240 volt capable relay, and the thing behind it is a 240 volt transformer which is capable of powering all the electronics on the boards. In the final PCB we wont need the arduino at all — just the Ethernet shield and the atmega 328 from the arduino. However, that didn’t work out this time around because of problems getting the Ethernet socket to fit nicely. Its clearer on this picture of the other side of the board:

See how we had to cut a hole in the PCB for the socket? That took out some of the pin holes for the atmega, and a few tracks. Its not a big problem because we’re going to iterate a little on the PCB design (and by “we”, I mean Doug). You can also see the perspex shield, which covers all the 240 volt rails, which is a nice touch. This version of the hardware is now sitting out on top of the beer fridge, and I wrote some simple scraping and visualization software for the temperature values I am seeing from the embedded hardware. You can see here the temperatures out the back of my house for this afternoon:

As I’ve mentioned before, the hardware and software can handle more than one temperature probe, so the ultimate plan is to take the opportunity to place a bunch of these probes around the house and see what interesting data we end up with.

Share

Beer fridge controller 0.2

Share

Further to yesterday’s post about the beer fridge thermostat replacement, I’ve been hacking on ethernet support for the controller. This is handy because I’d like to log the temperature and compressor state over the network, because I’m hoping that can be used to make calculations about the thermal mass of the contents of the fridge, and therefore derive how much beer is actually in the fridge at any given time.

Because the controller also supports more than one temperature probe, I’ll also add more 1-Wire temperature sensors around the house so I can determine important things like if its hot in the outside world.

The code is currently experiencing some bloat in the binary size, mainly because the ethernet library and the sprintf implementation are quite large. I’ll have to think more about that. Here’s the current code:

    #include <enc28j60.h>
    #include <etherShield.h>
    #include <ip_arp_udp_tcp.h>
    #include <ip_config.h>
    #include <net.h>
    #include <websrv_help_functions.h>
    
    #include <OneWire.h>
    #include <DallasTemperature.h>
    
    // Temperature sensor and compressor setup
    #define COMPRESSOR 9
    #define ONEWIRE 3
    
    #define HIGHTEMP 4
    #define LOWTEMP 3.6
    
    // 220L Kelvinator is 85 watts
    #define COMPRESSOR_WATTAGE 85.0
    
    #define SLEEP_SEC 10
    
    OneWire oneWire(ONEWIRE);
    DallasTemperature sensors(&oneWire);
    
    unsigned long runtime = 0, chilltime = 0, last_checked = 0, this_check = 0;
    uint8_t compressor = LOW;
    
    // Web server setup
    #define MYWWWPORT 80
    #define BUFFER_SIZE 550
    #define ERROR_500 "HTTP/1.0 500 Error\r\nContent-Type: text/html\r\n\r\n<h1>500 Error</h1>"
    
    static uint8_t mymac[6] = {0x54, 0x55, 0x58, 0x10, 0x00, 0x24};
    static uint8_t myip[4] = {192, 168, 1, 253};
    static uint8_t buf[BUFFER_SIZE + 1];
    char data[BUFFER_SIZE + 1];
    
    // The ethernet shield
    EtherShield es = EtherShield();
    
    uint16_t http200ok(void)
    {
      return(es.ES_fill_tcp_data_p(buf, 0, PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n"
                                                "Pragma: no-cache\r\n\r\n")));
    }
    
    // prepare the webpage by writing the data to the tcp send buffer
    uint16_t print_webpage(uint8_t *buf)
    {
      uint16_t plen;
      plen = http200ok();
      plen = es.ES_fill_tcp_data_p(buf, plen, PSTR("<html><head><title>Temperature sensor</title>"
                                                   "</head><body><pre>"));
      plen = es.ES_fill_tcp_data(buf, plen, data);
      plen = es.ES_fill_tcp_data_p(buf, plen, PSTR("</pre></body></html>"));
    
      return(plen);
    }
    
    // Float support is hard on arduinos
    // (http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1164927646)
    char *ftoa(char *a, double f, int precision)
    {
      long p[] = {0,10,100,1000,10000,100000,1000000,10000000,100000000};
    
      char *ret = a;
      long heiltal = (long)f;
      itoa(heiltal, a, 10);
      while (*a != '\0') a++;
      *a++ = '.';
      long desimal = abs((long)((f - heiltal) * p[precision]));
      itoa(desimal, a, 10);
      return ret;
    }
    
    void setup()   {
      // initialize the digital pin as an output:
      pinMode(COMPRESSOR, OUTPUT);
      Serial.begin(9600);
      sensors.begin();
    
      es.ES_enc28j60Init(mymac);
      es.ES_init_ip_arp_udp_tcp(mymac, myip, MYWWWPORT);
    }
    
    void loop()
    {
      int i, j, data_inset, delta;
      char float_conv[10];
      float t;
      DeviceAddress addr;
      uint16_t plen, dat_p;
    
      // Read temperatures, we dump the state to a buffer so we can serve it
      this_check = millis();
      if(this_check > last_checked + SLEEP_SEC * 1000)
      {
        delta = int((this_check - last_checked) / 1000);
        runtime += delta;
        if(compressor == HIGH) chilltime += delta;
    
        data_inset = 0;
        sensors.requestTemperatures();
        for(i = 0; i < sensors.getDeviceCount(); i++)
        {
          t = sensors.getTempCByIndex(i);
          sensors.getAddress(addr, i);
    
          for (j = 0; j < 8; j++)
          {
            sprintf(data + data_inset, "%02x", addr[j]);
            data_inset += 2;
          }
          sprintf(data + data_inset, ": %s\n", ftoa(float_conv, t, 2));
          data_inset = strlen(data);
        }
    
        // Control compressor
        if(t > HIGHTEMP) compressor = HIGH;
        else if(t < LOWTEMP) compressor = LOW;
        digitalWrite(COMPRESSOR, compressor);
    
        // Status dump
        sprintf(data + data_inset,
                "Compressor: %s\nRuntime: %lu\nChilltime: %lu\n%% chill: %d\nWatt hours: %d\n",
                compressor == HIGH ? "on" : "off", runtime, chilltime,
    	    int(chilltime * 100.0 / runtime),
                int(chilltime * COMPRESSOR_WATTAGE / 3600));
        Serial.println(data);
        last_checked = this_check;
      }
    
      // Handle network packets
      dat_p = es.ES_packetloop_icmp_tcp(buf, es.ES_enc28j60PacketReceive(BUFFER_SIZE, buf));
      if(dat_p != 0)
      {
        if (strncmp("GET ", (char *)&(buf[dat_p]), 4) != 0){
          // head, post and other methods:
          dat_p = http200ok();
          dat_p = es.ES_fill_tcp_data_p(buf, dat_p, PSTR("<h1>200 OK</h1>"));
        }
    
        // just one web page in the "root directory" of the web server
        else if (strncmp("/ ", (char *)&(buf[dat_p+4]), 2) == 0){
          dat_p = print_webpage(buf);
          Serial.println("Served temperature web page");
        }
    
        else{
          dat_p = es.ES_fill_tcp_data_p(buf, 0, PSTR(ERROR_500));
        }
    
        es.ES_www_server_reply(buf, dat_p);
      }
    }
    
Share

Beer fridge controller 0.1

Share

On the weekend I picked up a 220 liter beer fridge for $20. Its in really good condition (ignoring some minor rust in the freezer section), and the only real problem with it is that the thermostat doesn’t work leaving the compressor on the whole time. Doug suggested that instead of just buying a new thermostat, we should build an arduino fridge controller.

I’m not really a hardware guy, but once Doug had pointed me at the Dallas 1820 1-Wire temperature sensor, and lent me some resistors, it was pretty easy to pull the software side together. Note that this version doesn’t actually do any of the compressor control — it simulates that by turning a LED on. The compressor stuff has been delegated to Doug and will be mentioned later.

You can see that the circuit is in fact really simple. There is a LED to simulate the compressor (with a resistor), and then the 1-Wire temperature sensor (with another resistor). The code is pretty simple too. Here’s my latest fancy version:

    #include <OneWire.h>
    #include <DallasTemperature.h>
    
    #define COMPRESSOR 13
    #define ONEWIRE 2
    
    #define HIGHTEMP 4
    #define LOWTEMP 3
    
    #define SLEEP_SEC 10
    
    // 220L Kelvinator is 85 watts
    #define COMPRESSOR_WATTAGE 85.0
    
    OneWire oneWire(ONEWIRE);
    DallasTemperature sensors(&oneWire);
    
    unsigned long runtime = 0, chilltime = 0;
    boolean compressor = false;
    
    void setup()   {
      // initialize the digital pin as an output:
      pinMode(COMPRESSOR, OUTPUT);
      Serial.begin(9600);
      sensors.begin();
    }
    
    void loop()
    {
      int i;
      float temperature;
      DeviceAddress addr;
    
      sensors.requestTemperatures();
    
      for(i = 0; i < sensors.getDeviceCount(); i++)
      {
        temperature = sensors.getTempCByIndex(1);
        Serial.print("Current temperature at ");
        sensors.getAddress(addr, 1);
        printAddress(addr);
        Serial.print(" is: ");
        Serial.println(temperature);
      }
    
      if(temperature > HIGHTEMP)
      {
        digitalWrite(COMPRESSOR, HIGH);
        if(!compressor)
        {
          Serial.println("Compressor on");
          compressor = true;
        }
      }
      else if(temperature < LOWTEMP)
      {
        digitalWrite(COMPRESSOR, LOW);
        if(compressor)
        {
          Serial.println("Compressor off");
          compressor = false;
        }
      }
    
      delay(SLEEP_SEC * 1000);
      runtime += SLEEP_SEC;
      if(compressor) chilltime += SLEEP_SEC;
    
      Serial.print("Efficiency: Total runtime = ");
      Serial.print(runtime);
      Serial.print(", Chill time = ");
      Serial.print(chilltime);
      Serial.print(" (");
      Serial.print(chilltime * 100 / runtime);
      Serial.print("%, ");
    
      // The compressor wattage is consumption for an hour, so work
      // out how many hours we've been operating for. Then divide by
      // 1000 to get kWh.
      Serial.print(chilltime * COMPRESSOR_WATTAGE / 3600 / 1000);
      Serial.println("kWh)");
    
      Serial.println("");
    }
    
    // Function to print a one wire device address
    void printAddress(DeviceAddress deviceAddress)
    {
      for (uint8_t i = 0; i < 8; i++)
      {
        // zero pad the address if necessary
        if (deviceAddress[i] < 16) Serial.print("0");
        Serial.print(deviceAddress[i], HEX);
      }
    }
    

The code uses the Miles Burton 1-Wire library, which was easy to use once you figure out his example code has an impossible number for the pin. The code outputs information like this over serial:

    Current temperature at 10FA473500000037 is: 21.62
    Efficiency: Total runtime = 1780, Chill time = 1770 (99%, 0.04kWh)
    

That's with it on my desk where the "compressor" is permanently "on". I'll let you know how we go with further versions.

Share

An awesome weekend

Share

I don’t tend to write personal things here all that much any more, but I just wanted to say that I had an awesome weekend. It started off with a mate I haven’t talked to in years inviting me to go checkout the U brew it place in Hume that the Riot ACT tried out… That was great fun, with the mate putting on a great barbecue, and us producing about 200 liters of beer for bottling in a couple of weeks. I don’t think its cheaper than the beers that I would buy from the store, but when you factor in the social event, the barbie, and the fun of doing it yourself it was a great deal. Let me know if you’re interested in coming along next time.

Following on from that Catherine and I went and bought the road bike I have been eyeing off for the last week or so. Its a straight bar road bike, which makes it fast without having that bent over stance that I think looks so silly. Its a very fast bike, and I think my skill level is going to need to increase before I feel fully comfortable on it.

Then today was my birthday. I had a nice breakfast with half my family, checked out the ANU food coop, had a nice lunch at home with the rest of my family, and then got some chores done. I now have a fancy gray water tank in the yard, with an immersible pump. That was especially cool because it got it for a great price. Oh, and I got some really cool presents too, like new tools (I like tools), and three Clarkson books.

Overall, I had fun, got stuff done, and ended up with a great bike. I’m stoked.

Share

Surprise for the day: Australian beer is cheaper in the US than in Australia

Share

Image stolen from Woolworth’s Homeshop

My two favourite Australian beers would have to be Coopers Pale Ale, and the Malt Shovel Brewery Amber Ale. In fact, I am half way through a MSB now, which might explain my terrible spelling in this post. Then again, it might also be that I’ve been working away for 12 hours now. Anyways, back to the story… Since moving to the US I have been subsisting on these terrible American beers that they have around here. Some of the micro-brews are ok-ish, and Sierra Nevada is better than a punch in the face, but they’re really nothing special. For a start, their weak. On the non-micro-brew front, I recommend Coors if you’re in a no-beer emergency and you have to get something mainstream.

The other fall back drinking plan has been to develop a taste for Margaritas, which we have been serving in 500 mil pint glasses for effect.

Anyway, back to the story… So yesterday Catherine and the kids and I ventured out to try to fix this. BevMo was rumoured to have Australian beers, and I thought it might be worth a try. The exciting news is that it turns out that BevMo’s Australian beer options (my two favourites included) are cheaper here than in Australia. Both are available for $6.99 US ($9.42 Australian as I write this), compared with $13.98 in Australia for a six pack in Canberra.

Hurrah for international trade!

Share