Matplotlib and Live Data: A Tale of Two Technologies
Being unemployed over the summer is never usually a good thing for me. I get bored very easily if I don't have something to occupy myself with. This last bout of boredom led me to unpack some of my electronics. Dusted off my multimeter, Arduino and a digital thermometer I bought a little while ago. Figured I could use these to solve one of my current problems.
Living in Laramie usually subjects people to harsh winters which leaves most housing developments without central air conditioning installed since, well it's never really needed except maybe one or two days over the summer where it gets above 85 oF. This summer has apparently been hotter than previous summers and It's left my condo in an "uncomfortable state". Mind you I'm used to living in hot weather so this isn't such a terrible thing to me, I'm used to it.
What I'm not used to is not having AC and it cooling off enough at night that it's worthwhile to open a few windows and stick a fan in one of them. Which leaves me with this problem: When is the optimal time to open the windows and turn on the fan to get my condo cooled off earliest//fastest?
In comes my Arduino + digital thermometer[1]. Once I rigged up the proper power//data connections on a breadboard for my Arduino I set out to find code for the thermometer. I've setup the thermometer with a sketch on my Arduino before I just didn't feel like wasting a few hours trying to do it from scratch again. Soon enough I found some code[2] that worked perfectly. So I trimmed out some code I didn't need for the project and set it up to just write the temperature as fast as possible[3] to the serial port it's connected to.
After that I wrote a logging program on my desktop in Python to record temperatures sent via serial to my desktop. The program is incredibly simple and uses the pySerial library[4] to read temperatures from the serial port of my desktop and append them to a temperature log. I used a simple windows command to do this since it wouldn't lock the file so I could read data from it simultaneously. There are still occasionally collisions with the processing program locking the file and the logger not being able to write the data to the file but these are rare enough that it's negligible in my situation.
1 2 3 4 5 | import serial, os ser = serial.Serial(2) while True: os.system("echo %s>>out.txt" % (ser.readline().strip())) |
The next step in this project was visualizing the data. I've used matplotlib[5] before and I was thinking this time I would like to see if I could write the program to update data live as it recieves it. My first foray into this goal was a miserable disaster. Most of the solutions I could find involved just setting up an infinite loop with a short time delay in it. Which works great except that it sleeps the thread running the plot which makes it impossible to resize the plot or do anything at all with the GUI for that matter. So obviosly this wouldn't work at all.
After poking around for different solutions to this and crashing my computer once from spawning an infinite number of instances of the plot I gave up for a bit, only to discover that there was an example in the documentation which wasn't obviously named. I quickly discovered the best way to do this. I even added some pretty annotations and such.
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 | import gobject import matplotlib matplotlib.use('GTKAgg') import matplotlib.pyplot as plt current_pos = 0 temps = [] pad = 5.0 f = plt.figure() def update(vars): # Unpack variables that need to be persistent between # executions of this method. temps = vars[0] current_pos = vars[1] pad = vars[2] # Open the data file and get any new data points since # the last time we read from this file data = open("out.txt", "r") data.seek(current_pos) new_temps = map(lambda x: float(x) * (1 + 4.0/5.0) + 32.0, data.read().split("\n")[:-1]) current_pos = data.tell() data.close() # If we got new data then append it to the list of # temperatures and trim to 750 points if len(new_temps) > 0: temps.extend(new_temps) temps = temps[-750:] f.clear() f.suptitle("Live Temperature") a = f.add_subplot(111) a.grid(True) l, = a.plot(temps) plt.xlabel("Time (Seconds)") plt.ylabel(r'Temperature $^{\circ}$F') # Get the minimum and maximum temperatures these are # used for annotations and scaling the plot of data min_t = min(temps) max_t = max(temps) # Add annotations for minimum and maximum temperatures a.annotate(r'Min: %0.2f$^{\circ}$F' % (min_t), xy=(temps.index(min_t), min_t), xycoords='data', xytext=(20, -20), textcoords='offset points', bbox=dict(boxstyle="round", fc="0.8"), arrowprops=dict(arrowstyle="->", shrinkA=0, shrinkB=1, connectionstyle="angle,angleA=0,angleB=90,rad=10")) a.annotate(r'Max: %0.2f$^{\circ}$F' % (max_t), xy=(temps.index(max_t), max_t), xycoords='data', xytext=(20, 20), textcoords='offset points', bbox=dict(boxstyle="round", fc="0.8"), arrowprops=dict(arrowstyle="->", shrinkA=0, shrinkB=1, connectionstyle="angle,angleA=0,angleB=90,rad=10")) # Set the axis limits to make the data more readable a.axis([0,len(temps), min_t - pad,max_t + pad]) f.canvas.draw_idle() # Repack variables that need to be persistent between # executions of this method vars = {0: temps, 1: current_pos, 2: pad} return True vars = {0: temps, 1: current_pos, 2: pad} # Execute update method every 500ms gobject.timeout_add(500, update, vars) # Display the plot plt.show() |
This code generates a plot which updates every 500ms. This is based on an example in the matplotlib examples[6]. An example of the program's output can be seen below.

