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

here.

[caption id="attachment_1943" align="aligncenter" width="272" caption="Let's start with a can of colored markers"][/caption]

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)
[caption id="attachment_1942" align="aligncenter" width="620" caption="The insides of this (backup) payload"][/caption]

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

You may also like...

1 Response

  1. Edwin Zamora says:

    Buen trabajo. Felicitaciones.