[Payload] Making a simple payload for a High Altitude Balloon

header_backuyppayload

This post will be about making a simple payload for a high altitude balloon. This involves simple arduino related products, and scraping them together to make a nice rudimentary system. The basis for this article and the images are from the backup payload i used for the Space Camera Live 1. You can read all about that project here.

Let's start with a can of colored markers

What you need

Well, you need something to get the position right (GPS), something to send that out (a GSM or Radiotransmitter), and something to handle all of that (Arduino?) and maybe some storage thingy (MicroSD?). Well, arduino can do all of the previous. Arduino’s are pretty reliable, and proven to work for simple things like this. So see below how i stitched some stuff together. A (uncomplete) partslist:
Seeduino Stalker v2 (+microSD built in) (Arduino)
GPS bee (ublox 5 GPS)
Sony Ericsson T68i (GPRS/SMS)

The insides of this (backup) payload

Making a Antenna

I devoted a seperate article on making a good antenna here.

Making a ‘hot wire’ Cutdown Mechanism

I devoted a seperate article to that here.

Controlling a cheap cellphone with Arduino

Got some stuff on that written down here.

Arduino Code

This code just captures from a serial connection with a ublox GPS module, saves to a microSD and makes a cutdown when certain parameters are set. Also, a Sony Ericsson T68i cellphone is attached, that triggers sending of an SMS (PDU) message to my own cellphone including the most recent valid GPS location every 20 seconds after landing. Written down below here was the full final code succesfully used in the Space Camera Live 1 backup payload.

  1. // (c) Space Camera Live 1, 11-09-2011
  2. // Includes snippets from Daniel Richman, James Coxon, Robert Harisson
  3.  
  4. #include "TinyGPS.h"
  5. #include <stdio.h>
  6. #include <util/crc16.h>
  7. #include <flash.h>
  8. #include <wire.h>
  9. #include <tmp102.h>
  10. #include <cn3063.h>
  11. #include <sdFat.h> //Eats ~8kb
  12. #include <newSoftSerial.h>
  13.  
  14.  
  15. #define ENDLN           "rn"  // SD
  16.  
  17. /* T68i GPRS */
  18. #define num_to_char(number)   ((number) < 10 ?
  19.                                                ('0' + (number)) :
  20.                                                (('A' - 10) + (number)) )
  21. #define first_four(byte)       (0x0F & (byte))
  22. #define  last_four(byte)      ((0xF0 & (byte)) >> 4)
  23. #define hexdump_a(byte)  num_to_char( last_four(byte))
  24. #define hexdump_b(byte)  num_to_char(first_four(byte))
  25.  
  26. NewSoftSerial mySerial(5, 4); //T68I TX Goes in Pin 5
  27.  
  28. TinyGPS gps;
  29.  
  30. int cutdownpin = 9;
  31.  
  32. int count = 0;
  33. byte navmode = 0, maxmode = 0;
  34. float flat=0, flon=0;
  35. unsigned long date, time, chars, age;
  36.  
  37. int hour = 0 , minute = 0 , second = 0, oldsecond = 0;
  38. char latbuf[12] = "0", lonbuf[12] = "0";
  39. long int ialt = 0;
  40. long int maxAlt=0;
  41. int numbersats = 0;
  42.  
  43. int batmv=0;
  44. int temp=0;
  45.  
  46. int descent=0;
  47. unsigned long SmsStart = 0;     // SMS-time
  48. int ExecOnce=0;             // sms checker
  49.  
  50.  
  51. //SD
  52. Sd2Card card;
  53. SdVolume volume;
  54. SdFile root;
  55. SdFile file;
  56.  
  57. // RTTY Functions
  58. /*
  59. void rtty_txstring (char * string)
  60. {
  61.  
  62. 	char c;
  63. 	c = *string++;
  64. 	while ( c != '')
  65. 	{
  66. 		rtty_txbyte (c);
  67. 		c = *string++;
  68. 	}
  69. }
  70.  
  71. void rtty_txbyte (char c)
  72. {
  73.  
  74. 	int i;
  75. 	rtty_txbit (0); // Start bit
  76. 	// Send bits for for char LSB first
  77. 	for (i=0;i<7;i++) //7 or 8 bit ascii
  78. 	{
  79. 		if (c & 1) rtty_txbit(1);
  80. 			else rtty_txbit(0);
  81. 		c = c >> 1;
  82. 	}
  83. 	rtty_txbit (1); // Stop bit
  84.         rtty_txbit (1); // Stop bit
  85. }
  86.  
  87. void rtty_txbit (int bit)
  88. {
  89. 		if (bit)
  90. 		{
  91. 		  // high
  92.                     digitalWrite(5, HIGH);
  93.                     digitalWrite(4, LOW);
  94. 		}
  95. 		else
  96. 		{
  97. 		  // low
  98.                     digitalWrite(4, HIGH);
  99.                     digitalWrite(5, LOW);
  100. 		}
  101.  
  102.                 delay(10);
  103.  
  104.                 //delayMicroseconds(20500); // 10000 = 100 BAUD 20150
  105.                 //delayMicroseconds(20000); // 10000 = 100 BAUD 20150
  106. }*/
  107.  
  108. uint16_t gps_CRC16_checksum (char *string)
  109. {
  110. 	size_t i;
  111. 	uint16_t crc;
  112. 	uint8_t c;
  113.  
  114. 	crc = 0xFFFF;
  115.  
  116. 	// Calculate checksum ignoring the first two $s
  117. 	for (i = 2; i < strlen(string); i++)
  118. 	{
  119. 		c = string[i];
  120. 		crc = _crc_xmodem_update (crc, c);
  121. 	}
  122.  
  123. 	return crc;
  124. }
  125.  
  126. // Send a byte array of UBX protocol to the GPS
  127. void sendUBX(uint8_t *MSG, uint8_t len) {
  128.   for(int i=0; i<len; i++) {
  129.     Serial.print(MSG[i], BYTE);
  130.   }
  131.   Serial.println();
  132. }
  133.  
  134. // Get the current NAV5 mode
  135. int getUBXNAV5() {
  136.  
  137. 	uint8_t b;
  138. 	uint8_t byteID = 0;
  139. 	int startTime = millis();
  140.  
  141. 	// Poll/query message
  142. 	uint8_t getNAV5[] = { 0xB5, 0x62, 0x06, 0x24, 0x00, 0x00, 0x2A, 0x84 };
  143.  
  144. 	// First few bytes of the poll response
  145. 	uint8_t response[] = { 0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF};
  146.  
  147. 	// Interrogate Mr GPS...
  148. 	sendUBX(getNAV5, sizeof(getNAV5)/sizeof(uint8_t));
  149.  
  150. 	// Process his response...
  151. 	while (1) {
  152.  
  153. 		// Timeout if no valid response in 3 seconds
  154. 		if (millis() - startTime > 3000) {
  155. 			return -1;
  156. 		}
  157.  
  158. 		// Make sure data is available to read
  159. 		if (Serial.available()) {
  160. 			b = Serial.read();
  161.  
  162. 			// 8th byte is the nav mode
  163. 			if (byteID == 8) {
  164. 				return b;
  165. 			}
  166. 			// Make sure the response matches the expected preamble
  167. 			else if (b == response[byteID]) {
  168. 				byteID++;
  169. 			} else {
  170. 				byteID = 0;	// Reset and look again, invalid order
  171. 			}
  172.  
  173. 		}
  174. 	}
  175.  
  176. }
  177.  
  178.  
  179. void setupGPS() {
  180.  
  181.   //Turning off all GPS NMEA strings apart on the uBlox module
  182.   Serial.println("$PUBX,40,GLL,0,0,0,0*5C");
  183.   Serial.println("$PUBX,40,GGA,0,0,0,0*5A");
  184.   Serial.println("$PUBX,40,GSA,0,0,0,0*4E");
  185.   Serial.println("$PUBX,40,RMC,0,0,0,0*47");
  186.   Serial.println("$PUBX,40,GSV,0,0,0,0*59");
  187.   Serial.println("$PUBX,40,VTG,0,0,0,0*5E");
  188.  
  189.   Serial.begin(9600);
  190.  
  191.   delay(3000); // Wait for the GPS to process all the previous commands
  192.  
  193.   // Check and set the navigation mode (Airborne, 1G)
  194.   uint8_t setNav[] = {0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x05, 0x00, 0xFA, 0x00, 0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xDC};
  195.   sendUBX(setNav, sizeof(setNav)/sizeof(uint8_t));
  196.   navmode = getUBXNAV5();
  197.   //Serial.println(navmode, BIN);
  198.   delay(500);
  199.  
  200.   //set GPS to Maximum performance mode
  201.   uint8_t setMax[] = {0xB5, 0x62, 0x06, 0x11, 0x02, 0x01, 0x00, 0x00, 0x19, 0x81};
  202.   sendUBX(setMax, sizeof(setMax)/sizeof(uint8_t));
  203.   maxmode = getUBXNAV5();
  204.   //Serial.println(maxmode, BIN);
  205.   delay(500);
  206.  
  207. }
  208.  
  209.  
  210. // More GPS
  211.  
  212. /*void readgps(){
  213.   Serial.flush();
  214.  
  215.   bool newdata = false;
  216.   unsigned long start = millis();
  217.  
  218.   Serial.println("$PUBX,00*33");
  219.  
  220.   // Check for 1 second
  221.   while (millis() - start < 1000)
  222.   {
  223.     if (feedgps()){
  224.       newdata = true;
  225.     }
  226.   }
  227.   if (newdata)
  228.   {
  229.     //Serial << F("* GPSDATA:") << ENDLN;
  230.     gpsdump(gps);
  231.   }
  232. }
  233.  
  234. void gpsdump(TinyGPS &gps)
  235. {
  236.  
  237.     gps.get_datetime(&date, &time, &age);
  238.     hour = (time / 1000000);
  239.     minute = ((time - (hour * 1000000)) / 10000);
  240.     second = ((time - ((hour * 1000000) + (minute * 10000))));
  241.     second = second / 100;
  242.  
  243.  
  244.  
  245.     numbersats = gps.sats();
  246.  
  247.     if (numbersats >= 1) {
  248.  
  249.       //Get Position
  250.       gps.f_get_position(&flat, &flon);
  251.  
  252.       //convert float to string
  253.       dtostrf(flat, 7, 4, latbuf);
  254.       dtostrf(flon, 7, 4, lonbuf);
  255.  
  256.       //just check that we are putting a space at the front of lonbuf
  257.       if(lonbuf[0] == ' ')
  258.       {
  259.         lonbuf[0] = '+';
  260.       }
  261.  
  262.       // +/- altitude in meters
  263.       ialt = (gps.altitude() / 100);
  264.     }
  265.  
  266. }
  267.  
  268. bool feedgps()
  269. {
  270.   while (Serial.available()){
  271.     if (gps.encode(Serial.read()))
  272.       return true;
  273.   }
  274.   return false;
  275. }*/
  276.  
  277.  
  278.  
  279.  
  280. // Sensor-stuff
  281.  
  282. int gettemp(){
  283.   int t;
  284.   TMP102.getValues(&t);
  285.   return t;
  286. }
  287.  
  288. int getbat(){
  289.   int bat_volt;
  290.   CN3063.getBatVolt(&bat_volt); //in mV
  291.   return bat_volt;
  292. }
  293.  
  294. /* T68i SMS Code */
  295.  
  296. void hexdump_byte(unsigned char byte)
  297. {
  298.   mySerial.print(hexdump_a(byte), BYTE);
  299.   mySerial.print(hexdump_b(byte), BYTE);
  300. }
  301.  
  302. void send_sms(char *data)
  303. {
  304.   size_t data_length, x;
  305.   char c, l;
  306.   long i;
  307.   long n;
  308.  
  309.   data_length = strlen(data);
  310.   i = data_length * 7;
  311.  
  312.   /* Round i up to a multiple of 8 */
  313.   if (i & 0x07) i = (i & ~0x07) + 0x08;
  314.  
  315.   /* Calculate the number of message octets */
  316.   i = i / 8;
  317.  
  318.   mySerial.println("AT+CMGF=0");
  319.   delay(1500);
  320.   mySerial.print("AT+CMGS=");
  321.   delay(1500);
  322.   mySerial.println(i + 14);
  323.   delay(1500);
  324.   mySerial.print("0011000B911356537837F80000AA");
  325.   hexdump_byte(data_length & 0xFF);
  326.  
  327.   /* from sms_example_v2.c ALIEN Project Daniel Richman */
  328.   l = 0;
  329.   n = 0;
  330.  
  331.   for (x = 0; x < data_length; x++)
  332.   {
  333.     if (data[x] == '$')  data[x] = 0x02;
  334.  
  335.     n |= (data[x] & 0x7F) << l;
  336.     l += 7;
  337.  
  338.     if (l >= 8)
  339.     {
  340.       hexdump_byte(n & 0xFF);
  341.       l -= 8;
  342.       n >>= 8;
  343.     }
  344.   }
  345.  
  346.   if (l != 0)
  347.   {
  348.     hexdump_byte(n & 0xFF);
  349.   }
  350.  
  351.   mySerial.println(0x1A, BYTE);
  352. }
  353.  
  354.  
  355.  
  356.  
  357.  
  358. /*Setup and Main*/
  359.  
  360.  
  361.  
  362.  
  363.  
  364.  
  365. void setup()
  366. {
  367.   pinMode(4, OUTPUT); //Radio Tx0
  368.   pinMode(5, OUTPUT); //Radio Tx1
  369.   Serial.begin(9600);
  370.   mySerial.begin(9600);
  371.   pinMode(cutdownpin, OUTPUT);
  372.   //mySerial.println("Hello, world?");
  373.  
  374.   CN3063.attach_ana(7);
  375.   TMP102.init();
  376.   delay(5000); // We have to wait for a bit for the GPS to boot otherwise the commands get missed
  377.  
  378.   setupGPS();
  379.  
  380.     //SD
  381.   if (!card.init()) Serial << F("* SD card init. failed!") << ENDLN;
  382.   if (!volume.init(&card)) Serial << F("* volume init. failed!") << ENDLN;
  383.   if (!root.openRoot(&volume)) Serial << F("* openRoot failed") << ENDLN;
  384.  
  385.   // Check for an available filename
  386.   char fileName[13];
  387.   Serial << F("* Opening log file...") << ENDLN;
  388.   for (int i = 0; i < 1000; i++) {
  389.     sprintf(fileName, "SCLOG%03d.TXT", i);
  390.     if (file.open(&root, fileName, O_CREAT | O_EXCL | O_WRITE)) break;
  391.   }
  392.  
  393.   // Ensure we opened the file without error
  394.   if (!file.isOpen()) {
  395.     Serial << F("* Failed to open log file!") << ENDLN;
  396.   }
  397.   else {
  398.     file.writeError = false;
  399.     Serial << F("* Logging to ") << fileName << ENDLN;
  400.     file.write("* ");
  401.     file << F("Project SpaceCameraLive booted!") << ENDLN;
  402.     if (file.writeError || !file.sync()){
  403.       Serial << F("* Error writing to SD card!") << ENDLN;
  404.     }/* else {
  405.       send_sms("SD Works!");
  406.     }*/
  407.   }
  408.   Serial << ENDLN;
  409.  
  410.  
  411. }
  412.  
  413.  
  414. /* Main Loop */
  415.  
  416.  
  417.  
  418. void loop() {
  419.     char superbuffer [120];
  420.     char checksum [10];
  421.     int n;
  422.  
  423.     temp=gettemp();
  424.     batmv=getbat();
  425.  
  426.     Serial.println("$PUBX,00*33"); //Poll GPS
  427.  
  428.     while (Serial.available())
  429.     {
  430.       int c = Serial.read();
  431.       if (gps.encode(c))
  432.       {
  433.         //Get Data from GPS library
  434.         //Get Time and split it
  435.         gps.get_datetime(&date, &time, &age);
  436.         hour = (time / 1000000);
  437.         minute = ((time - (hour * 1000000)) / 10000);
  438.         second = ((time - ((hour * 1000000) + (minute * 10000))));
  439.         second = second / 100;
  440.       }
  441.     }
  442.  
  443.     numbersats = gps.sats();
  444.  
  445.     if (numbersats >= 1) {
  446.  
  447.       //Get Position
  448.       gps.f_get_position(&flat, &flon);
  449.  
  450.       //convert float to string
  451.       dtostrf(flat, 7, 4, latbuf);
  452.       dtostrf(flon, 7, 4, lonbuf);
  453.  
  454.       //just check that we are putting a space at the front of lonbuf
  455.       if(lonbuf[0] == ' ')
  456.       {
  457.         lonbuf[0] = '+';
  458.       }
  459.  
  460.       // +/- altitude in meters
  461.       ialt = (gps.altitude() / 100);
  462.     }
  463.  
  464.     n=sprintf (superbuffer, "$$PD4TA,%d,%02d:%02d:%02d,%s,%s,%ld,%d,%d,%d", count, hour, minute, second, latbuf, lonbuf, ialt, numbersats, temp, batmv);
  465.     if (n > -1){
  466.       n = sprintf (superbuffer, "%s*%04Xn", superbuffer, gps_CRC16_checksum(superbuffer));
  467.       //noInterrupts(); //DO NOT USE (No)Interrupts!
  468.       //rtty_txstring(superbuffer); //Not using RTTY on this launch in this module
  469.       //interrupts(); //DO NOT USE (No)Interrupts!
  470.  
  471.     }
  472.  
  473.     /* SMS send parameters */
  474.     /* After 15 strings.. */
  475.     if( count == 15){
  476.       send_sms(superbuffer);
  477.     }
  478.  
  479.     // Keep track of the maximum altitude
  480.     if (ialt > maxAlt) {
  481.       maxAlt = ialt;
  482.     }
  483.     // Check to see if we've fallen 1000m, if so switch to descent mode
  484.     if (ialt < (maxAlt - 1000)) {
  485.       descent = 1;
  486.     }
  487.  
  488.     /* When it has dropped below 900m */
  489.     if (descent == 1){
  490.       if (ialt < 900){
  491.  
  492.         if (ExecOnce == 0){
  493.           SmsStart = millis();
  494.           ExecOnce = 1;
  495.           send_sms(superbuffer);
  496.           file << "SMS verstuurd" << ENDLN;
  497.           delay(50);
  498.         }
  499.         if (millis() - SmsStart > 20000){
  500.           ExecOnce =0;
  501.         }
  502.  
  503.       }
  504.     }
  505.  
  506.     /* Cutdown Paramters
  507.     (1,2) If its to the north OR east of Urk
  508.     (3) OR if it is in descent mode
  509.  
  510.     */
  511.     if( flon > 5.6 || flat > 52.7 || descent == 1 )
  512.     {
  513.       Serial.println("Cutdown params met");
  514.       digitalWrite(cutdownpin, HIGH);
  515.       delay(5000);
  516.       digitalWrite(cutdownpin, LOW);
  517.       delay(2000);
  518.       digitalWrite(cutdownpin, HIGH);
  519.       delay(10000);
  520.       digitalWrite(cutdownpin, LOW);
  521.       delay(2000);
  522.       digitalWrite(cutdownpin, HIGH);
  523.       delay(5000);
  524.       digitalWrite(cutdownpin, LOW);
  525.     } /*else {
  526.       Serial.println("Cutdown params not met");
  527.     }
  528.  
  529.       Serial.println(flat);
  530.       Serial.println(flon);
  531.       Serial.println(ialt);
  532.       Serial.println(maxAlt);
  533.       Serial.println(descent);
  534.       Serial.println(ExecOnce);
  535.       Serial.println();
  536.     */
  537.     count++;
  538.  
  539.     //Serial << superbuffer << ENDLN;
  540.     file << superbuffer << ENDLN;
  541.     file.sync(); //Force update SD
  542.     delay(1000); //give some rest
  543.     delay(2500); //Extra rest
  544. }

Tim Zaman

Engineer from The Netherlands (1988). Living in Delft. MSc degree in Robotics. Is on a PhD research position on development of imaging devices. For contact see 'About' in the above menu.

You may also like...

1 Response

  1. Edwin Zamora says:

    Buen trabajo. Felicitaciones.

Leave a Reply

Your email address will not be published. Required fields are marked *


two × = 14

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre lang="" line="" escaped="" cssfile="">