I imagine that I could have made this simpler by not using the GTK libraries which are a pain to install since there are 3 or 4 modules you have to install in order to make all this work including the GTK+ runtime. I may come back later and post a version written using TK since it can be used without installing extra modules and stuff.
- DS18S20 Digital Thermometer Datasheet [↩]
- Temperature Measurement using the Dallas DS18B20 by Peter H. Anderson [↩]
- Somewhere in the range of 750ms between readings since it is in parasite mode, may change this later to run in non-parasite mode. [↩]
- pySerial Python Library [↩]
- matplotlib Python Library [↩]
- Animation example code: simple_anim_gtk.py [↩]
LCD Hello World!
I finally got impatient with my progress on the LCD Shield I'm designing and decided to solder the headers onto the LCD and give it a try on the breadboard, which has turned out to be a good idea. The original pin-mapping I setup in the design wouldn't have worked and I would have been very frustrated at my wasting ~$20 on getting a PCB printed. I got a lot of great help from http://www.alfonsomartone.itb.it/kwztcq.html which almost problem-for-problem outlined the same order of issues I had excluding problems #2, #4 and #8.
1 2 3 4 5 6 7 8 9 10 11 12 13 | #define RS 11 #define RW 2 #define E 3 #define D0 4 #define D1 5 #define D2 6 #define D3 7 #define D4 14 #define D5 15 #define D6 16 #define D7 17 LiquidCrystal lcd(RS,RW,E,D0,D1,D2,D3,D4,D5,D6,D7); |
Above is what the pin-mapping ended up being. I also discovered I had no pots to use for the contrast pin on the LCD at all so that's going to have to be fixed in the future. So for the time being the contrast is a little out of whack. But I was able to get it to print the traditional "Hello World!" string as you can see in the photo. Also you'll note that I used pins 14 through 17 and you're probably scratching your head as to which pins those are. They're actually the row of pins marked as analog in, analog in 0 is 14 and can actually be used as a digital IO pin as well.
Something else that I've also noticed that I'll need to fix is that the rows seem to be written to out of order. Writing order is as follows: Row 1, Row 3, Row 2, Row 4. Which I'm sure I can fix somehow but I don't know exactly why it's doing that just yet.
I should have looked at the dimensions…
Over the weekend I ordered a 20x4 character LCD for my arduino... I should have looked a little more closely in the datasheet at the section about it's dimensions. Needless to say it's a bit larger than I had originally pictured. So the LCD Shield I've been designing has been thrown off a little bit by the size of this beast.
Looking at the picture the LCD is on the left and my arduino + battery backpack is on the right. I wasn't really planning on it being that huge, but anyway it looks like I'm going to have to heavily modify the PCB layout I've already done for it.
High(ish)-speed Photography (followup)
So this Tuesday I got my Arduino in the mail. I really mean just in the mail... it was in an anti-static bag in a bubble-wrap envelope! Anyway I managed to waste about 6 hours screwing around with it the first night I got it. Since then after a lot of researching, googling, swearing and scratching my head I've successfully setup the first major part for the camera trigger project.
I discovered that the delay functions built into the Arduino C library are mostly useless. The microsecond delay function is most useless of all because it will only produce accurate delays between 4 microseconds and 16383 microseconds. So I gave up on that and just wrote a custom delay function. But before I wrote the custom delay function I looked at creating my own timer using Timer2 and giving up digital pins 11 and 3 for accurate times. Eventually I ditched this method because I never got any of the sample code to work at all.
Eventually I arrived at this at least reliable if not accurate delay function:
1 2 3 4 | void customDelay(unsigned long time) { unsigned long end_time = micros() + time; while(micros() < end_time); } |
The advantage to this over the delayMicroseconds() function is that this will at least pay attention to how long it's actually been since it started where the delayMicroseconds() function just blindly sleeps for an arbitrary amount of time and wakes up when it sees fit. The function for oscillating the IR led at the frequency I needed for triggering the camera is of the same form just with a bit of code for switching the LED on and off twice every clock cycle in the while loop. It also turns out that changing the state of a digital pin isn't near instantaneous either, which is to be expected. I've compensated for this using a constant defined for the number of microseconds it takes for it to turn a digital pin on or off.
Once I solved the timing issues it was essentially about cleaning up the code from that point on. I ended up storing the sequence of oscillation and pausing that Nikon SLR's use in two arrays, one for oscillation and one for the matching pause between each oscillation period. Which makes for a clean section for firing the led and pausing all inside a for loop that iterates over each item in the two arrays.
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 | /* Author: BeMasher Description: Code sample in C for firing the IR sequence that mimics the ML-L1 or ML-L3 IR remote control for most Nikon SLR's. Based off of: http://make.refractal.org/?p=3 http://www.cibomahto.com/2008/10/october-thing-a-day-day-7-nikon-camera-intervalometer-part-1/ http://ilpleut.be/doku.php/code:nikonremote:start http://www.bigmike.it/ircontrol/ Notes: This differs slightly from the other 3 versions I found in that this doesn't use the built in delay functions that the Arduino comes with. I discovered that they weren't accurate enough for the values I was trying to give them. The delayMicrosecond() function is only accurate between about 4uS and 16383uS which isn't a very workable range for the values we need to delay in for this project. The ASM code that Matt wrote works well but is limited to only pin 12 and I haven't got a good enough grasp of the architecture to modify the code to work on any pin. So this is what I've come up with to produce the same result. */ #define IR_LED 13 //Pin the IR LED is on #define DELAY 13 //Half of the clock cycle of a 38.4Khz signal #define DELAY_OFFSET 4 //The amount of time the micros() function takes to return a value #define SEQ_LEN 4 //The number of long's in the sequence unsigned long seq_on[] = {2000, 390, 410, 400}; //Period in uS the LED should oscillate unsigned long seq_off[] = {27830, 1580, 3580, 0}; //Period in uS that should be delayed between pulses void setup() { Serial.begin(19200); //Initialize Serial at 19200 baud pinMode(IR_LED, OUTPUT); //Set the IR_LED pin to output } void customDelay(unsigned long time) { unsigned long end_time = micros() + time; //Calculate when the function should return to it's caller while(micros() < end_time); //Do nothing 'till we get to the end time } void oscillationWrite(int pin, int time) { unsigned long end_time = micros() + time; //Calculate when function should return to it's caller while(micros() < end_time) { //Until we get to the end time oscillate the LED at 38.4Khz digitalWrite(pin, HIGH); customDelay(DELAY); digitalWrite(pin, LOW); customDelay(DELAY - DELAY_OFFSET); //Assume micros() takes about 4uS to return a value } } void triggerCamera() { for(int i = 0; i < SEQ_LEN; i++) { //For each long in the sequence oscillationWrite(IR_LED, seq_on[i]); //Oscillate for the current long's value in uS customDelay(seq_off[i]); //Delay for the current long's value in uS } customDelay(63200); //Wait about 63mS before repeating the sequence for(int i = 0; i < SEQ_LEN; i++) { oscillationWrite(IR_LED, seq_on[i]); customDelay(seq_off[i]); } } void loop() { if(Serial.available()) { //Wait 'till something is connected if(Serial.read() != 0) { //If anything but 0 is sent take a photo triggerCamera(); //Take a photo } delay(100); //Delay an arbitrary amount of time, serial isn't instantaneous } } |
High(ish)-speed Photography
This semester I've started a class with the electrical and computer engineering college called Microprocessor Organization. The purpose of this class is to familiarize the students with you guessed it microprocessors. So far it's basically a glorified lab they wanted to turn into a lecture. The lab portion of the course is heavily oriented around assembling your own prototype board and programming said board. In the class we're using a PIC18F4550 microcontroller, which is fascinating and a bit more complex than I wanted to play with at home.
I started looking around for a microcontroller that was simpler and easier to play around with. Eventually I found the arduino which fit my needs perfectly, it's powered through USB, is cheap, has a built in programmer and enough processing power for most of the simple projects I've been thinking about doing at home for fun. As soon as I picked a decent online store I bought an Arduino Duemillanove and a lithium ion battery pack (rechargable through mini-usb).
I should have both in the next few days and the very first thing I decided on trying was a high-speed camera trigger. I've already done some high-speed flash photography and it was amusing but it was too much of a hassle. Focusing in the dark isn't the easiest thing to do and setting up analog circuits to do this sort of thing aren't the simplest in the world and without custom PCB (breadboards instead) they fall apart as you go. I immediately got started coding a sound-sensitive trigger for firing the IR sequence for shutter release on my Nikon D50. Turns out I'm in luck since I was able to find the sequence and it works for almost all Nikon DSLR's of which my friends have a few. My friend pete wrote about this briefly on his blog and a reader offered to bring his camera out for the expirement to the shooting range (which I haven't mentioned yet... patience).
Now that I've gotten the code written out and a small microphone for sensing sound, an IR LED and at least 3 Nikon DSLR's, all that's left is going out to the shooting range. My main goal for this is to get photographs (in sequence or parallel for stereoscopic photos) of different guns ejecting shells and cycling bolts. Since we're not using the same camera's for each shot they've all got different shutter lag times. My D50 is about 130-140ms when pre-focused. The D40 is 95-105ms when pre-focused. Last but not least the D80 is about 75-85ms. This will turn out well because they're all close enough in time that I won't even need to trigger each camera individually for quick sequences of photos. However this makes doing stereoscopic photos a bit more difficult since I'll have to synchronise two of the camera's to the slower shutter lag of the two. Doing that requires that I have at least two IR LED's, and in the case that I want the cameras to fire in much faster sequence I'll need at least 3 IR LED's (and hope that I can trigger them as quick as I need with the Arduino).
The actual photographic part of this experiment is relatively simple, all I'll need is a similar focal length on each of the cameras which isn't difficult since they all use DX format sensors and it's safe to assume that we all have a lense which can match the same focal length on each camera. And suppose we're shooting at the equivelant sensitivity of iso 400 on each camera (since I doubt we all have prime 35mm lenses that are as fast as the one I've been using) with the same aperture, shutter speed (~1/500-640th) and white-balance we can achieve near identical exposures between the different cameras.



