Sunday, October 25, 2009

Temperature Logger in Refrigerator

The fun continues with my new temperature logger. After leaving it running for a week in my room, I decided to give it a shot at my refrigerator.
Click to enlarge.
To say the least, I was awfully surprised by the data once I graphed it. First of all, I expected it to be much more temperature stable than it really is. With a slope that high, it's clear that you really don't want to open the door when the power is out, because it is already rapidly approaching room temperature.
  • The initial drop is just because I started the logger while it was still in my room, since it had to be plugged into my computer to be reset, so it took about an hour for the breadboard to cool off.
  • Around minute 850, we got home from grocery shopping, so before then, our fridge was rather barren, and the spike was because we had the door open, and were loading warm food into it.
  • It surprised me how the temperature started swinging more after it was fully loaded, instead of less. This might be because all of the added food blocking air flow means there is a greater differential between the cooling element and the sensor. Any other theories?
  • The bizarre temperature spike for the last hour didn't actually happen. With a diode and a voltage regulator between my 8V battery pack and the 5V ICs, that left a very small (<0.5v) margin before the regulator started sagging.
I'm really quite interested in seeing what the difference is between the bottom and top of the fridge, and getting more than a day and a half of data. Luckily, I just happened to have ordered TWO temperature sensors, so the only things preventing me from logging two simultaneous temperatures is the lack of spare hookup wire (left my big spool in Sunnyvale -_-), a dozen lines of C, and a fierce competition between roommates about whether we should be keeping temperature sensors or food in the refrigerator.

Saturday, October 24, 2009

Protip: Masking Tape for Buying Electronics

This was a new idea I got yesterday when I was shopping at Halted. Since the Sunnyvale Halted is kinda far from my house, and the Sacramento one is hellza far from my Davis apartment, I usually tend to go with parts lists for several projects I have queued up at once. Unfortunately, this means the first thing I get to do when I get home is sort out three projects worth of parts from one bag, based on the what seems to be sometimes optional markings on parts.

New trick: Bring a roll of masking tape, and then lay each project down on one strip of tape, then once they're all collected, put a second piece down on top of it. Once you get home, it's just a matter of peeling the tape apart when you have time to work on that specific project.

Sunday, October 18, 2009

Arduino Temperature Logger

Just like every other good engineer in life, I thrive on data. It doesn't even need to be solving a problem. Simply having the ability to say, "I know this," is awesome. Having some free time last month, I started playing with my Arduino again, and finally got around to ordering all the parts I needed for a project I've been thinking about for a long time.

This is my Arduino-based temperature logger. On the breadboard, from left to right, is a serial real time clock, a 1Mb EEPROM, and lastly the serial temperature sensor. Parts list:
Instead of all of the parts for the clock, you can also consider SparkFun's DS1307 module. You pay for it, but you get what looks like a very nice clock/crystal/battery package that you can just drop into your project. Disclaimer: I've never played with it personally. Only had it recommended to me. No way am I paying $20 for $3 in parts on a board.
Forget that. I was able to put together the functional equivalent with less than $10 worth of parts on a scrap piece of perf board.

Since pretty much everything uses I2C, I'll leave wiring it all up as an exercise for the student (Hint: connect everything to analog 4 & 5).

Source code.

Note that some of the features of the dumper (setting the clock, reading the clock), don't work. The main goal was to just get the D(ump) and Z(ero) commands working. I'll get the other commands working at some point if I mess up and the DS1307 loses power at some point (I originally set it using some sample code I found online).

I measured the current draw as 42.5mA, so ignoring the loss in the power supply, that's only half a watt. For this much data, I can pay for half a watt!

How It Works

On power-up, the Arduino checks the CH (Clock Halt) flag on the DS1307 clock chip. If this flag is set, it indicates that the clock has lost power, and that the date and data stored on it isn't valid. Without valid data, the Arduino displays an error message on the LCD and enters an infinite loop to do nothing.

If the clock is running, the controller then loads the first two bytes of general purpose RAM off of the clock. The DS1307 provides 56 bytes of RAM which can be backed by its battery in the case of power failure. I use the first two bytes of it to store the current index into the EEPROM, so that if the controller gets reset, as long as the clock has power, I'll never start writing over collected data by starting from the beginning again. It then prints the read count on the LCD for a second before changing to the current time and date. I found this useful for debugging, since I then only needed to hit the reset button to figure out where it was writing to on the EEPROM. It also sends the temperature sensor the signal to start taking temperature readings. The DS1631 is factory set to continuously take readings, as opposed to the other option where it only takes one and goes back into standby.

Once all of the initialization is finished, the Arduino enters a loop where every 100ms it reads the time off the DS1307 and the temperature off the DS1631 to display on the LCD. It then compares the minute value to the previous one, and if they're different, that means it's been a minute since the last temperature recorded in the EEPROM.

To record the temperature to the EEPROM, the current temperature reading is written to the EEPROM, and the address stored in the clock's RAM is incremented, so if the power goes out in the next minute, no data is lost or overwritten.

