03 July 2010

Partial Success?

Well, the balloon definitely went up and we got some good GPS locations from it. The text messages received from the balloon fairly closely followed the University of Wyoming's balloon prediction tool. The problem is that we stopped receiving texts, the balloon was definitely still on its way up, and it's now past the 4 hour battery life of the payload. Tomorrow we're going to make a futile effort to find where it landed based on the prediction model, but it's pretty well a lost cause now. The worst part is that I completely forgot to write my phone number on the payload, so there really isn't any hope of getting it back. I'm going to send a picture to all the sheriff's departments in the area just in case.
I hope anyone reading this blog has enjoyed the challenges and successes. If I happen to find the payload or if it comes back to me somehow I'll be certain to post it here. The last known location was  46.082375°, -107.913120°

UPDATE...not that good of one....
After searching for the balloon based on the UWYO flight prediction and some extrapolation we didn't find anything. I have sent a text message to the cellular number just in case someone finds it and is clever enough to try the SIM in a phone. I'll leave the SIM active for a month and monitor activity on the line. The flight path and the data received from the flight computer are shown below. Since the altitude reporting from the balloon wasn't working I can't say exactly how high it was when I received the last text, but if the model is any indication it was above 60,000 feet. There was a cell tower almost directly beneath the balloon at that time and the antenna on the balloon was oriented so the major lobe of radiation was directed downwards. I've removed some outlier points from the flight computer reports, but you can see that it followed the UWYO model very closely until the last two reported points. Even at the last two you can see that it was turning the corner like the model had shown. My best guess is that it burst and landed somewhere that there was no cell coverage. I am really kicking myself for not writing my phone number on the payload...lesson learned for next time.
With the balloon searching out of the way I have enjoyed the last three days in Yellowstone National Park with my dad and Arhan. This was the real reason for my trip to Montana, the balloon was just a fun experiment and challenge. I'm considering a second balloon since I have most of the hardware, but next time I'll add in a zigbee radio and a much larger battery.

Purple is the model and the yellow pins are the received data from the flight computer

Yellowstone traffic jam...damn buffalo


Arhan and I at the start of the Blacktail Deer Creek trail


Dad and I at the top of the Beartooth Pass about 10,500 feet elevation

F@#* YOU T-MOBILE!!!!!

We got to the launch site and found that my software wasn't quite working, so we postponed the launch and while working on the software I discovered that I couldn't make CSD calls again... I called up T-Mobile technical support and their response was that I didn't have a T-Mobile phone so they wouldn't support me. The only thing I could think is that no cellular carrier makes their own phone, the product they sell is the network access and T-Mobile was blatantly refusing to support their product. I will never use or recommend T-Mobile ever again. I hope you join me in this effort.
As a backup I am having the balloon send me text messages with the GPS information. It's not perfect, but it should work. Hopefully there will be pretty pictures of space posted here tonight, otherwise I'm going to purchase as many explosive fireworks as I can just to destroy all evidence of my failure hahaha!


UPDATE!!!
The balloon is well on its way. Arhan, my dad, and I are waiting in our hotel in Billings for some final position to be reached. At last record the balloon was at 46.082375,-107.913120. The altitude from the GPS doesn't seem to be working as it is constantly reporting 4092.34 feet. If it would have been at that altitude during its flight there were several 6000 feet tall mountains that it would have crashed into. Some launch pictures and video are below. I'll post up the pictures from the recovered payload when it happens. Wish us luck! I'm still angry at T-Mobile...

A little preflight action with Luke and Arhan

Launch time, Farmer Jim is standing in the back. He just showed up and was very curious and friendly!




02 July 2010

How sweet it is

Not only is it Friday, it's the Friday after payday, the weather is perfect, I got off work early, and I'm heading to beautiful Montana in a few hours. Looking at the forecast for the Billings,MT area I see that there are predictions of thunderstorms for the next two days... I'm still hopeful that it will be clear skies tomorrow at 2pm for the launch, but it may be delayed until Sunday or possibly Wednesday if things look cloudy. I'm moving the launch site to the side of Hwy 312 somewhere just north of Fromberg, MT. I'm concerned that the original site is a little too close to the airport and the winds look strong so I'll need to adjust to keep the landing in a cellphone friendly area. I'll post another update tonight when I can feel for myself what the weather is like in Billings.

To all thieves planning on ransacking my apartment:
Do you really want to try stealing things from a guy that designs crazy electronic stuff? Who knows what might be waiting for you inside...

UPDATE!!!
The weather looks a bit cloudy, but the launch will go ahead as planned from Fromberg, MT. The lat/long is 45.413430, -108.889972 which is just a bit south of Montaqua Rd. There is a pulloff on the west side of the road where we'll be in a white pickup filling up a big balloon. The fun should start at about 1:45.

The DC airport didn't have them in my size, but I thought about it haha


Somewhere over the Dakota's dreaming about what the pictures would look like from 3 times this altitude

29 June 2010

Turn up the heat and the pressure's on!

After a 3 hour test run of my flight computer and cameras I had a lot of success and one problem. I ended the test by putting the whole payload in my freezer. The flight computer came out just fine, but the cameras shut down from being too cold. I've added insulation and space blanket wrapping to keep them warm, but I don't know if it will be enough for the -50 F temperatures. My freezer read as -2 C (about 30 F). Below are graphs of pressure and temperature during the testing.

Pressure in PSIA

Temperature in degrees C

