picoballoon/V1_0.ino

380 wiersze
10 KiB
C++

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/crc16.h>
#include <SPI.h>
#include <RFM22.h>
#include <TinyGPS.h>
#define ASCII 7 // ASCII 7 or 8
#define STOPBITS 2 // Either 1 or 2
#define TXDELAY 0 // Delay between sentence TX's
#define RTTY_BAUD 50 // Baud rate for use with RFM22B Max = 600
#define RADIO_FREQUENCY 434.160
#define RFM22B_SDN 8
#define RFM22B_PIN 10
#define LED 7
char callsign[] = "KOMO-1"; //Callsign
char datastring[80];
char txstring[80];
volatile int txstatus = 1;
volatile int txstringlength = 0;
volatile char txc;
volatile int txi;
volatile int txj;
unsigned int count = 0;
TinyGPS gps;
rfm22 radio1(RFM22B_PIN);
const int analogInPin = A0;
int sensorValue = 0;
float actualValue = 0.00;
float divider = 1.8;
char voltage[6];
int v1;
int v2;
float flat, flon = 0;
unsigned long age;
char latbuf[12] = "0", lonbuf[12] = "0", altbuf[12] = "0";
int hour = 0, minute = 0, second = 0, oldsecond = 0, sats = 0;
unsigned long date, time;
long int ialt = 123;
long int ticks = 1;
byte gps_set_sucess = 0;
bool newData = false;
bool cBusy = true;
bool reinit = false;
bool reinit_done = false;
int reinitcnt = 8;
int reinitcntr = 0;
void setup()
{
pinMode(LED, OUTPUT);
digitalWrite(LED, LOW);
delay(500);
digitalWrite(LED, HIGH);
delay(500);
initialise_interrupt();
Serial.begin(9600);
//Setup GPS
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
};
uint8_t ecoMode[] = { 0xB5, 0x62, 0x06, 0x11, 0x02, 0x00, 0x00, 0x04, 0x1D, 0x85 };
while (!gps_set_sucess) {
sendUBX(setNav, sizeof(setNav) / sizeof(uint8_t));
gps_set_sucess = getUBX_ACK(setNav);
/*sendUBX(ecoMode, sizeof(ecoMode)/sizeof(uint8_t));
gps_set_sucess&=getUBX_ACK(ecoMode); */
}
gps_set_sucess = 0;
digitalWrite(LED, LOW);
delay(500);
digitalWrite(LED, HIGH);
delay(500);
digitalWrite(LED, LOW);
delay(500);
digitalWrite(LED, HIGH);
delay(500);
digitalWrite(LED, LOW);
while (sats < 6 && flat == 0) {
while (Serial.available()) {
char c = Serial.read();
newData = gps.encode(c);
if (newData) {
gps.f_get_position(&flat, &flon, &age);
sats = gps.satellites();
}
}
}
setupGPSpower();
delay(1000);
//Setup RFM22B
setupRadio();
}
void loop()
{
unsigned long chars;
unsigned short sentences, failed;
while (Serial.available()) {
char c = Serial.read();
if (gps.encode(c)) // Did a new valid sentence come in?
newData = true;
}
if (newData) {
digitalWrite(LED, HIGH);
gps.f_get_position(&flat, &flon, &age);
sats = gps.satellites();
dtostrf(flat, 10, 6, latbuf);
dtostrf(flon, 9, 6, lonbuf);
if (lonbuf[0] == ' ') {
lonbuf[0] = '+';
}
if (latbuf[0] == ' ') {
latbuf[0] = '+';
}
ialt = (gps.altitude() / 100);
if (ialt >= 0) {
itoa(ialt, altbuf, 10);
}
gps.get_datetime(&date, &time, &age);
hour = (time / 1000000);
minute = ((time - (hour * 1000000)) / 10000);
second = ((time - ((hour * 1000000) + (minute * 10000))));
second = second / 100;
sensorValue = analogRead(analogInPin);
actualValue = (sensorValue / 1023.00) * divider;
v1 = actualValue;
v2 = (actualValue - v1) * 100;
snprintf(voltage, sizeof(voltage), "%i.%02i", v1, v2);
cBusy = true;
sprintf(datastring, "$$$$%s,%li,%02i:%02i:%02i,%s,%s,%s,%i,%s",
callsign, ticks, hour, minute, second, latbuf, lonbuf, altbuf, sats, voltage);
unsigned int CHECKSUM = gps_CRC16_checksum(datastring); // Calculates the checksum for this datastring
char checksum_str[7];
sprintf(checksum_str, "*%04X\n", CHECKSUM);
strcat(datastring, checksum_str);
cBusy = false;
gps.stats(&chars, &sentences, &failed);
} else {
digitalWrite(LED, LOW);
}
if (reinit == true) {
setupRadio();
reinit = false;
}
}
ISR(TIMER1_COMPA_vect)
{
switch (txstatus) {
case 0: // This is the optional delay between transmissions.
txj++;
if (txj > (TXDELAY * RTTY_BAUD)) {
txj = 0;
txstatus = 1;
}
break;
case 1: // Initialise transmission, take a copy of the string so it doesn't change mid transmission.
if (reinit == false) {
if (cBusy == false) {
if (reinitcntr == reinitcnt) {
reinit = true;
reinitcntr = 0;
} else {
strcpy(txstring, datastring);
txstringlength = strlen(txstring);
if (txstringlength != 0)
txj = 0;
ticks++;
txstatus = 2;
reinitcntr++;
}
}
}
break;
case 2: // Grab a char and lets go transmit it.
if (txj < txstringlength) {
txc = txstring[txj];
txj++;
txstatus = 3;
rtty_txbit(0); // Start Bit;
txi = 0;
} else {
txstatus = 0; // Should be finished
txj = 0;
}
break;
case 3:
if (txi < ASCII) {
txi++;
if (txc & 1)
rtty_txbit(1);
else
rtty_txbit(0);
txc = txc >> 1;
break;
} else {
rtty_txbit(1); // Stop Bit
txstatus = 4;
txi = 0;
break;
}
case 4:
if (STOPBITS == 2) {
rtty_txbit(1); // Stop Bit
txstatus = 2;
break;
} else {
txstatus = 2;
break;
}
}
}
void rtty_txbit(int bit)
{
if (bit) {
radio1.write(0x73, 0x03); // High
} else {
radio1.write(0x73, 0x00); // Low
}
}
void setupGPSpower()
{
//Set GPS ot Power Save Mode
uint8_t setPSM[] = { 0xB5, 0x62, 0x06, 0x11, 0x02, 0x00, 0x08, 0x01, 0x22, 0x92 }; // Setup for Power Save Mode (Default Cyclic 1s)
sendUBX(setPSM, sizeof(setPSM) / sizeof(uint8_t));
}
void setupRadio()
{
pinMode(RFM22B_SDN, OUTPUT); // RFM22B SDN is on ARDUINO A3
digitalWrite(RFM22B_SDN, LOW);
delay(1000);
rfm22::initSPI();
radio1.init();
radio1.write(0x71, 0x00); // unmodulated carrier
//This sets up the GPIOs to automatically switch the antenna depending on Tx or Rx state, only needs to be done at start up
radio1.write(0x0b, 0x12);
radio1.write(0x0c, 0x15);
radio1.setFrequency(RADIO_FREQUENCY);
radio1.write(0x6D, 0x04); // turn tx low power 11db
radio1.write(0x07, 0x08);
delay(500);
}
uint16_t gps_CRC16_checksum(char *string)
{
size_t i;
uint16_t crc;
uint8_t c;
crc = 0xFFFF;
// Calculate checksum ignoring the first four $s
for (i = 4; i < strlen(string); i++) {
c = string[i];
crc = _crc_xmodem_update(crc, c);
}
return crc;
}
void initialise_interrupt()
{
// initialize Timer1
cli(); // disable global interrupts
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
OCR1A = F_CPU / 1024 / RTTY_BAUD - 1; // set compare match register to desired timer count:
TCCR1B |= (1 << WGM12); // turn on CTC mode:
// Set CS10 and CS12 bits for:
TCCR1B |= (1 << CS10);
TCCR1B |= (1 << CS12);
// enable timer compare interrupt:
TIMSK1 |= (1 << OCIE1A);
sei(); // enable global interrupts
}
// 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]);
//mySerial.print(MSG[i], HEX);
}
Serial.println();
}
// Calculate expected UBX ACK packet and parse UBX response from GPS
boolean getUBX_ACK(uint8_t * MSG)
{
uint8_t b;
uint8_t ackByteID = 0;
uint8_t ackPacket[10];
unsigned long startTime = millis();
//mySerial.print(" * Reading ACK response: ");
// Construct the expected ACK packet
ackPacket[0] = 0xB5; // header
ackPacket[1] = 0x62; // header
ackPacket[2] = 0x05; // class
ackPacket[3] = 0x01; // id
ackPacket[4] = 0x02; // length
ackPacket[5] = 0x00;
ackPacket[6] = MSG[2]; // ACK class
ackPacket[7] = MSG[3]; // ACK id
ackPacket[8] = 0; // CK_A
ackPacket[9] = 0; // CK_B
// Calculate the checksums
for (uint8_t i = 2; i < 8; i++) {
ackPacket[8] = ackPacket[8] + ackPacket[i];
ackPacket[9] = ackPacket[9] + ackPacket[8];
}
while (1) {
// Test for success
if (ackByteID > 9) {
// All packets in order!
//mySerial.println(" (SUCCESS!)");
return true;
}
// Timeout if no valid response in 3 seconds
if (millis() - startTime > 3000) {
//mySerial.println(" (FAILED!)");
return false;
}
// Make sure data is available to read
if (Serial.available()) {
b = Serial.read();
// Check that bytes arrive in sequence as per expected ACK packet
if (b == ackPacket[ackByteID]) {
ackByteID++;
//mySerial.print(b, HEX);
} else {
ackByteID = 0; // Reset and look again, invalid order
}
}
}
}