Conveniently, all three of the ICs use the I2C serial interface (by accident? Who knows...), so that leaves a ton of extra pins if I manage to think up anything else to add on.

Possible Extensions

Each temperature reading is stored as 2 bytes, one for the whole degrees, and the other to store 4 bits of fractional degrees. It wouldn't be unreasonable to assume my room is never going to swing outside of something like 20-30 degrees Celsius, and try and pack the reading into one byte to save room. I'm using a 1Mb EEPROM, which gives me enough room to take two byte readings every minute for 45 days. This is an incredibly high time resolution, and backing it off to something more reasonable, like once every 15 minutes, stretches this time out to 2 years. Changing the source is trivial; just add minute%15==0 to the if statement in loop().

Since the EEPROM is I2C, having the maximum 4 devices per bus is simply a question of paying the $4 per device. You would have to change how the tempreadcount variable is handled everywhere (store it in three bytes on the DS1307 instead of two, etc) so it doesn't roll over after the first chip is filled, but that would mean being able to take a temperature reading every minute for half a year, and that's just freakin awesome.

Even without the AT24C EEPROM, the Arduino comes with 512 or 1024 bytes of EEPROM on-board. This can get you a couple days of 15 minute readings, which is at least enough to play with, if you just wanted to buy the DS1631 chip.

Problems
  • For some reason, I could not get the LiquidCrystal library and Serial to both work at the same time. This means that to dump the EEPROM or reset the clock, I have to flash in a different program, then reflash the logging program.
  • I was originally using a DS1306 clock chip, but it drifted badly (a few minutes a day). Once I switched to the DS1307, which calls for the 12pF crystal I had, instead of a 6pF crystal, it hasn't noticeably drifted in a few days. Lesson learned: make sure you have the right crystal for your clock.
  • Don't copy paste big chunks of code that almost do the same thing without stopping and really thinking about it. I spent a long time trying to figure out why the data dump just read the last measurement for all of them. Was using the read number count instead of the loop variable... I was afraid I had somehow smoked my $4 EEPROM.
  • I think finding I2C addresses in data sheets is some kind of right of passage. You'd think that they would be something they're put rather prominently on the first page, or refer to it several times, but instead, every time they do refer to it, they refer to it as, "that address as noted previously," etc. Freakin pain in the ass, digging through not only the text, but all the I2C spec diagrams to find where they happened to show the waveform for the device's address, which you then get to decode into the 7 bit number you needed in the first place. I finally wrote up one page of notes with all the I2C addresses and register numbers so I could actually write some code instead of spend two hours digging through the data sheets every time I try to implement something.

Results

After running for one day, it came time to dump the data into Excel. I did this by writing the second program, which just prints each reading number, and the two bytes of the reading, all tab seperated so they will go into seperate cells nicely. To calculate the temperature was simply a matter of =(first cell)+(second cell/256). A little bit of good old graphing magic later, I got this:
Click to enlarge!
It is pretty clear that a one minute time resolution on a 12 bit temperature measurement is a little overkill. Most values are repeated for 10 minutes before drifting down another 1/16th of a degree to the next possible value. I've been playing with massaging the data after the fact to get a smoother line, but the logical next step is to just record the average reading for 5-15 minute periods to ink out two more bits, while saving a tremendous amount of EEPROM (45 days per chip to 1.8 years for 15 minute intervals). Note that the spikes in the afternoon were me playing with it by putting my finger on it and watching the temperature go up and fall back down to room temp.


Update 10/24/09: So after running the logger for a whole week, I've dumped the 10,000 some odd data points, and graphed them in Excel, just for your enjoyment (I'm lying, mine too).
Click to enlarge.
The ticks on the x axis are about where I believe midnight to be (I'm planning on patching the firmware to log a 0xFFFF temperature at midnight, since it knows what time it is). It's pretty clear that I left my window open over night two of the seven nights (Monday morning, Friday morning). What I did find surprising was how late it was before it started getting cooler. Most nights you can see it at least holding steady until I finally went to bed around 12:30am (Sunday night being the exception, due to the open window). On Thursday (second to last day) it's most dramatic, but when I leave for class, etc, the temperature markedly stops rising. I expected being home, awake, and active had an effect on my room's temperature, but certainly not to the degree shown on these graphs.


I also implemented a histogram feature on the last of the unused LCD space using the eight custom glyphs feature on the HD44780 controller. Essentially what I did was every hour (every half hour in the picture), stored the current temperature in an array of the last 40 (5 columns * 8 glyphs) such readings. I then calculated the maximum and minimum values for this set, and used that to normalize the values so they ranged between zero and eight, to calculate how many pixels high that column should be. The code ended up being pretty ugly, so I'll leave the specifics as a challenge for the student (don't you hate it when they say that?). The general idea is:
Bar height = (temperature - minimum temperature) * 8 / (maximum - minimum)
Storing the hourly temp readings in an array was just me being lazy. The I2C bus is pretty freakin fast, so the better solution would be to just read the values back off the EEPROM, since this only needs to be done once an hour, and that has the added advantage of not having an empty graph on reset.