With the data gathering out of the way I've been covering the payload casing in space blanket to make it reflective to radar. I wouldn't appreciate it if an airplane hit my project, but I think they would be even more angry. I've also done more flight predictions and come up with a tentative launch site. The site is east of Billings just off of the freeway on Yellowstone Trail Rd. You can see the launch spot in the picture below. The other picture shows the University of Wyoming's flight prediction (http://weather.uwyo.edu/polar/balloon_traj.html) and the Near Space Venture's landing spot prediction (http://nearspaceventures.com/w3Baltrak/readyget.pl). The difference is that the University of Wyoming won't let me put in my ascent or descent rates. I expect the actual landing site will be somewhere in-between the two, which would be great. I hope to see a ton of people at the launch site and be completely surprised...by the one person following this blog I thing it's going to be just me, Arhan, and my dad though. I'll post the final launch site as soon as it is solidly established and I'm thinking about a twitter feed.


Launching East of Billings, MT on Yellowstone Trail

The flight predictions have the balloon going further east

27 June 2010

Put it all together, and what have you got?

The answer is a somewhat shabby looking near space balloon payload. It is all working brilliantly though! You can see in the pictures below how the parachute bay folds open and how there is a string that wraps around the soda bottle to a 2 prong hook to hold the hatch closed during the ascent. There's a servo motor inside that pulls the hook closed during ascent and pushes the hook out when the set altitude is crossed twice (once going up primes the circuit and then on the way down the servo pushes the hook out letting the parachute hatch be opened by the force of wind).

Parachute bay open, cameras in the green section

GPS antenna on the left, pressure sensor where the coiled wires are in the middle

2 prong hook holding parachute bay closed by the string

Cellular antenna from EAD-LTD.com

Last weekend I also did a test inflation and tethered flight of the payload I had completed at the time. During that inflation I used a nitrogen regulator on a 130 cubic foot helium tank. In-between the regulator and the balloon was just a section of air hose, which worked alright to inflate. The problem with that setup is that I had no idea how much weight the balloon was lifting. I had to use my hand to keep the balloon sealed to the air hose, so we just filled the balloon until the tank was empty. I've seen on a few websites that 1 extra pound of weight is enough to give the balloon a 1000 ft/minute ascent rate. Sure, I could just inflate the balloon all the way and let it rocket up until it bursts, but the less helium I put in the higher it will go (it has to be able to lift the payload of course and it has to make it through the cold tropopause without getting too brittle). With all that in mind I created a new end for the filling hose that weighs just under 1 pound. The filling end fits snugly in the balloon opening and with the help of a hose clamp it will add exactly one pound of weight to my payload. Now I just hang my payload from the red lever on the filling end, fill up the balloon until it starts to lift the payload off the ground, tie everything off, and let it go.

Regulator, hose, and filling end

The filling end is a 3/4" brass plumbing shutoff valve, and a 3/4" to 1/2" adapter to connect to the hose. On the balloon end of the shutoff valve I have added some automotive gasket material and silicone to make the end round instead of the hex bolt shape. It's very difficult to get a good seal on the balloon with the hex shape.

Looks like this time I was early on my deadline! Tomorrow I'm going to do lots of testing and then put the finishing touches on the payload. It will look a lot nicer once I get the space blanket wrapped around it. It's kinda like a classic car without the chrome bumpers right now. See you next time space cowboys!

24 June 2010

GPS Tracking and Logging...finally

Well it's about time that things are coming together. I have one week left before the launch, so it's getting a bit tense. I have decided to scrap the idea of steering the payload back to the launch location. I don't have the time to make the mechanical pieces, the code would be simple though. I also scrapped the SD cards since they were not being very reliable. I am now logging the data to the microcontroller's flash memory. I have three things left to finalize. The first is the parachute ejection system, the second is the pressure sensor logging, and the final one is the finishing touches of the payload. I should have it all tidied up by the end of the weekend...I think I've said those words several times on this blog and have yet to live up to it...
Anyway, below are some pictures of a car trip I took with the flight computer GPS logging and my computer tracking the trip live. I've written a windows application to read the serial data coming from my GM862 cellular module. It displays the most current reading and writes all the captured data to a .kml file to be displayed in Google Earth. In Google Earth you can create a network location that points to the .kml file and has a refresh rate so the data is updated in "real time."



My glorious tracking application!


The track generated by my tracking application (~1 GPS point per second)

The track generated from the flight computer log (10 seconds of averaged data per point)

16 June 2010

Oh, to make a CSD call...

Well, it's been a tough week. The balloons and parachute came in over the weekend, so I was very excited at the start. Then, I spent $50 on an AT&T SIM card only to find out that they don't support CSD calls (it's like a fax machine or a dial-up modem). I borrowed two T-mobile SIM cards to test them for CSD calls and it worked! I quickly went to the T-mobile store and tried to buy two prepaid cards, but this store locks their prepaid cards in a time-lock vault after 5pm. I showed up bright and early the next morning to buy the cards and when I went to test them they didn't actually get activated. Since I'll never be able to get to the store before 5pm before my test launch on Saturday I had to get a refund and go shopping elsewhere. I found another T-mobile store and ended up paying twice as much for the SIM cards as the first store. I was also told that CSD calls would definitely work. By this time it should be no surprise to you that CSD calls do not work with T-mobile prepaid SIM cards. Tomorrow I am going to attempt to get the SIM cards converted into a one month family plan, wish me luck!
Since I don't want to leave you with a bad taste in your mouth (it should be about poopy flavored lollie pop by now), I'm going to attach the balloon and parachute photos below. You'll notice that the parachute is not the normal circular shape. The "x-form" parachute is used by high powered amateur rockets because it reduces the drifting caused by wind. That sounds like a brilliant thing to use on this project. We'll see how it turns out in about two weeks!

6' in diameter!!! 1200 gram balloon from Kaymont

Inlet is about 2.5" in diameter and very stiff

60" x-form parachute from Top Flight Recovery (purchased from Performance Hobbies)

60" chute folds to about 4"x2"

Dismantled Argus Bean camera

Deeper inside

All back together with wires soldered across the shutter switch so I can virtually snap photos with the microcontroller.

07 June 2010

FAA

Before I start talking about who you need to notify about your launch I'm going to tell you that you should read Part 101 Subpart D of the FAA regulations. You can find the current version on the FAA's website. If you read the whole Part 101 regulation you'll notice at the very start that the regulation only applies to free balloons with payloads heavier than 4 pounds, so most amateur projects won't really fall under the scope of the regulation. You should still follow all of the rules in Subpart D to remove yourself from as much liability as possible. Not following the rules can lead to fines, imprisonment, and more importantly could seriously injure or kill someone.
Now, on to who you need to talk to. I have to admit that I've heard horror stories about dealing with the FAA and I was very nervous about calling. It doesn't help that I didn't really know what office I should call or who I needed to talk to about this project. I started by calling the FISDO (flight inspection safety district office) in Helena, MT. They seemed to be the most prominent FAA presence in Montana, and it turned out to be a pretty good choice. The FISDO doesn't have anything to do with small balloon launches, but they did point me to the right ATC (air traffic control) office. The ATC office doesn't actually have anything to do with small free balloons either, but information about your launch will get communicated to them through the NOTAM system. NOTAM (notice to airmen) is a system of hazardous condition reporting for the skies. It covers things from air shows to volcano eruptions...yes we're looking at you Iceland. The ATC office I talked to gave me a phone number for Prescott Flight Service Station (877-487-6867). They apparently handle all of the NOTAM reporting for the "West US." I have no idea what is included in "West US" so you should call the FAA ATC office closest to your launch and ask what the NOTAM reporting number is for your area. When you launch your balloon you should be ready to give your launch location, launch time, time to 60k feet ascending, time of reaching 60k feet descending, expected landing location, and expected landing time. You need to make this call no earlier than 24 hours before the flight and no later than 6 hours before the flight. If you cancel or change the times you need to update the NOTAM immediately. You should also notify the NOTAM station when you actually reach 60k feet up and down, if you lose communication with your balloon, and when your payload has landed if possible. That's all the reporting you need to do.
The 60k feet barrier is important because 99% of aircraft can't fly above that altitude. Unless you're hanging out in Korea or Russia there won't be too many U-2 spy planes cruising past your balloon and there's only one X-37 space plane in existence right now. So, once you're above 60k feet you can breath a little bit easier knowing that you won't be ingested by a jet engine. One note about launch site selection is that you need to make sure you are more than 5 miles away from any airport. It's also a good idea to plan your flight path so that it doesn't cross over any airports. This is in the Part 101 regulations, but it's easy to miss. My launch is still on for 3 JULY 2010 at 1 PM from the Billings, MT area (actual location to be selected about 4 days ahead of launch for weather reasons).

26 May 2010

VectorNav Sensor

I soldered on the VN-100 AHRS (Acceleration Heading Rotation Sensor) from VectorNav. I've setup the sensor to output the yaw, pitch, and roll since I need that information to steer the payload module during its descent. You can see that the yaw, pitch, and roll are output in a NMEA formatted sentence in the picture below. The sensor has 3-axis accelerometer, magnetic sensors, and 3-axis gyro that it uses to create the yaw, pitch, and roll.













































The Honeywell IPT sensor also arrived in the mail today. It's much smaller than I expected and the protective cover on the pressure sensing tube is...well...interestingly shaped... Hopefully over the weekend I'll be able to put all the functions together into a nice data logging package and get started on the servo controls.

23 May 2010

RRRRRUUUUNNNN!!!!!!

UThe trap was set. A man with an orange ribbon watched from across the street as four men with blue ribbons jogged past. He sprinted across the street splitting the group of blue, three ahead and one behind. With a skidding stop the one behind barely missed being tagged and bolts like a startled gazelle in the opposite direction, isolated from his friends. The chase ensued as his three friends looked on, cheering for escape. Down the busy D.C. street, across and up the other side at an all out sprint they went. At the top of the block fortune smiled on the blue runner as another slower blue ribbon came around the corner and the attention of the orange chaser was diverted. Escape! Back to his friends, exhausted, exhilarated, and ready to survive D.C.
This is my story from two weekends ago when I promised that I would have GPS tracking ready. Since I was frustrated with the uM-FPU issue I decided to enter the annual Survive D.C. challenge instead. It was a great time, and I have my strategy set to win next year. As for GPS tracking I just figured out (literally five minutes ago) that the chip selects for my uM-FPU code were setup improperly. Somehow I inadvertently changed the fpu_spi.S file and it screwed everything up for me. Many thanks to Cam at Micromega for his debugging help! Now that I have it working it should be a quick coding session away from KML track files stored on the SD card. Maybe you'll even get two posts in the same day out of me...

UPDATE!!!!
I decided to just update my post instead of making a new post. Below is a Google Earth view of a track I took. I actually walked around the parking lot in a loop, so the track isn't very accurate. The KML file created by the balloon hardware is below the Google Earth picture. The KML format is very easy and you can probably figure it out on your own. If you're struggling check out the KML Tutorial.

<?xml version="1.0" encoding="UTF-8"?><kml xmlns="http://www.opengis.net/kml/2.2"> <Document> <name>Paths </name> <description> </description> <Style id="yellowLineGreenPoly"> <LineStyle> <color>7f00ffff </color> <width>4 </width> </LineStyle> <PolyStyle> <color>7f00ff00 </color> </PolyStyle> </Style> <Placemark> <name>Balloon Track </name> <description> </description> <styleUrl>#yellowLineGreenPoly </styleUrl> <LineString> <extrude>1 </extrude> <tessellate>1 </tessellate> <altitudeMode>clampToGround </altitudeMode> <coordinates>
-77.257050,38.613289,9.081365
-77.257057,38.613308,2.353583
-77.257080,38.613327,2.509873
-77.257095,38.613342,3.073757
-77.257111,38.613358,3.027795
-77.257133,38.613377,2.419199
-77.257149,38.613392,3.866859
-77.257294,38.613525,0.235732
-77.257309,38.613548,0.831693
-77.257317,38.613567,2.116111
-77.257332,38.613594,0.741470
-77.257393,38.613712,0.035968
-77.257408,38.613747,3.533464
-77.257416,38.613762,2.116111
-77.257423,38.613297,3.149637
-77.257439,38.613323,8.648294
-77.257446,38.613346,10.772677
-77.257454,38.613373,13.453123
-77.257462,38.613396,15.093421
-77.257469,38.613422,14.571769
-77.257469,38.613453,8.369545
-77.257469,38.613476,14.250367
-77.257469,38.613495,11.215469
-77.257477,38.613518,9.271531
-77.257477,38.613541,8.516939
-77.257469,38.613564,10.319799
-77.257469,38.613583,12.511400
-77.257469,38.613605,11.633858
-77.257477,38.613628,12.813361
-77.257477,38.613647,3.715162
-77.257477,38.613667,3.324711
-77.257484,38.613693,8.213501
-77.257492,38.613720,8.390788
-77.257469,38.614410,3.092222
-77.257462,38.614483,11.747047
-77.257462,38.614559,38.845306
-77.257462,38.614601,50.285427
-77.257462,38.614582,38.765915
-77.257462,38.614571,13.786172
-77.257462,38.614578,10.733390
-77.257462,38.614586,8.428599
-77.257446,38.614590,9.388164
-77.257423,38.614586,0.905824
-77.257408,38.614601,2.939602
-77.257393,38.614620,2.275262
-77.257378,38.614632,3.469908
-77.257370,38.614655,2.148981
-77.257355,38.614677,3.093412
-77.257339,38.614700,2.980643
-77.257317,38.614719,2.573429
-77.257301,38.614742,2.985564
-77.257278,38.614277,0.159121
-77.257256,38.614288,0.157967
-77.257172,38.614346,2.926479
-77.257156,38.614361,3.323521
-77.257133,38.614376,2.401185
-77.257111,38.614391,3.074147
-77.257095,38.614403,3.051151
-77.257072,38.614418,0.912386
-77.257744,38.614407,2.063618
-77.257713,38.614368,0.603980
-77.257706,38.614349,0.920581
-77.257690,38.614330,2.564366
-77.257690,38.614326,3.520341
-77.257683,38.614304,3.454725
-77.257675,38.614296,10.204970
-77.257668,38.614277,10.006439
-77.257652,38.614731,2.918307
-77.257378,38.613541,2.427822
-77.257240,38.613312,3.723723
-77.257217,38.613300,3.293993
-77.257195,38.613289,8.024935
-77.257187,38.613281,2.655420
-77.257149,38.613739,3.595770
-77.257118,38.613708,2.689068
-77.257080,38.613674,3.831990
-77.257072,38.613678,12.383448
-77.257065,38.613647,9.143579
-77.257057,38.613590,2.204724
-77.256989,38.613518,8.838460
-77.256981,38.613510,3.180385
-77.256950,38.613464,0.999016
-77.255814,38.612747,0.925197
-77.255791,38.612736,2.014436
-77.255775,38.612717,2.316242
-77.255768,38.612709,3.418635
-77.255753,38.612698,10.382257
-77.255722,38.612690,9.806552
-77.255707,38.612667,10.249466
-77.255699,38.612648,33.782318
-77.255692,38.612610,14.921221
-77.255676,38.612564,10.246062
-77.255661,38.612518,10.145915
-77.255638,38.612476,10.592231
-77.255638,38.612438,11.517306
-77.255623,38.612423,9.165108
-77.255608,38.612400,2.404886
-77.255608,38.612328,2.322865
-77.255577,38.612331,3.499016
-77.255569,38.612328,8.767963
-77.255569,38.612328,0.055896
-77.255562,38.612316,3.419886
-77.255524,38.612320,3.431758
-77.255348,38.612591,2.858832
-77.255356,38.612579,12.516404
-77.255348,38.612556,15.675814
-77.255318,38.612530,52.365486
</coordinates>
</LineString>
</Placemark>
</Document>
</kml>

11 May 2010

Where am I, literally?

The uM-FPU's are up and running and so is my GPS receiver. I used the NMEA sentence application note on the micromega website as the basis for a user defined function inside my uM-FPU. The function collects a PUBX, 00 NMEA sentance from the GPS receiver and breaks down it's fields to be stored into the registers of the uM-FPU. The code is working when I load a test sentence into the uM-FPU manually, but there is an unknown problem getting the actual data from the GPS receiver into the uM-FPU. It's getting a bit late, so I decided to find the problem tomorrow. Below are some pictures of the output with the test sentence and the output of my GPS receiver. I'm right on track for my XML output at the end of the week.


09 May 2010

Look Mom, no hands...because soldering irons are hot. Safety First!

Well, after the last circuit boards and their soldering problems I decided to go back to the basics. You can see my classy soldering workspace/kitchen. I used lead free solder, don't worry.


So, like I said...back to the basics. Instead of using a whole loaf of bread while soldering, this time I used bread flour! Let me tell you, it made all the difference. Choosing a 100% organic bread flour gave my soldering that earthy, healthy taste too. All kidding aside there were no problems getting this soldered together and my initial tests were great! I've managed to get both micros running with the desired settings, both SD card slots work as you can see in the picture below, and I'm working through the code for the uM-FPU floating point co-processors now. I should be logging GPS tracks to the SD cards in XML format by the end of the week.


My co-pilot, Arhan, and I have both purchased our airplane tickets for Billings, MT. The flight will happen either on Saturday, July 3rd at 1PM or on Sunday, July 4th at 8am. The location will be one of the three previously discussed. It's really too early to know what the weather will be like. It would be great to have a ton of people show up and help, so if you're at all interested leave a comment and I'll keep you posted on the exact details. Happy Mother's Day Mom!

06 May 2010

PCB's Are In!

The new PCB's are in and they are stylish! I chose to do a white solder mask and black silk screen. They look so good I'm going to make it my new signature colors. These boards were made by Colonial Circuits in Fredericksburg, VA. I highly recommend them for your circuit board needs. I'm always ordering small quantities and their prices are great for the complex boards. They also ship out the yield from the whole panel even if you only asked for one. If you're looking to have a new board made send your gerber files to Beth at sales2@colonialcircuits.com. I should have these babies soldered up and working by the end of the weekend, so I'll leave it here for now. Enjoy the pics and try not to be too jealous!

03 May 2010

One Bad Apple is All it Takes

It's always been a dream of mine to own a company. There's the trill of being in control, fighting to keep everyone paid, and of course my company would be technology centric so I could be on the cutting edge. I realized today that one bad employee, at any level in the company, could ruin the whole thing. Part of this realization came from my new set of bicycle rims being stolen and the other part came from some record breaking NASA news.
The bicycle rims were a birthday present from my parents. They were being FedExed to my apartment and I've never had any issues with deliveries there. Now, bicycle rims aren't small, the box measured 3 feet tall and wide. When I saw the tracking information claiming the package was delivered I expected to find the rims at the front office of the apartment complex. For some reason the FedEx driver left a small package at the front office and decided to leave my giant package at the front door of my apartment. Of course the package was stolen, because one bad apple didn't care enough to think it's foolish to leave packages out in an apartment complex. I don't have a signed agreement with FedEx allowing them to leave packages, so this has reflected very poorly on the company in my mind.
The NASA news is that the Spirit rover on Mars is about to become the longest running program on Mars. Both Spirit and Opportunity were designed to be 90-day missions, and they have both survived more than 6 years! The engineers and crew for these two missions have done a fantastic job! The bad apple side of this story is in the way NASA has taken engineers and mission crew like this and ground them into the pavement with terrible management practices. Granted this problem involves several bad managers, but it had to start with one bad policy decision.
The second revision of circuit boards for the balloon are due in this week, so look for a good status update next week or this weekend. I am tentatively planning to launch on the 3rd of July from a location close to Billings, MT. I will post the exact launch date and time as soon as I get all of my crew to buy their plane tickets. Anyone who wants to help of observe is welcome to show up. Science is always more fun with a group of spectators to see your fantastic failures! Check out the latest NASA balloon project in Australia for a worst case scenario of what you might see at my balloon launch.

12 April 2010

Slow Progress is Still Progress

    I've been dragging my feet a little bit on this project lately. It's mostly due to two reasons. First the weather has been great and I am spending my weekends outside instead of writing code. Second I ended up paying $2500 in taxes this year...that coupled with the adjustment I made to my tax withholding has left my bank account somewhat empty lately. I still need to purchase cameras, servos, the temperature/pressure sensor, balloon, some sort of cable to connect the GPS to the circuit board (probably has to be a flex circuit...expensive), and miscellaneous mechanical stuff.
    The connector on the GPS unit is a very small surface mount connector that is impossible to work with. I've attempted to solder 30 gauge wires to the leads, but even with a good microscope it isn't really working. My first idea was that I could create a flex circuit with the mating connector and a standard 100 mil header on the other end. It would be fairly costly to make the flex circuit, but the GPS unit is quite nice and I'm sure to use it again for other projects. Looking at the actual cost of the flex circuit ($550) I've changed my mind. My new plan is to redesign the main PCB to stand upright in the capsule and have a small tab with the GPS connector where the GPS unit will mount to and stick out of the top of the capsule. The GPS unit needs to at least have the antenna protruding out of the top of the capsule to ensure good signal reception. Redesigning the main board will also give me a chance to fix the programming issues with the mega128.
    Now, as promised there is some code attached. This code has preliminary functions to test all aspects of the micro that is attached to the GPS and cellular modules. The other micro will be attached to the temperature, pressure, and innertial sensors. I haven't written any code for it, but it will look mostly the same. I'm not going to take a lot of time explaining the code. I could easily fill up a year's worth of blog pages with that. There are fairly good comments in the code, so have at it. More coming soon, supposed to be thunderstorms this Friday hahaha.


////////////////////////////////////////////////////////////////
// Project Name: Ahnung
// File Name: ahnung.c
// Description: This is the firmware for MCU1 on the Ahnung
// near space balloon. This firmware handles
// GPS tracking, cellular comms, and parachute deployment.
// Micro: ATMega128
// Date: 22 JAN 2010
// Author: Luke Wardensky
////////////////////////////////////////////////////////////////
#define F_CPU 8000000UL
#define F_PWM 60
#define MY_PWM_FREQ ((F_CPU/8)/F_PWM)-1
#define BAUD 9600
#define LATITUDE 4
#define LONGITUDE 5
#define ALTITUDE 6
#define HOURS 7
#define MINUTES 8
#define SECONDS 9
#define GROUND_SPEED 10
#define VERT_VELOCITY 11
#define COURSE 12
#define HORZ_ACCURACY 13
#define VERT_ACCURACY 14
#define STATUS 15
#define GET_LAT 0
#define GET_LONG 1
#define GET_ALT 2
#define GET_UTC 3
#define GET_GND_SPEED 4
#define GET_VERT_VEL 5
#define GET_COURSE 6
#define GET_HORZ_ACC 7
#define GET_VERT_ACC 8
#define GET_STATUS 9
#define GPS_NO_FIX 0
#define GPS_DEAD_RECKONING 1
#define GPS_READY 2
#define GPS_TIME_ONLY 3
#define NO_FUNCTION -1

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "SPI_routines.h"
#include "SD_routines.h"
#include "FAT32.h"

/////////////////////////////////////////
// Global Variables
/////////////////////////////////////////
unsigned int PWM_FREQ = MY_PWM_FREQ;

/////////////////////////////////////////
// Function Definitions
/////////////////////////////////////////
void micro_init();
int Cell_Init();
void SMS_Test();
char *GPS_Status();
void Test_Chute_Servo();
void Test_Cutdown_Servo();
void Test_SD(char *str);
void delay_seconds(unsigned int seconds);
void uart_gets(char *temp_str, int max_str_size);
int check_cell_response(const char *response);
double read_gps_float(char function, char gps_register);
long read_gps_long(char function, char gps_register);
void delay_seconds(unsigned int seconds);
void uart_gets(char *temp_str, int max_str_size);
int check_cell_response(const char *response);

int main()
{
/////////////////////////////////////////
// Main Variables
/////////////////////////////////////////
unsigned int command=UART_NO_DATA;
char status=0, gps_string[512];
double last_lat=0, last_lng=0, last_altitude=0, lat=0, lng=0, altitude=0, gnd_speed=0, vert_vel=0, horz_acc=0, vert_acc=0, course=0;
unsigned int hours=0, minutes=0, seconds=0;

/////////////////////////////////////////
// Setup the microcontroller ports and
// perripherals
/////////////////////////////////////////
micro_init();

/////////////////////////////////////////
// Configuration & Test Menu
/////////////////////////////////////////
while(command != '7')
{
command = UART_NO_DATA;
uart1_puts_p(PSTR("\r\n**************************************************\r\n            MCU1 Configuration Menu\r\n**************************************************\r\n\r\n"));
uart1_puts_p(PSTR("1. Turn on cellular module\r\n2. Send SMS test message\r\n3. Get GPS status\r\n4. Test parachute servo\r\n5. Test cutdown servo\r\n6. Test SD card\r\n7. Start data logging\r\n\r\n>>"));
while(command == UART_NO_DATA)
{
command = uart1_getc();
}

while((command < '1') || (command > '6'))
{
uart1_puts_p(PSTR("\r\nInvalid selection\r\n>>"));
do
{
command = uart1_getc();
}while(command == UART_NO_DATA);
}

if(command == '1')
{
if(Cell_Init())
{
uart1_puts_p(PSTR("failed!!!!!\r\n"));
}
else
{
uart1_puts_p(PSTR("Cellular module initialization complete.\r\n"));
}
}
else if(command == '2')
{
SMS_Test();
}
else if(command == '3')
{
uart1_puts(GPS_Status());
}
else if(command == '4')
{
Test_Chute_Servo();
}
else if(command == '5')
{
Test_Cutdown_Servo();
}
else if(command == '6')
{
Test_SD("This is a test of the Ahnung SD memory card.\r\n123456789\r\nabcdefghijklmnopqrstuvwxyz\r\nABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n!@#$%^&*()-+=?.,<>\r\n");
}
}

while(1)
{
// Have the uM-FPU get NMEA sentances from the GPS receiver
fpu_write2(SERIN, 6);
fpu_wait();

// Disable the uM-FPU NMEA sentance mode to prevent buffer overflows
fpu_write2(SERIN, 0);

// Get the GPS receiver status
status = read_gps_long(GET_STATUS, STATUS);

if(status == GPS_READY)
{
// Get the timestamp
hours = read_gps_long(GET_UTC, HOURS);
minutes = read_gps_long(NO_FUNCTION, MINUTES);
seconds = read_gps_long(NO_FUNCTION, SECONDS);

// Get the latitude
lat = read_gps_float(GET_LAT, LATITUDE);

// Get the longitude
lng = read_gps_float(GET_LONG, LONGITUDE);

// Get the altitude
altitude = read_gps_float(GET_ALT, ALTITUDE);

// Get the speed over ground
gnd_speed = read_gps_float(GET_GND_SPEED, GROUND_SPEED);

// Get the vertical velocity
vert_vel = read_gps_float(GET_VERT_VEL, VERT_VELOCITY);

// Get the course heading
course = read_gps_float(GET_COURSE, COURSE);

// Get the horizontal accuracy
horz_acc = read_gps_float(GET_HORZ_ACC, HORZ_ACCURACY);

// Get the vertical accuracy
vert_acc = read_gps_float(GET_VERT_ACC, VERT_ACCURACY);

if((last_lat != lat) || (last_lng != lng) || (last_altitude != altitude))
{
sprintf(gps_string, "", lat, lng, altitude, gnd_speed, vert_vel, course, horz_acc, vert_acc, hours, minutes, seconds);
writeFile("GPS.XML", gps_string);

// Format the SMS message string
uart_puts_p(PSTR("AT+CMGS=500\r"));
_delay_ms(500);
// Enter the string into the cell module
uart_puts_p(PSTR("lwardens@gonzaga.edu "));
uart_puts(gps_string);

// Send the CTRL-Z character to send the SMS
uart_putc(0x1A);

last_lat = lat;
last_lng = lng;
last_altitude = altitude;
}
}

// Re-enable the uM-FPU NMEA sentance mode
fpu_write2(SERIN, 4);
}

return 0;
}

void micro_init()
{
/////////////////////////////////////////
// Setup Port A
// Bit: 7 6 5 4 3 2 1 0
// Dir: O O O O O O O O
// Pullup: N N N N N N N N
/////////////////////////////////////////
DDRA = 0xFF;
PORTA = 0x00;

/////////////////////////////////////////
// Setup Port B
// Bit: 7 6 5 4 3 2 1 0
// Dir: I I O O I O O O
// Pullup: P P P P P N N N
/////////////////////////////////////////
DDRB = 0x37;
PORTB = 0xF8;

/////////////////////////////////////////
// Setup Port C
// Bit: 7 6 5 4 3 2 1 0
// Dir: I I I I I I I O
// Pullup: P P P P P P P N
/////////////////////////////////////////
DDRC = 0x01;
PORTC = 0xFE;

/////////////////////////////////////////
// Setup Port D
// Bit: 7 6 5 4 3 2 1 0
// Dir: I I I I O I I I
// Pullup: P P P P N P P P
/////////////////////////////////////////
DDRD = 0x08;
PORTD = 0xF7;

/////////////////////////////////////////
// Setup Port E
// Bit: 7 6 5 4 3 2 1 0
// Dir: I I I O O I O I
// Pullup: P P P N N P N P
/////////////////////////////////////////
DDRE = 0x1A;
PORTE = 0xE5;

/////////////////////////////////////////
// Setup Port F
// Bit: 7 6 5 4 3 2 1 0
// Dir: O I I I I I I I
// Pullup: N P P P P N N N
/////////////////////////////////////////
DDRF = 0x80;
PORTF = 0x78;

/////////////////////////////////////////
// Setup Port G
// Bit: 7 6 5 4 3 2 1 0
// Dir: I I I
// Pullup: P P P
/////////////////////////////////////////
DDRG = 0x00;
PORTG = 0x07;

/////////////////////////////////////////
// Setup Timer 3 PWM
// non-inverting fast PWM
// Fclk = F_CPU/8
// frequency set by ICR3 Fpwm = Fclk/(1+ICR3);
/////////////////////////////////////////
TCCR3A = 0xA2;
TCCR3B = 0x1A;
ICR3 = PWM_FREQ;
OCR3A = PWM_FREQ * (0.321/3.3);
OCR3B = PWM_FREQ * (0.321/3.3);

/////////////////////////////////////////
// Setup SPI bus
/////////////////////////////////////////
spi_init();

/////////////////////////////////////////
// Setup SD card
/////////////////////////////////////////
SD_init();
SPI_HIGH_SPEED; //SCK - 4 MHz
_delay_ms(1);   //some delay

/////////////////////////////////////////
// Setup FAT tables
/////////////////////////////////////////
if(getBootSectorData())  //read boot sector and keep necessary data in global variables
{
uart1_puts("FAT32 not found!");
}

/////////////////////////////////////////
// Enable Interrupts
/////////////////////////////////////////
asm("SEI");

/////////////////////////////////////////
// Setup USART
/////////////////////////////////////////
uart_init(UART_BAUD_SELECT(BAUD, F_CPU));
uart1_init(UART_BAUD_SELECT(BAUD, F_CPU));

/////////////////////////////////////////
// Setup the uM-FPU
// Serial Port: 9600 8/N/1 NMEA parsing
/////////////////////////////////////////
fpu_reset();
fpu_write3(SEROUT, 0, 6);
fpu_write2(SERIN, 4);
}

/////////////////////////////////////////
// Cell_Init function
// This function initializes the cell
// module: 9600 baud, no command echos, auto band select,
// text mode SMS, no call or SMS notifications,
// and use SIM card memory
/////////////////////////////////////////
int Cell_Init()
{
uart1_puts("\r\n********************************************************************************\r\n                    CELLULAR MODULE INITIALIZATION\r\n********************************************************************************\r\n");

char cell_response[UART_RX_BUFFER_SIZE] = {'\0'};

// Check if the cell module is already powered on
uart1_puts_p(PSTR("checking if cell module is powered on..."));
uart_puts_p(PSTR("AT\r"));
if(check_cell_response("OK"))
{
// Power on the cell module
PORTA = 0x80;
delay_seconds(2);
PORTA = 0x00;
delay_seconds(5);
}
uart1_puts_p(PSTR("ok\r\n"));

// Set the cell module baud to 9600
uart1_puts(PSTR("setting baud to 9600..."));
uart_puts(PSTR("AT+IPR=9600\r"));
if(check_cell_response("OK"))
{
return 1;
}
uart1_puts_p(PSTR("ok\r\n"));

// Disable character echos from the cell module
uart1_puts_p(PSTR("disabling command echo..."));
uart_puts_p(PSTR("ATE0\r"));
if(check_cell_response("OK"))
{
uart1_puts_p(PSTR("failed\r\n"));
return 1;
}
uart1_puts_p(PSTR("ok\r\n"));

// Enable auto band selection
uart1_puts_p(PSTR("setting auto band selection..."));
uart_puts_p(PSTR("AT#AUTOBND=2\r"));
if(check_cell_response("OK"))
{
uart1_puts_p(PSTR("failed\r\n"));
return 1;
}
uart1_puts_p(PSTR("ok\r\n"));

// Use text mode for SMS editing
uart1_puts_p(PSTR("setting SMS text mode..."));
delay_seconds(5);
uart_puts_p(PSTR("AT+CMGF=1\r"));
if(check_cell_response("OK"))
{
uart1_puts_p(PSTR("failed\r\n"));
return 1;
}
uart1_puts_p(PSTR("ok\r\n"));

// Disable all incoming and outgoing alerts
uart1_puts_p(PSTR("disabling alerts..."));
delay_seconds(5);
uart_puts_p(PSTR("AT+CNMI=0,0,0,0,0\r"));
if(check_cell_response("OK"))
{
uart1_puts_p(PSTR("failed\r\n"));
return 1;
}
uart1_puts_p(PSTR("ok\r\n"));

// Use SIM card storage for text messages
uart1_puts_p(PSTR("setting SMS memory to SIM card..."));
delay_seconds(5);
uart_puts_p(PSTR("AT+CPMS=SM\r"));
if(check_cell_response("OK"))
{
uart1_puts_p(PSTR("failed\r\n"));
return 1;
}
uart1_puts_p(PSTR("ok\r\n"));

uart1_puts_p(PSTR("waiting for network registration..."));
// Wait for the cell module to register with a network
while(strstr(cell_response, "+CREG: 0,1") == NULL)
{
uart_puts_p(PSTR("AT+CREG?\r"));
delay_seconds(1);
uart_gets(cell_response, UART_RX_BUFFER_SIZE);
delay_seconds(5);
}
uart1_puts_p(PSTR("ok\r\n"));

return 0;
}

/////////////////////////////////////////
// SMS_Test function
// This function sends a SMS message
// including a test string to the hardwired
// email or phone number.
/////////////////////////////////////////
void SMS_Test()
{
char cell_response[UART_RX_BUFFER_SIZE] = {'\0'};

uart1_puts_p(PSTR("\r\n################################################################################\r\n                    SEND SMS TEST MESSAGE\r\n################################################################################\r\n"));

uart1_puts_p(PSTR("sending SMS message..."));

// If there is an error sending the message keep trying
while(strstr(cell_response, "OK") == NULL)
{
// Format the SMS message string
uart_puts_p(PSTR("AT+CMGS=500\r"));
delay_seconds(1);
// Enter the string into the cell module
uart_puts_p(PSTR("lwardens@gonzaga.edu This is a test of the Ahnung SMS message system.\r\nabcdefghijklmnopqrstuvwxyz\r\nABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n1234567890~!@#$%^&*()_+<>"));

// Send the CTRL-Z character to send the SMS
uart_putc(0x1A);

// Check the response from the cell module
uart_gets(cell_response, UART_RX_BUFFER_SIZE);
delay_seconds(5);
}
uart1_puts_p(PSTR("ok\r\n"));
}

/////////////////////////////////////////
// GPS_Status function
// This function sets the uM-FPU to
// receive NMEA sentances and reads the
// GPS status register
/////////////////////////////////////////
char *GPS_Status()
{
char tmp_status;

uart1_puts_p(PSTR("\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n                    GPS STATUS\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n"));

// Have the uM-FPU get a NMEA sentance from the GPS receiver
fpu_write2(SERIN, 6);
fpu_wait();

// Get the GPS receiver status
tmp_status = read_gps_long(GET_STATUS, STATUS);
if(tmp_status == GPS_READY)
{
return "3D GPS Fix Ready\r\n";
}
else if(tmp_status == GPS_DEAD_RECKONING)
{
return "GPS Dead Reckoning Fix Only\r\n";
}
else if(tmp_status == GPS_TIME_ONLY)
{
return "GPS Time Only Fix\r\n";
}
else
{
return "GPS Searching For Singal\r\n";
}

// Disable the uM-FPU serial port to avoid buffer overflows
fpu_write2(SERIN, 0);
fpu_wait();
}

/////////////////////////////////////////
// Test_Chute_Servo function
// This function reads an angle entered
// by the user and converts it to PWM to
// be output on the OCR3B pin
/////////////////////////////////////////
void Test_Chute_Servo()
{
unsigned int servo_cmd=UART_NO_DATA, count=0;
char angle[5];

uart1_puts_p(PSTR("\r\n================================================================================\r\n                    PARACHUTE SERVO TEST\r\n================================================================================\r\n"));
while(1)
{
servo_cmd = UART_NO_DATA;
uart1_puts_p(PSTR("Enter an angle from 0 to 110 degrees or 'E' to exit.\r\n>>"));
while((servo_cmd != '\r') || (count == 0))
{
do
{
servo_cmd = uart1_getc();
}while(servo_cmd == UART_NO_DATA);

if((servo_cmd >= '0') && (servo_cmd <= '9') && (count < 3))
{
uart1_putc(servo_cmd);
angle[count] = servo_cmd;
count++;
angle[count] = '\0';
}
else if((servo_cmd == 'E') || (servo_cmd == 'e'))
{
return;
}
}

// Convert the angle to a PWM pulse width
OCR3B = PWM_FREQ * (0.227 + (atoi(angle) * 0.0022))/3.3;
count = 0;
}
}

/////////////////////////////////////////
// Test_Cutdown_Servo function
// This function reads an angle entered
// by the user and converts it to PWM to
// be output on the OCR3A pin
/////////////////////////////////////////
void Test_Cutdown_Servo()
{
unsigned int servo_cmd=UART_NO_DATA, count=0;
char angle[5];

uart1_puts_p(PSTR("\r\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\r\n                    CUTDOWN SERVO TEST\r\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\r\n"));
while(1)
{
servo_cmd = UART_NO_DATA;
uart1_puts_p(PSTR("Enter an angle from 0 to 110 degrees or 'E' to exit.\r\n>>"));
while((servo_cmd != '\r') || (count == 0))
{
do
{
servo_cmd = uart1_getc();
}while(servo_cmd == UART_NO_DATA);

if((servo_cmd >= '0') && (servo_cmd <= '9') && (count < 3))
{
uart1_putc(servo_cmd);
angle[count] = servo_cmd;
count++;
angle[count] = '\0';
}
else if((servo_cmd == 'E') || (servo_cmd == 'e'))
{
return;
}
}

// Convert the angle to a PWM pulse width
OCR3A = PWM_FREQ * (0.227 + (atoi(angle) * 0.0022))/3.3;
count = 0;
}
}

/////////////////////////////////////////
// Test_SD function
// This function writes the str string
// to the test.dat file on the SD card,
// waits for the user to modify the file on a PC,
// and reads out the modified file
/////////////////////////////////////////
void Test_SD(char *str)
{
unsigned int SD_cmd = UART_NO_DATA;

uart1_puts_p(PSTR("\r\n$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\r\n                   SD CARD TEST\r\n$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\r\n"));
uart1_puts_p(PSTR("Writing string to SD card test.dat file...\r\n"));
uart1_puts(str);
uart1_puts_p(PSTR("\r\n"));
writeFile("test.dat", str);
uart1_puts_p(PSTR("\r\nPlease remove the SD card, read the test.dat file on your PC, modify the file, re-insert the card in the balloon, and press 'G'\r\n>>"));

// Wait for the user to re-insert the SD card and press the G key
while((SD_cmd != 'G') && (SD_cmd != 'g'))
{
SD_cmd = uart1_getc();
}

// Re-initialize the SD card
SPI_SD;
SD_init();
SPI_HIGH_SPEED;
uart1_puts_p(PSTR("\r\nReading SD card test.dat file...\r\n"));
_delay_ms(1);

readFile(READ, "test.dat");
deleteFile("test.dat");
}

/////////////////////////////////////////
// delay_seconds function
// This function uses the _delay_ms()
// function to create a seconds long delay
/////////////////////////////////////////
void delay_seconds(unsigned int seconds)
{
int i = 0;

while(seconds > 0)
{
for(i=0; i<1000; i++)
{
_delay_ms(1);
}

seconds--;
}
}

/////////////////////////////////////////
// uart_gets function
// This function uses the uart_getc()
// function to read all valid characters in
// the uart buffer
/////////////////////////////////////////
void uart_gets(char *temp_str, int max_str_size)
{
int index=0;
unsigned long int timeout=0;
char temp;

// Keep getting characters until the buffer is full or the timeout is reached
while((index < max_str_size) && (timeout < 2000000))
{
temp = (char)uart_getc();
if(temp != 0)
{
temp_str[index] = temp;
index++;
}

timeout++;
}
temp_str[index] = '\0';
}

/////////////////////////////////////////
// check_cell_response function
// This function checks the cellular module's
// response string for 'response'. If 'response'
// is not found the module is powered off and 1 is
// returned, otherwise 0 is returned.
/////////////////////////////////////////
int check_cell_response(const char *response)
{
char temp_response[UART_RX_BUFFER_SIZE] = {'\0'};

uart_gets(temp_response, UART_RX_BUFFER_SIZE - 1);
if(strstr(temp_response, response) == NULL)
{
// Power off the cell module
PORTA = 0xFF;
delay_seconds(2);
PORTA = 0x7F;
delay_seconds(10);
return 1;
}

return 0;
}

/////////////////////////////////////////
// read_gps_float function
// Get one float from the specified
// uM-FPU register
/////////////////////////////////////////
double read_gps_float(char function, char gps_register)
{
if(function >= 0)
{
fpu_write2(FCALL, function);
}

fpu_wait();
fpu_write2(FREAD, gps_register);
return fpu_readFloat();
}

/////////////////////////////////////////
// read_gps_float function
// Get one long from the specified
// uM-FPU register
/////////////////////////////////////////
long read_gps_long(char function, char gps_register)
{
if(function >= 0)
{
fpu_write2(FCALL, function);
}

fpu_wait();
fpu_write2(LREAD, gps_register);
return fpu_readLong();
}