First, click here for the experimental setup.
In the photo we have two PING))) Ultrasonic Sensors from Parallax, mounted at right angles to one another. Because I forgot to buy the right kind of connector, I had solder wires to the X axis (the one attached to the wine bottle), which took a lot of concentration. On the other hand, the Y axis is just sticking out of my breadboard, which was convenient. The breadboard itself is held in a vertical position by a heavy pair of pliers oriented just so.
The wiring was pretty simple. These things are hooked up to +5 and GND in the normal way. The SIG pin of the X-axis (wine bottle) detector is wired to digital pin 5, and that of the Y-axis detector is wired to digital pin 6.
Because these detectors have a somewhat narrow angle field that they can see (+/- 20 degrees), the effective 2D field in which I could detect stuff in this setup is kind of small. But anyway, here is the code:
using System; using System.Threading; using Microsoft.SPOT; using Microsoft.SPOT.Hardware; using SecretLabs.NETMF.Hardware; using SecretLabs.NETMF.Hardware.Netduino; using Pin=Microsoft.SPOT.Hardware.Cpu.Pin; namespace hacks.netduino.avatar { public static class Program { public static void Main() { try { Doit(); } catch(Exception e) { Debug.Print("ugh: "+e); } } private static void Doit() { const int numReadings=2000; const Pin xPin=Pins.GPIO_PIN_D5; const Pin yPin=Pins.GPIO_PIN_D6; for(var i=0; i<numReadings; ++i) { var y=Probe(yPin); var x=Probe(xPin); Debug.Print(i+" "+y+" "+x); } } private static long Probe(Pin pin) { //set some things up early long machineEndTime=0; NativeEventHandler handler=(d1, d2, t) => machineEndTime=Utility.GetMachineTime().Ticks; var pwm=new PWM(pin); //note the time, fire what we hope to be a 5 microsecond pulse var machineStartTime=Utility.GetMachineTime().Ticks; pwm.SetPulse(5, 65535); var dummy=new string('*', 3); //burn some time pwm.Dispose(); //wait a long time for the echo using(var iport=new InterruptPort(pin, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeLow)) { iport.OnInterrupt+=handler; Thread.Sleep(150); return machineEndTime-machineStartTime; } } } }
The specifications say that the way to talk to this thing is to send it a 5-microsecond pulse, then wait for a while, then measure the length of the pulse that comes back. My first problem was how to accurately send a 5-microsecond pulse. I suspected that there wasn't really a way, so I decided to (ab)use the PWM feature. My little trick was to set a period of 5 microseconds, a duration of 65535 microseconds, and then to dispose the thing real quick so it would never have a chance to fire a second pulse. You can see this in the code here:
pwm.SetPulse(5, 65535); var dummy=new string('*', 3); //burn some time pwm.Dispose();
Obviously for this to have any chance of working, a lot of things need to be true
- The pin isn't doing anything before the call to pwm.Pulse()
- The pin goes high right after pwm.Pulse(), and stays high for 5 microseconds
- The framework is slow enough that the call to Dispose happens sometime between 5 and 65535 microseconds.
Who knows if any of that is true? Sometimes ignorance is bliss.
Now my next task was to measure the width of the pulse coming back, which is how the device tells me how far away the measured object is. My first plan was that I would configure the pin to give me an interrupt on both the high and the low edges of the pulse coming back. However for some reason I could never get it to work. (In that version of the program, every once in a while I *would* get two edges, but most often I would only get (what I assume to be) the trailing edge). Since I couldn't really get that to work, instead I wrote the program that you see above, where I store the starting "machine time" (not really sure what "machine time" is) at the beginning, and the ending machine time after the interrupt fires.
I ran it, and tried to store a bunch of samples in memory. I quickly ran out of memory unfortunately (How big is the heap available to my program anyway? Is it possible that it's as low as 5-10 kilobytes? That's what I think I was seeing, but I didn't have time to research it). Anyway, I decided to print my output to the debug window instead.
Then it was showtime. I decided to trace a square in the air with my fist. I wanted to see what the program came up with (one should expect some distortion because of the geometry of the situation: when my hand is moving, say, straight up and down, even though its X coordinate isn't changing relative to me, it is changing its angle and therefore its distance with respect to the X detector. It seems we eventually need to do a little coordinate transformation here. But first let's see if it works at all)
Here is a video of me making the square:
http://www.youtube.com/watch?v=bVYQGHbyJSk
Now, in the video I claimed I was actually going to use the collected data in this web post, but it didn't turn out very well. Instead for the purposes of this post I will use data from an earlier run. Here is the data: run2.txt 1.5KB 11 downloads
If we plot the X and Y axes vs time, we get this graph.
There is some intriguing activity at the bottom there. It looks sort of like two sine waves. If we adjust our axes to zoom into that area, we get:
this fabulous graph.
Now for a plot of Y vs X, which should actually represent what we tried to trace out in space. That looks like this.
Hmm, what's that little blob in that corner? Why, it looks (sort of) like our square! Wow!!!!!!
I hope this hacky little project was of interest to people. Thanks for reading my project report!