Update 10/25/09: Data collected in my refrigerator.

Sunday, October 11, 2009

Disabling a Car's Panic Button

In some situations, having a car alarm with a remote panic button can make a lot of sense.  The traditional somebody-is-mugging-you one comes to mind, but even for much more mundane uses such as trying to find your grey colored Toyota Camry in pretty much any parking lot.

Unfortunately, this is a less than ideal world, so many times you will accidentally set off your car alarm at a time when you don't want to.  In an even less ideal world, your remote actually fails, putting it in a state where it will randomly set off your car alarm ALL THE TIME.

Fortunately there is a relatively easy fix, that I've actually done a few times on various remotes already. Usually what I do is just cut the actual button rubber down so it becomes more difficult to press, and less likely to be pressed accidentally in your pocket, but sometimes the actual button in the remote will fail, which means either buying an entirely new remote, or just disabling the remote panic feature and to live without it.



The button covers were all a single sheet of multicolored rubber, molded so it would look like four buttons. Thank you wonderful modern technology: you make our lives so much easier, when you work. Cutting that off just didn't seem like a good idea, breaking the seal on the electronics, etc.

That worked out just as well, anyways, since a little poking around with a volt-ohm meter made it pretty clear that this wasn't a case of fat fingers. The panic button (top right in the picture) was really defective, naturally shorted out, just waiting for some good reason to cry wolf. This meant I needed to actually disconnect the button entirely.

I first tried using my soldering iron to heat and lift off the entire button, but I've never really had much success with surface mount components like these, so I gave up on that fairly quickly. I instead opted to simply cut the trace. A little scratchy scratchy with an exacto knife, and the button is officially out of service.

You can see the scratch between the button and the 4th pin on the IC on the vertical trace. Tested it with my ohm meter to make sure it was really open, put everything back together, and we have a much calmer remote (get it? no panic - calmer. Man, I kill). Another day saved by not being afraid to open something and figure out what's up. Carpe diem, people.

Thursday, October 1, 2009

The Square Root of 2 is Irrational

You remember that fun piece of trivia from some math class in high school? Some teacher, in their infinite wisdom, laid down the law: The square root of 2 (√2) is an irrational number, which means that it cannot be represented by a repeating sequence of digits. The first few are 1.4142135623730950488016887242097...

But I'm in college now. I'm supposed to be challenging preconceptions about religion, culture, and basic properties of numbers, right? Of course, but fortunately, religion and culture aren't nearly as interesting, so we're going to be limiting ourselves to the third subject.

So if √2 really is an irrational number, I should be able to prove it. Before I prove it, we're going to need something to prove it with:
  • A rational number is a number that can be represented by the division of two integers. This means that I can represent 4 as 4/1, 2.5 as 5/2, and 3.333... as 10/3. An irrational number is a number which cannot be represented by these two integers. Some familiar ones are π and e. φ has also been coming up in discussion a lot this week.
  • The Well Ordering Property of Integers: Given any positive set of integers, I claim that of them, there is one that is less than all the others. That's right, when given the numbers {4, 9, 123, 19244000}, you can always look at them and say, "Ah ha! That four is less than all the other numbers!" This is important, I promise.
So now that we have something to work with, let us start making the magic. A good way to prove something in mathematics is to instead prove that what it isn't, it is not. Show that it can't possibly be anything else, so the only option left must be the answer.

Lucky for us, there is only two options here: √2 is either going to be rational, or irrational. We're claiming it's irrational, so let's show that it's not rational:

ASSUME √2 IS RATIONAL. This is mathematics, so we can play pretend. This means that we can express it as the division of two integers: √2 = a/b. Now if we multiply both sides by b, we get the following:
b√2 = a.
We know that a is an integer, since we called it an integer to begin with, so that means that b√2 must also be an integer. Now since a and b exist, it's not hard to imagine there is a positive set of integers that would satisfy the requirement √2 = c/d (2a and 2b come to mind).

Let's call the set of all the possible positive integers b√2 the set S. This means S is a really big group of positive integers, all of which are products of another integer and √2. This set must have a smallest member, as per the well ordering property. Let's call that smallest member s = t√2.

Since s is the smallest member of this set S, that means that if we can show that there is yet a SMALLER member of S than s, we've got a problem, since smaller than the smallest doesn't make much sense.

s = t√2
s√2 - s = s√2 - t√2
s√2 - s = (s -t) √2

Now we know s and t are integers, by definition. If we multiply them by another √2, we have s√2 = 2t, which means that s√2 is also an integer. Since s and s√2 are both integers, that means the left side of the equation is an integer, which means both sides are integers (we can make this argument from either side).

We also know that both sides are positive. The left side can be factored out to s (√2 - 1), and √2 > 1, since 12 is 1, and 22 is 4.

Now we've got a problem. We've got this integer (s - t) which is positive, and which is an integer when multipled by √2. This is bad, because (s - t) is clearly less than s, and we picked s as the smallest member of S. This means S can't exist, and finally that √2 is an irrational number. Q.E.D.

Well that was fun, wasn't it?