A.P.G.E. (Altitude & Position Gauging Electronics)
Introduction
As previously mentioned with A.I.I.R.S., the L1 program is a great way to learn the fundamentals of rocketry, but has little need for avionics. However, as an Electrical Engineering major, I know everything is better with electronics. The A.P.G.E. flight computer was designed as a way to teach team members the basics of avionics such as reading electronics datasheets, designing PCBs from scratch, and programming.
This board includes two accelerometers (High-G and Low-G), an altimeter, a GPS, a microcontroller to coordinate all of the sensors, and a MicroSD slot to record the data. A.P.G.E. has a special place in my heart as it was the first board I really worked on independently. It turned out pretty great in my opinion. My only wish was for “yellow” silkscreen to be more yellow than orange. I did learn my lesson, however, that black silkscreen is always the way to go.
Electrical Specifications
- (1) – LD1117D33CTR 3.3V Regulator
- (1) – Teensy-LC Microcontroller
- (1) – ADXL377 200g Accelerometer
- (1) – ADXL326 16g Accelerometer
- (1) – SparkFun Venus GPS
- (1) – Piezo Buzzer
- (1) – MicroSD slot
The Teensy platform was chosen for this project due to its easy integration with the Arduino IDE (Teensyduino), as well as its increased speed over most Arduino. Both accelerometers have easy-to-use analog outputs and are very basic from a schematic standpoint, only requiring some external capacitors. The Venus GPS was used on recommendation from another member of Launch Initiative, and communicates with the Teensy over UART. The Piezo Buzzer is extremely loud and obnoxious, but is used to help find the rocket during recovery. A slide switch is inline with the buzzer so it is not always active when the board is powered. The MicroSD card was mentally a bigger challenge than it should have been, mainly my own insanity rechecking the pinout every time I opened the project.
Structures
As stated before, A.P.G.E. was designed to fit the form-factor of the L1 rocket (~2.75″ diameter). With this in mind, the board needs a means to stay in place inside the rocket. This structure is known as a sled. The sled for A.P.G.E. was designed in Fusion 360 and is made from laser-cut 1/8″ plywood. The sled has a nut epoxied onto its end to fit a threaded rod. This rod screws into the base of the nosecone, allowing for the ability to adjust the position of the sled in the payload section of the rocket. Functionally, one puts the sled in the rocket while the threaded rod is fully extended and twists the nosecone until the nosecone sits flush with the rocket body. This method guarantees the sled is completely up against the back of the payload section and has pressure against the threaded rod and nosecone.
Code
/*--------------------------------------------------------------- RIT Launch Initiative A.P.G.E. Flight Computer L1 Program Engineer: Joseph Even Description: Complete code for the A.P.G.E. Flight Computer ----------------------------------------------------------------*/ // GPS #include#include static const int RXPin = 7, TXPin = 8; static const uint32_t GPSBaud = 9600; TinyGPSPlus gps; SoftwareSerial ss(RXPin, TXPin); //Altimeter #include #include "MPL3115A2.h" MPL3115A2 altimeter; //Create an instance of the object // MicroSD #include #include // PIN ASSIGNMENTS //const byte XXXX = 1; const byte Int2 = 2; const byte Int1 = 3; //const byte XXXX = 4; //const byte XXXX = 5; //const byte XXXX = 6; //const byte RX3 = 7; TeensyRX3 //const byte TX3 = 8; TeensyTX3 //const byte XXXX = 9; const byte SDCS = 10; //const byte MOSI = 11; //const byte MISO = 12; //const byte SCLK = 13; const byte xOut200 = 14; const byte yOut200 = 15; const byte zOut200 = 16; const byte xOut16 = 17; const byte yOut16 = 18; const byte zOut16 = 19; //const byte XXXX = 20; //const byte XXXX = 21; //const byte SCL = 22; //SCL //const byte SDA = 23; //SDA // FUNCTION CALLS void wait(); void getGPS(); void getAlt(); void getAccel(); void writeSD(); void printData(); // VARIABLES //Control bool wait_var = 1; int start_threshold = 560; // Acceleration threshold for rocket launching and beginning SD card write unsigned long postStart_time_threshold = 10000; // Time after acceleration -- slows sampling rate //Timers unsigned long currentMillis = 0; unsigned long previousMillis = 0; unsigned long accel_start = 0; // Time of beginning of acceleration unsigned long previous_Write = 0; unsigned long data_acquisition_time = 300000; // 5 minutes of data acquisition after start of acceleration //GPS float gps_lat = 0; float gps_long = 0; int gps_hour = 0; int gps_minute = 0; int gps_second = 0; //Altimeter float altft = 0; float pressure = 0; float tempC = 0; //200G int x200_raw = 0; int y200_raw = 0; int z200_raw = 0; //16G int x16_raw = 0; int y16_raw = 0; int z16_raw = 0; //MicroSD void setup() { Serial.begin(115200); // GPS ss.begin(GPSBaud); //Altimeter Wire1.begin(); // Join i2c bus altimeter.begin(); // Get sensor online altimeter.setModeAltimeter(); // Measure altitude above sea level in meters altimeter.setModeBarometer(); // Measure pressure in Pascals from 20 to 110 kPa altimeter.setOversampleRate(7); // Set Oversample to the recommended 128 altimeter.enableEventFlags(); // Enable all three pressure and temp event flags // Accelerometers pinMode(xOut16, INPUT); //x16 pinMode(yOut16, INPUT); //y16 pinMode(zOut16, INPUT); //z16 pinMode(xOut200, INPUT); //x200 pinMode(yOut200, INPUT); //y200 pinMode(zOut200, INPUT); //z200 // SD Serial.print("Initializing SD card..."); // see if the card is present and can be initialized: if (!SD.begin(SDCS)) { Serial.println("Card failed, or not present"); // don't do anything more: return; } Serial.println("Card initialized."); } // End Setup void loop() { currentMillis = millis(); // Loop Time if ( (currentMillis - accel_start) < data_acquisition_time) { // Stops recording data when over data_acquistion_time if (wait_var == 1) { wait(); } else { if ( (currentMillis - accel_start) < postStart_time_threshold ) { // Sample Rate 10 Hz if ( (currentMillis - previous_Write) > 100 ) { getGPS(); //getAlt(); getAccel(); writeSD(); previous_Write = millis(); } } if ( (currentMillis - accel_start) > postStart_time_threshold ) { // Sample Rate 1 Hz if ( (currentMillis - previous_Write) > 1000 ) { getGPS(); //getAlt(); getAccel(); writeSD(); previous_Write = millis(); } } } // End else delay(5); //printData(); } // End data_acquisition_time if } // End Loop // EXTERNAL FUNCTIONS void wait() { while ( x16_raw < start_threshold ) { // Stay in while loop until rocket begins to accelerate x16_raw = analogRead(xOut16);// Collect data for datalogging trigger (Vertical x-Axis) Serial.print("Wait--"); //Check for GPS lock getGPS(); Serial.print("Lat: "); Serial.print(gps_lat); Serial.print(" Long: "); Serial.println(gps_long); delay(10); } wait_var = 0; accel_start = millis(); } // End wait void getGPS() { while (ss.available() > 0) { if (gps.encode(ss.read())) { // Location if (gps.location.isValid()) { gps_lat = gps.location.lat(); gps_long = gps.location.lng(); } else { gps_lat = 180; gps_long = 180; } // Time if (gps.time.isValid()) { gps_hour = gps.time.hour(); gps_minute = gps.time.minute(); gps_second = gps.time.second(); } else { gps_hour = 60; gps_minute = 60; gps_second = 60; } } // End GPS Valid } // End While } // End getGPS void getAlt() { //Pressure pressure = altimeter.readPressure(); //Serial.print(pressure); Serial.println(" Pa"); //Altitude altft = altimeter.readAltitudeFt(); //Serial.print(altft); Serial.println(" Ft"); //Temperature tempC = altimeter.readTemp(); //Serial.print(tempC); Serial.println(" *C"); //Serial.println(); } // End getAlt void getAccel() { // 200g x200_raw = analogRead(xOut200); y200_raw = analogRead(yOut200); z200_raw = analogRead(zOut200); // 16g x16_raw = analogRead(xOut16); y16_raw = analogRead(yOut16); z16_raw = analogRead(zOut16); } // End getAccel void writeSD() { File datalog = SD.open("datalog.txt", FILE_WRITE); if (datalog) { //Timer datalog.print(millis()); datalog.print(", "); // Milliseconds Since Startup //GPS datalog.print(gps_hour); datalog.print(", "); // Hour datalog.print(gps_minute); datalog.print(", "); // Minute datalog.print(gps_second); datalog.print(", "); // Second datalog.print(gps_lat); datalog.print(", "); // Latitude datalog.print(gps_long); datalog.print(", "); // Longitude //High G datalog.print(x200_raw); datalog.print(", "); // X200 datalog.print(y200_raw); datalog.print(", "); // Y200 datalog.print(z200_raw); datalog.print(", "); // Z200 //Low G datalog.print(x16_raw); datalog.print(", "); // X16 datalog.print(y16_raw); datalog.print(", "); // Y16 datalog.print(z16_raw); datalog.print(", "); // Z16 //Altimeter datalog.print(altft); datalog.print(", "); // Altitude datalog.print(pressure); datalog.println(""); // Pressure // println for last line // No comma after last data set datalog.close(); // Needed to write to SD } //End if } // End writeSD void printData() { //GPS Serial.print(gps_hour); Serial.print(", "); // Hour Serial.print(gps_minute); Serial.print(", "); // Minute Serial.print(gps_second); Serial.print(", "); // Second Serial.print(gps_lat); Serial.print(", "); // Latitude Serial.print(gps_long); Serial.print(", "); // Longitude //High G Serial.print(x200_raw); Serial.print(", "); // X200 Serial.print(y200_raw); Serial.print(", "); // Y200 Serial.print(z200_raw); Serial.print(", "); // Z200 //Low G Serial.print(x16_raw); Serial.print(", "); // X16 Serial.print(y16_raw); Serial.print(", "); // Y16 Serial.print(z16_raw); Serial.print(", "); // Z16 //Altimeter Serial.print(altft); Serial.print(", "); // Altitude Serial.print(pressure); Serial.println(""); // Pressure }