/* APRS rocket tracker kg4wsv Argent Data radio shield MPXAZ6115 pressure sensor MCP3202 SPI dual 12 bit ADC NMEA GPS GPS on hardware TX on UART's RX radioshield RX on UART's TX radioshield TX on bit-banged (newSoftSerial) RX get X,Y from GPS, calculate altitude based on pressure this tracker operates on a private channel, transmitting once every time a new valid $GPGGA sentence is received. normally this is once a second. gmail address: kg4wsv 2011-05-08: corrected a bug in radioshield status check */ #define BEACON Serial.print("@KG4WSV\r\n") #include //#include #include #include #define altitude_t int #define ALT_SAMPLES 4 #define MOSI 11 #define MISO 12 #define SPICLOCK 13 #define SLAVE_SELECT_ADC1 4 NewSoftSerial ss(2,3); altitude_t base_altitude; void setup() { byte c, tick = 10; pinMode(MOSI, OUTPUT); pinMode(MISO, INPUT); pinMode(SPICLOCK, OUTPUT); pinMode(SLAVE_SELECT_ADC1, OUTPUT); digitalWrite(SLAVE_SELECT_ADC1, HIGH); digitalWrite(MOSI, LOW); digitalWrite(SPICLOCK, LOW); Serial.begin(4800); ss.begin(4800); // wait for the radio to boot delay(3000); BEACON; base_altitude = altitude_ft(); // print_prompt_P(PSTR("temp/pressure/voltage test\n")); /************************* TO DO: establish base pressure altitude *************************/ // long term to do: // read GPS altitude, adjust base pressure altitude with GPS data // add timestamp } volatile byte new_GGA = 0; char nmea_buffer[128]; char *lat_ptr, *ns_ptr, *lon_ptr, *ew_ptr; //char *lat_ptr="3434.98", *ns_ptr="N", *lon_ptr="08514.84", *ew_ptr="W"; altitude_t max_AGL = -10000; void loop() { altitude_t altitude_AGL; altitude_t altitude; byte rs_ok = 0; byte rs_status; gps_handler(); if (new_GGA == 1) { new_GGA = 0; altitude = altitude_ft(); altitude_AGL = altitude - base_altitude; if (altitude_AGL > max_AGL) { max_AGL = altitude_AGL; } Serial.print("!!"); // one to tell the OT to start the packet, the other is the first char in a position report Serial.print(lat_ptr); Serial.print(ns_ptr); Serial.print('\\'); // rocket is \O Serial.print(lon_ptr); Serial.print(ew_ptr); Serial.print('O'); // rocket is \O Serial.print(" /A="); Serial.print(altitude); // Serial.print(" AGL "); // Serial.print(altitude_AGL); // Serial.print(" base "); // Serial.print(base_altitude); Serial.print(" max "); Serial.print(max_AGL); Serial.print(' '); Serial.print(battery_voltage()); Serial.print('V'); Serial.print("\r\n"); // we need to block here, and check status of radioshield until // it is through transmitting. this keeps from overflowing the radioshield, // as well as ensures we aren't sampling ADCs when the trasnmitter is active while (!rs_ok) { ssflush(); // flush radioshield input buffer Serial.print("S\r\n"); delay(10); if (ss.available() > 0) { rs_status = ss.read(); if (rs_status == '1') { rs_ok = 1; } } } } } void gps_handler() { char sentence[6]; // NMEA sentence type, without $ , null terminated byte i; if (!Serial.available()) { return; } if (Serial.read() != '$') { return; } while (Serial.available() < 5) {;} for (i=0; i<5; i++) { while (Serial.available() < 1) {;} sentence[i] = Serial.read(); } sentence[i] = '\0'; if (strcmp(sentence, "GPGGA") == 0) { // receive lat/lon begins here i=0; nmea_buffer[i]='\0'; while (nmea_buffer[i] != '\r') { while (!Serial.available()) {;} nmea_buffer[i++] = Serial.read(); // to do: verify checksum as we go } nmea_buffer[i] = '\0'; i=0; while (nmea_buffer[i] != ',') // skip to time { i++; } i++; while (nmea_buffer[i] != ',') // now skip time { i++; } lat_ptr = &nmea_buffer[++i]; while (nmea_buffer[i] != ',') { i++; } nmea_buffer[i-2] = '\0'; ns_ptr = &nmea_buffer[++i]; while (nmea_buffer[i] != ',') { i++; } nmea_buffer[i] = '\0'; lon_ptr = &nmea_buffer[++i]; while (nmea_buffer[i] != ',') { i++; } nmea_buffer[i-2] = '\0'; ew_ptr = &nmea_buffer[++i]; while (nmea_buffer[i] != ',') { i++; } nmea_buffer[i] = '\0'; new_GGA++; } } /* battery voltage input is on a 1/2 voltage divider */ float battery_voltage() { return ain_to_v_12(read_adc(SLAVE_SELECT_ADC1, 1)) * 2.0; } /* read from ADC convert from ADC units to volts convert volts to pressure (kPa?) convert pressure to altitude */ altitude_t altitude_ft() { float pressure, altitude_ft; int adc_pressure = 0; byte i; for (i=0; i< ALT_SAMPLES; i++) { adc_pressure += read_adc(SLAVE_SELECT_ADC1, 2); } adc_pressure = adc_pressure / ALT_SAMPLES; pressure = (ain_to_v_12(adc_pressure) + 0.095*5.0)/(5.0 * 0.009); altitude_ft = (1 - pow(pressure*10.0/1013.25, 0.190284)) * 145366.45; return (altitude_t) altitude_ft; } /* float ain_to_v_10(int a) { return 5.0 * (float)a / 1024.0; } */ float ain_to_v_12(int a) { return 5.0 * (float) a / 4096.0; } /* // print prompts and messages from program flash to save SRAM void print_prompt_P(const char *data) { while (pgm_read_byte(data) != 0x00) Serial.print(pgm_read_byte(data++)); } */ // MCP3202 2 channel ADC // 3202 is different software-wise from 3204/3208 (which are identical) int read_adc(byte cs, byte channel) { int adcvalue = 0; // byte commandbits = B11000000; //command bits - start, mode, chn (3), dont care (3) byte commandbits = B11010000; //command bits - start, mode, chn, MSBF //allow channel selection // commandbits|=((channel-1)<<3); commandbits|=(channel<<5); digitalWrite(cs, LOW); //Select adc // setup bits to be written // for (int i=7; i>=3; i--){ for (int i=7; i>=4; i--){ digitalWrite(MOSI,commandbits&1<=0; i--){ adcvalue+=digitalRead(MISO)< 0) { char c = ss.read(); } }