Created
August 7, 2016 09:40
-
-
Save jamescoxon/1662093161c996b6a73cf1958bb3f12c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| UKHASnet rf69_repeater code | |
| based on rf22_client.pde/ino from the RF22 library | |
| */ | |
| #include <SPI.h> | |
| //You'll need to use the RFM69 library from the UKHASnet repo | |
| #include <RFM69.h> | |
| #include <RFM69Config.h> | |
| #include <avr/io.h> | |
| #include <avr/interrupt.h> | |
| #include <string.h> | |
| //*************Node ID Setup ****************/ | |
| char id[] = "BALL"; //Please register your node on the UKHASnet website | |
| int node_type = 4; //0 = gateway, 1 = repeater voltage, 2 = bridge (not implemented), 3 = low power node (please set battery thresholds), 4 = balloon | |
| char location_string[] = "0,0,0"; | |
| //*************Pin Setup ****************/ | |
| int batt_pin = 5; //ADC 0 - measure battery voltage | |
| int charge_pin = 1; //ADC 1 - measure solar voltage | |
| //*************Misc Setup ****************/ | |
| byte num_repeats = '5'; //The number of hops the message will make before stopping | |
| int battV_threshold = 1000; // ADC reading which below will stop listening and only tx | |
| int slow_freq_threshold = 800; // ADC reading which below will reduce frequency of tx to conserve power | |
| int n, battV = 0, chargeV = 0, packet_len, delay_length = 50, count = 0, tx_trigger = 0, num_counts = 9, rx_packets = 0, reset_pin = A0, total_count = 0; | |
| byte data_count = 97; // 'a' | |
| char data_temp[64], string_end[] = "]"; | |
| int navmode = 9, GPSerror = 1; | |
| uint8_t buf[80]; //GPS receive buffer | |
| int32_t lat = 12345, lon = 6789, alt = 0; | |
| uint8_t lock = 0, sats = 0; | |
| volatile int f_timer=0; | |
| // Singleton instance of the radio | |
| RFM69 rf69; | |
| void setupRFM69(){ | |
| if(!rf69.init()){ | |
| //Serial.println("RFM69 Failed"); | |
| } | |
| else{ | |
| //Serial.println("RFM69 Booted"); | |
| } | |
| delay(100); | |
| attachInterrupt(0, interrupt_function, RISING); | |
| } | |
| void interrupt_function(){ | |
| rf69.isr0(); | |
| /* set the flag. */ | |
| f_timer = 2; | |
| } | |
| ISR(TIMER1_COMPA_vect) | |
| { | |
| /* set the flag. */ | |
| f_timer = 1; | |
| } | |
| int gen_Data(){ | |
| //**** Internal Temperature (RFM69) ****** | |
| //byte intTemp = rf69.readTemperature(-1); // -1 = user cal factor, adjust for correct ambient | |
| byte intTemp = 0; | |
| //**** RSSI ****** | |
| uint8_t rssi = rf69.lastRssi(); | |
| if(node_type == 0 || data_count == 97){ | |
| //For Gateway (no sensors attached) | |
| // Location is hard coded | |
| n=sprintf(data_temp, "%c%cL%sT%dR%d[%s]", num_repeats, data_count, location_string, intTemp, rssi, id); | |
| } | |
| else if(node_type == 4){ | |
| //Stop stuff | |
| SPI.end(); | |
| detachInterrupt(0); | |
| //**** Battery Voltage ****** | |
| battV = analogRead(batt_pin); | |
| //**** GPS Data ****** | |
| gps_check_nav(); | |
| if(navmode != 6){ | |
| setupGPS(); | |
| delay(2000); | |
| } | |
| gps_check_lock(); | |
| gps_get_position(); | |
| Serial.print(sats); | |
| Serial.print(" "); | |
| Serial.println(GPSerror); | |
| n=sprintf(data_temp, "%c%cL%ld,%ld,%ldV%dX%d,%d,%d[%s]", num_repeats, data_count, lat, lon, alt, battV, rx_packets, navmode, sats, id); | |
| //Reset Radio module | |
| digitalWrite(HIGH, reset_pin); | |
| delay(100); | |
| digitalWrite(LOW, reset_pin); | |
| delay(100); | |
| setupRFM69(); | |
| delay(2000); | |
| } | |
| Serial.print(data_temp); Serial.print (" "); Serial.println(n); | |
| return n; | |
| } | |
| void setup() | |
| { | |
| pinMode(reset_pin, OUTPUT); | |
| digitalWrite(reset_pin, LOW); | |
| Serial.begin(9600); | |
| randomSeed(analogRead(6)); | |
| timer_setup(); | |
| delay(1000); | |
| setupGPS(); | |
| setupRFM69(); | |
| delay(1000); | |
| packet_len = gen_Data(); | |
| //Serial.print("tx: "); Serial.println((char*)data); | |
| send_data(); | |
| } | |
| void loop() | |
| { | |
| delay(1000); | |
| Serial.println(rf69.spiRead(RFM69_REG_07_FRF_MSB)); | |
| if (f_timer == 2){ | |
| detected_data(); | |
| f_timer = 0; | |
| } | |
| if (total_count > 10){ | |
| transmit_time(); | |
| total_count = 0; | |
| } | |
| else{ | |
| total_count++; | |
| } | |
| } | |
| void transmit_time() | |
| { | |
| //**** Packet Tx Count ****** | |
| //using a byte to keep track of transmit count | |
| // its meant to roll over | |
| data_count++; | |
| //'a' packet is only sent on the first transmission so we need to move it along | |
| // when we roll over. | |
| // 98 = 'b' up to 122 = 'z' | |
| if(data_count > 122){ | |
| data_count = 98; //'b' | |
| } | |
| packet_len = gen_Data(); | |
| send_data(); | |
| } | |
| void send_data(){ | |
| rf69.send((byte*)data_temp, packet_len); | |
| delay(delay_length); | |
| Serial.println("Transmitted"); | |
| } | |
| void detected_data() | |
| { | |
| // Listen for data | |
| uint8_t buf_radio[64]; | |
| uint8_t len = sizeof(buf_radio); | |
| rf69.recv(buf_radio, &len); | |
| // Need to take the recieved buffer and decode it and add a reference | |
| for (int i=0; i<len; i++) { | |
| if (buf_radio[i] == ']') { | |
| //Print the string | |
| buf_radio[i+1] = '\0'; | |
| Serial.print("rx: "); Serial.println((char*)buf_radio); | |
| rx_packets++; | |
| if (buf_radio[0] > '0'){ | |
| //Reduce the repeat value | |
| buf_radio[0] = buf_radio[0] - 1; | |
| //Now add , and end line and let string functions do the rest | |
| buf_radio[i] = ','; | |
| buf_radio[i+1] = '\0'; | |
| strcpy(data_temp, (char*)buf_radio); //first copy buf to data (bytes to char) | |
| if(strstr(data_temp, id) == 0){ | |
| strcat(data_temp, id); //add ID | |
| strcat(data_temp, string_end); //add ending | |
| packet_len = strlen(data_temp); | |
| //random delay to try and avoid packet collision | |
| delay(random(50, 500)); | |
| //Serial.print("Repeat data: "); Serial.println((char*)data_temp); | |
| send_data(); | |
| break; | |
| } | |
| else { | |
| break; | |
| } | |
| } | |
| else { | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| //************Other Functions***************** | |
| /** | |
| * Verify that the uBlox 6 GPS receiver is set to the <1g airborne | |
| * navigaion mode. | |
| */ | |
| uint8_t gps_check_nav(void) | |
| { | |
| GPSerror = 0; | |
| Serial.flush(); | |
| uint8_t request[8] = {0xB5, 0x62, 0x06, 0x24, 0x00, 0x00, | |
| 0x2A, 0x84}; | |
| sendUBX(request, 8); | |
| // Get the message back from the GPS | |
| gps_get_data(); | |
| // Verify sync and header bytes | |
| if( buf[0] != 0xB5 || buf[1] != 0x62 ){ | |
| GPSerror = 41; | |
| } | |
| if( buf[2] != 0x06 || buf[3] != 0x24 ){ | |
| GPSerror = 42; | |
| } | |
| // Check 40 bytes of message checksum | |
| if( !_gps_verify_checksum(&buf[2], 40) ) { | |
| GPSerror = 43; | |
| } | |
| // Return the navigation mode and let the caller analyse it | |
| navmode = buf[8]; | |
| } | |
| // Send a byte array of UBX protocol to the GPS | |
| void sendUBX(uint8_t *MSG, uint8_t len) { | |
| for(int i=0; i<len; i++) { | |
| Serial.write(MSG[i]); | |
| } | |
| //Serial.println(); | |
| } | |
| /** | |
| * Calculate a UBX checksum using 8-bit Fletcher (RFC1145) | |
| */ | |
| void gps_ubx_checksum(uint8_t* data, uint8_t len, uint8_t* cka, | |
| uint8_t* ckb) | |
| { | |
| *cka = 0; | |
| *ckb = 0; | |
| for( uint8_t i = 0; i < len; i++ ) | |
| { | |
| *cka += *data; | |
| *ckb += *cka; | |
| data++; | |
| } | |
| } | |
| /** | |
| * Verify the checksum for the given data and length. | |
| */ | |
| bool _gps_verify_checksum(uint8_t* data, uint8_t len) | |
| { | |
| uint8_t a, b; | |
| gps_ubx_checksum(data, len, &a, &b); | |
| if( a != *(data + len) || b != *(data + len + 1)) | |
| return false; | |
| else | |
| return true; | |
| } | |
| void timer_setup(){ | |
| // initialize Timer1 | |
| cli(); // disable global interrupts | |
| TCCR1A = 0; // set entire TCCR1A register to 0 | |
| TCCR1B = 0; // same for TCCR1B | |
| // set compare match register to desired timer count: | |
| OCR1A = 15624; | |
| // turn on CTC mode: | |
| TCCR1B |= (1 << WGM12); | |
| // Set CS10 and CS12 bits for 1024 prescaler: | |
| TCCR1B |= (1 << CS10); | |
| TCCR1B |= (1 << CS12); | |
| // enable timer compare interrupt: | |
| TIMSK1 |= (1 << OCIE1A); | |
| sei(); // enable global interrupts | |
| } | |
| void gps_get_data() | |
| { | |
| f_timer = 0; | |
| int i = 0; | |
| while (f_timer == 0) { | |
| // Make sure data is available to read | |
| if (Serial.available()) { | |
| buf[i] = Serial.read(); | |
| i++; | |
| } | |
| } | |
| f_timer = 0; | |
| } | |
| /** | |
| * Poll the GPS for a position message then extract the useful | |
| * information from it - POSLLH. | |
| */ | |
| void gps_get_position() | |
| { | |
| GPSerror = 0; | |
| Serial.flush(); | |
| // Request a NAV-POSLLH message from the GPS | |
| uint8_t request[8] = {0xB5, 0x62, 0x01, 0x02, 0x00, 0x00, 0x03, | |
| 0x0A}; | |
| sendUBX(request, 8); | |
| // Get the message back from the GPS | |
| gps_get_data(); | |
| // Verify the sync and header bits | |
| if( buf[0] != 0xB5 || buf[1] != 0x62 ) | |
| GPSerror = 21; | |
| if( buf[2] != 0x01 || buf[3] != 0x02 ) | |
| GPSerror = 22; | |
| if( !_gps_verify_checksum(&buf[2], 32) ) { | |
| GPSerror = 23; | |
| } | |
| if(GPSerror == 0) { | |
| // 4 bytes of longitude (1e-7) | |
| lon = (int32_t)buf[10] | (int32_t)buf[11] << 8 | | |
| (int32_t)buf[12] << 16 | (int32_t)buf[13] << 24; | |
| lon /= 1000; | |
| // 4 bytes of latitude (1e-7) | |
| lat = (int32_t)buf[14] | (int32_t)buf[15] << 8 | | |
| (int32_t)buf[16] << 16 | (int32_t)buf[17] << 24; | |
| lat /= 1000; | |
| // 4 bytes of altitude above MSL (mm) | |
| alt = (int32_t)buf[22] | (int32_t)buf[23] << 8 | | |
| (int32_t)buf[24] << 16 | (int32_t)buf[25] << 24; | |
| alt /= 1000; | |
| } | |
| } | |
| /** | |
| * Check the navigation status to determine the quality of the | |
| * fix currently held by the receiver with a NAV-STATUS message. | |
| */ | |
| void gps_check_lock() | |
| { | |
| GPSerror = 0; | |
| Serial.flush(); | |
| // Construct the request to the GPS | |
| uint8_t request[8] = {0xB5, 0x62, 0x01, 0x06, 0x00, 0x00, | |
| 0x07, 0x16}; | |
| sendUBX(request, 8); | |
| // Get the message back from the GPS | |
| gps_get_data(); | |
| // Verify the sync and header bits | |
| if( buf[0] != 0xB5 || buf[1] != 0x62 ) { | |
| GPSerror = 11; | |
| } | |
| if( buf[2] != 0x01 || buf[3] != 0x06 ) { | |
| GPSerror = 12; | |
| } | |
| // Check 60 bytes minus SYNC and CHECKSUM (4 bytes) | |
| if( !_gps_verify_checksum(&buf[2], 56) ) { | |
| GPSerror = 13; | |
| } | |
| if(GPSerror == 0){ | |
| // Return the value if GPSfixOK is set in 'flags' | |
| if( buf[17] & 0x01 ) | |
| lock = buf[16]; | |
| else | |
| lock = 0; | |
| sats = buf[53]; | |
| } | |
| else { | |
| lock = 0; | |
| } | |
| } | |
| void setupGPS() { | |
| //Turning off all GPS NMEA strings apart on the uBlox module | |
| // Taken from Project Swift (rather than the old way of sending ascii text) | |
| uint8_t setNMEAoff[] = {0xB5, 0x62, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00, 0x80, 0x25, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xA9}; | |
| sendUBX(setNMEAoff, sizeof(setNMEAoff)/sizeof(uint8_t)); | |
| delay(500); | |
| // Check and set the navigation mode (Airborne, 1G) | |
| 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}; | |
| sendUBX(setNav, sizeof(setNav)/sizeof(uint8_t)); | |
| delay(500); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment