Tuesday, November 10, 2009

DS1307 Module

I've been doing a few projects lately using the DS1307 IC. If you don't already know, the DS1307 is a real time clock chip (as opposed to an unreal time clock chip, which is often used by musicians and people attending events on Facebook). This means that you just have to attach a quartz watch crystal to it, and it'll keep time for you. RTC chips are divided into two categories; those that keep time by counting seconds (usually as a 32 bit number) since some specific start point, and those that count seconds, minutes, hours, days, months, and years.

The DS1307 is of the latter type. This means that it stores the time as seconds : minutes : hours (12 or 24 hour) : day of the week : day of the month : month : year. It also has some smarts so it knows how many days are in each month, and even knows about leap years out to 2100.

This is great, and super useful, but every time I need to pull the chip out of the breadboard to move it, or even just accidentally remove power, it loses the current date and time. Since most of my projects aren't serious enough to warrant wiring up buttons and coding a way to reset the clock, I have to instead plug my Arduino into my laptop with a USB cable, reflash a DS1307 clock setting program I threw together, send it a 13 digit number over the serial port, reflash the firmware for whatever project I'm currently working on, and move forward. Can you see how this would get just a little annoying after the twentieth time? I also have been using the SRAM on the clock to store data, and losing that isn't catastrophic, but annoying as well.

Lucky me, the DS1307 has a neat little feature, where it has an extra pin that you can connect to a Lithium 3V battery, or tie to ground if you're not using it. Sparkfun has a board that has a surface mount DS1307 on top, and a battery clip underneath, which would be useful, but at $20, is a little rich for an inconvenience. I then saw an idea where you do the same thing, just with the DIP package, and all on one side of a piece of perf board.

Parts list
  • Perf board - 5 x ~17 (I used a leftover piece from Radio Shack, but any would do)
  • 5x right angle male header. (sourced locally at Halted, 14 cents) This is so I can turn the board on end and plug the board into my breadboards when I'm done, which is the only place I plan on using this.
  • 1x 8P3 DIP socket (sourced locally, ~30 cents), because soldering ICs hurt my soul.
  • 1x DS1307 RTC (Digikey, $3.74)
  • 1x 12.5pF 32.768kHz crystal (Digikey, 32 cents)
  • 1x CR2032 battery clip (Digikey, 96 cents)
  • 1x CR2032 cell (Digikey, 40 cents, USPS won't ship | Locally, $4.50, I hate you RiteAid)
  • Some wire, solder, soldering iron, etc.
Wiring it up
The DS1307 is nice enough to have 4 of the 5 IO pins on one side (5 - SDA, 6 - SCL, 7 - SQW, 8 - VCC). The fifth pin is for the ground connection, which you need to jump from the other side of the IC with some wire. The battery clip needs to be connected between VBAT and GND, so that'll take at least one piece of wire from the far end. I soldered the crystal straight to the perf board and DIP socket. You should end up with something that looks like this:

Plug in the DS1307 and a CR2032, and you're ready to set the time once, and use until DST.

Results
It works quite well when it's plugged into a project, and I power down everything else. It's super nice not having to try and keep this powered while I'm mucking around with all the other wiring on a board.

I have had a problem when it's just sitting loose. It seems that when VCC isn't pulled to ground, the chip only keeps time half as fast as reality, which makes it less real time, which personally, I find quite offensive. For the second revision, I would probably add a resistor, or even just a bypass cap (0.1μF) between the VCC and GND lines to improve this. While plugged into a project, the rest of the project keeps the VCC line very much at ground, so it's not a very big issue as far as I see it.

Considering that I managed to put this together for easily half of what the Sparkfun board costs, and that I got to break out the soldering iron, which is always welcome, I'd call this project a success.

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

You all know the scenario: You're walking out to your car, it's late at night, it's dark, and you're alone. You've got your keys held firmly in your hands, just like in all those public service announcements, when some gangsta mo-fo comes out of the shadows, AND STARTS MUGGING YOU! But wait, you have your car remote in hand; you're saved! You press the big red button... Annnd your car alarm goes off.

Now here you are, still getting mugged, but at least now all the neighbors are annoyed with you as well...

I'm kidding, but seriously, those little red buttons can get freakin annoying. A few weeks ago, my sister's cars alarm starting going off every time she pulled her keys out of her pocket. It got to the point where she couldn't carry her remote with her any more, so I decided I should try and "fix" it.

And by "fix," I mean disable the crap out of that freakin panic button, in case we're at all unclear what I meant by fix.

This 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 exceedingly difficult to press, but once I opened this remote up, that didn't look to work out well.



The buttons 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.