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();
}

15 March 2010

What's This PBS?

    I've been making slow and steady progress on the balloon's firmware. Since the microcontrollers on my PCB are throwing a tantrum and not letting me program them in-system I've been using an STK600 and an ATMega64 to do my development. The real balloon will be using an ATMega128, so the Mega64 I'm using for development should keep me well under the memory size for the real balloon. I currently have the diagnostic and startup functions coded. When the balloon starts up both microcontrollers will boot to a configuration menu. The menu will allow the operator to use a laptop and a terminal program to exercise each of the balloon's functions. There's an option in the menu to start the flight program and data logging functions for the real flight. All that's left is to write the data logging, flight routines (things like cutting off the balloon after it bursts and deploying the parachute at the right altitude), and assembling the hardware components together.
    To keep me inspired I've been watching some scientific and creative TV shows from Netflix. I've just found a fantastic show that aired on PBS in 2002. The show is called Rough Science. It's a mix of Mythbuster's type challenges and MacGuyver type science. It has really inspired me and shown how fun science can be. The first season the team used basic chemistry, botany, physics, and very simple tools to collect gold and smelt it into jewelry! The second season, which I'm currently watching, is all about space as simulated in Death Valley. If you want to recapture the excitement of high school science experiments without all the work and weird teacher guys, you need to watch this show!
    Hopefully next time I'll post up some code...that is if I have more to show you than some terminal output. See you next time space cowboys! *cue banjo music and gun shots*

02 March 2010

Launch and Landing

    While I've been working on the hardware and firmware for the balloon I've also been thinking about where to launch from and where it might land. I've been using the Near Space Flight Tracking Utility from Near Space Ventures, Inc. While the Google map plotting doesn't seem to work, the XML file is well formatted with altitudes and lat/long. I've entered in data assuming a 3 pound capsule, a kaymont 1500 gram cold weather balloon, and the secret design item I've been alluding to... When the balloon reaches burst altitude I've added in the hardware capability to separate the balloon scraps from the capsule. I've also added in hardware to release a parachute at a specified altitude. Finally, Arhan will be programming two servos that control steering fins on the end of the capsule, which will be rocket shaped. All of this added together amounts to something close to a GPS guided bomb, without the explosives of course. The capsule will separate from the balloon after burst, attempt to steer itself back to its launch location, and at a safe altitude it will deploy the parachute. All of that should reduce the effects of wind drift during landing.
    I'm living in the D.C. area now, and everywhere I look is city or dense trees. I haven't been in the area all that long either and I'm not certain about good launch sites, so I decided to go with what I know. I've picked a few launch sites near Billings, MT where I know there are few people, few trees, and predictable weather patterns. The maps below show the predictions from Near Space Ventures that I've taken over the past couple months. You can see that I have several options depending on which way the wind decides to blow on launch day. Along with the launch I plan on taking my summer vacation in Yellowstone National Park this year! If everything goes well with the hardware and firmware I'm planning on an early July launch.