ground-dashboard/receiver/receiver.ino
2022-12-05 22:25:36 +00:00

328 lines
12 KiB
C++

#include <SPI.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <base64.hpp>
#define BIT(n) (1 << (n))
#define BIT_FLIGHT_TIME BIT(0)
#define BIT_ALTITUDE_100 BIT(1)
#define BIT_ALTITUDE BIT(2)
#define BIT_VELOCITY BIT(3)
#define BIT_ACCELERATION BIT(4)
#define BIT_FLIGHT_PHASE BIT(5)
#define BIT_CHANNEL BIT(6)
#define BIT_TEMPERATURE_10 BIT(7)
#define BIT_NAME BIT(8)
#define BIT_BATERY_10 BIT(9)
#define BIT_APOGEE BIT(10)
#define BIT_MAX_VELOCITY BIT(11)
#define BIT_MAX_ACCELERATION BIT(12)
#define ALL_ELEMENTS 0b1111111111111
#define TRIGGER_FLIGHT_TIME '#'
#define TRIGGER_ALTITUDE_100 '{'
#define TRIGGER_ALTITUDE '<'
#define TRIGGER_VELOCITY '('
#define TRIGGER_ACCELERATION_10 '\\'
#define TRIGGER_FLIGHT_PHASE '@'
#define TRIGGER_CHANNEL '~'
#define TRIGGER_TEMPERATURE_10 '!'
#define TRIGGER_NAME '='
#define TRIGGER_BATERY_10 '?'
#define TRIGGER_APOGEE '%'
#define TRIGGER_MAX_VELOCITY '^'
#define TRIGGER_MAX_ACCELERATION '['
#define TRIGGER_END_PACKET '>'
#define MAX_DATA_SIZE 100
typedef struct WhiteVestsPacket{
double flight_time;
double flight_phase;
double altitude;
double velocity;
double acceleration;
double temperature;
double latitude;
double longitude;
double gpsSignal; // GPS Signal Strength
double gpsstat; // GPS Status
}WhiteVestsPacket;
typedef struct EggtimerElement{
char type;
char data[MAX_DATA_SIZE];
} EggtimerElement;
/**
* @brief This function is used to parse the data received from the Eggtimer
* @param byte_received byte received
* @param packet package to store the data
*
* @return true if receive new data packet, false otherwise
*/
bool decode_eggtimer_data(char byte_received, EggtimerElement * packet){
static size_t counter = 0;
static char last_state = 0;
switch (byte_received)
{
case TRIGGER_FLIGHT_TIME:
case TRIGGER_ALTITUDE_100:
case TRIGGER_ALTITUDE:
case TRIGGER_VELOCITY:
case TRIGGER_ACCELERATION_10:
case TRIGGER_FLIGHT_PHASE:
case TRIGGER_CHANNEL:
case TRIGGER_TEMPERATURE_10:
case TRIGGER_NAME:
case TRIGGER_BATERY_10:
case TRIGGER_APOGEE:
case TRIGGER_MAX_VELOCITY:
case TRIGGER_MAX_ACCELERATION:
last_state = byte_received;
packet->type = byte_received;
memset(&packet->data, 0, sizeof packet->data);
counter = 0;
break;
case TRIGGER_END_PACKET:
if (last_state){
packet->type = last_state;
packet->data[counter] = '\0';
last_state = 0;
return true;
}
last_state = 0;
break;
default:
// save data
if(last_state)
packet->data[counter++] = byte_received;
break;
}
return false;
}
bool buildPacket(EggtimerElement *eggt_packet, WhiteVestsPacket *wv_packet){
static int16_t elements_received = 0;
static WhiteVestsPacket nextPacket; // packet used when the packet is sent but it is not complete
static bool sentIncomplete = false;
if(sentIncomplete){
memcpy(wv_packet, &nextPacket, sizeof(WhiteVestsPacket));
sentIncomplete = false;
}
int16_t element_bit = NULL;
double *element_pointer = NULL;
double *nextElement_pointer = NULL;
// convert data to double
bool converted = true;
char *endptr;
double element = strtol((char*)&eggt_packet->data, &endptr, 10);
if(endptr == (char*)&eggt_packet->data|| *endptr != NULL){ // error
converted = false;
element = 0;
}
switch (eggt_packet->type){
case TRIGGER_FLIGHT_TIME:
element_bit = BIT_FLIGHT_TIME;
element_pointer = &wv_packet->flight_time;
nextElement_pointer = &nextPacket.flight_time;
break;
case TRIGGER_ALTITUDE_100:
element_bit = BIT_ALTITUDE_100;
element_pointer = &wv_packet->altitude;
nextElement_pointer = &nextPacket.altitude;
element *= 100.0;
break;
case TRIGGER_ALTITUDE:
element_bit = BIT_ALTITUDE;
element_pointer = &wv_packet->altitude;
nextElement_pointer = &nextPacket.altitude;
break;
case TRIGGER_VELOCITY:
element_bit = BIT_VELOCITY;
element_pointer = &wv_packet->velocity;
nextElement_pointer = &nextPacket.velocity;
break;
case TRIGGER_ACCELERATION_10:
element_bit = BIT_ACCELERATION;
element_pointer = &wv_packet->acceleration;
nextElement_pointer = &nextPacket.acceleration;
element /= 10;
break;
case TRIGGER_FLIGHT_PHASE:
element_bit = BIT_FLIGHT_PHASE;
element_pointer = &wv_packet->flight_phase;
nextElement_pointer = &nextPacket.flight_phase;
break;
case TRIGGER_TEMPERATURE_10:
element_bit = BIT_TEMPERATURE_10;
element_pointer = &wv_packet->temperature;
nextElement_pointer = &nextPacket.temperature;
element /= 10;
break;
case TRIGGER_BATERY_10:
element_bit = BIT_BATERY_10;
break;
case TRIGGER_APOGEE:
element_bit = BIT_APOGEE;
break;
case TRIGGER_MAX_VELOCITY:
element_bit = BIT_MAX_VELOCITY;
break;
case TRIGGER_MAX_ACCELERATION:
element_bit = BIT_MAX_ACCELERATION;
break;
case TRIGGER_CHANNEL:
element_bit = BIT_CHANNEL;
break;
case TRIGGER_NAME:
element_bit = BIT_NAME;
break;
default:
break;
}
// check if the data received is from current packet, if not, it will be stored in the next packet
if((elements_received & element_bit) && element_pointer != NULL && nextElement_pointer != NULL && converted){
memcpy(&nextPacket, wv_packet, sizeof(WhiteVestsPacket));
*nextElement_pointer = element; // stores element in nextPacket
elements_received = element_bit;
sentIncomplete = true;
return true;
}
if( element_pointer != NULL){
if(converted)
*element_pointer = element;
else
return false; // error converting data
}
elements_received |= element_bit;
// check if the packet is complete
if(elements_received == ALL_ELEMENTS){
elements_received = 0;
return true;
}
return false;
}
/**
* @brief This function send the packet to the server
*/
void sendPacket(WhiteVestsPacket * packet){
unsigned char data_base64[255];
encode_base64((unsigned char*)packet, sizeof(WhiteVestsPacket), data_base64);
char rssi_base64[] = "//8=";
Serial.print("T,");
Serial.print((char *) data_base64);
Serial.print(",");
Serial.print((char *) rssi_base64);
Serial.print("\n");
}
bool test(){
char str[] ="{000>@1>#0000>~AB---->?079>!211>=KM6ZFL>"
"{000>@1>#0000>~AB---->?079>!211>=KM6ZFL>"
"{040>@2>#0010>~AB---->(0213>\\-001>?079>!212>=KM6ZFL>01>?079>!212>=KM6ZFL>"
"{045>@2>#0014>~AB---->(0072>\\-001>?079>!212>=KM6ZFL>"
"{046>@4>#0016>~AB---->(0033>\\-001>?079>!212>=KM6ZFL>"
"{046>@5>#0018>~1B---->(2787>\\000>%04679>^0660>[025>?079>!210>=KM6ZFL> "
"{045>@5>#0020>~1B---->(-0354>\\0=KM6ZFL>"
"{043>@5>#0022>~1B---->(-0029>\\004>%04679>^0660>[025>?079>!209>=KM6ZFL>"
"{043>@5>#0024>~1B---->(-0023>\\000>%04679>^0660>[025>?079>!209>=KM6ZFL>"
"{042>@5>#0026>~1B---->(-0028>\\000>%04679>^0660>[025>?079>!209>=KM6ZFL>"
"{042>@5>#0028>~1B---->(-0035>\\000>%04679>^0660>[025>?079>!209>=KM6ZFL>"
"{041>@5>#0030>~1B---->(-0029>\\000>%04679>^0660>[025>?079>!209>=KM6ZFL>"
"{040>@5>#0032>~1B---->(-0032>\\000>%04679>^0660>[025>?079>!209>=KM6ZFL>"
"{040>@5>#0034>~1B---->(-0021>\\000>%04679>^0660>[025>?079>!209>=KM6ZFL>"
"{039>@5>#0036>~1B---->(-0028>\\000>%04679>^0660>[025>?079>!209>=KM6ZFL>"
"{038>@5>#0038>~1B---->(-0028>\\000>%04679>^0660>[025>?079>!209>=KM6ZFL>"
"{038>@5>#0040>~1B---->(-0031>\\000>%04679>^0660>[025>?079>!209>=KM6ZFL>"
"{037>@5>#0042>~1B---->(-0043>\\000>%04679>^0660>[025>?079>!208>=KM6ZFL>"
"{037>@5>#0044>~1B---->(-0040>\\000>%04679>^0660>[025>?079>!209>=KM6ZFL>"
"{036>@5>#0046>~1B---->(-0022>\\000>%04679>^0660>[025>?079>!208>=KM6ZFL>"
"{035>@5>#0048>~1B---->(-0038>\\000>%04679>^0660>[025>?079>!208>=KM6ZFL>"
"{034>@5>#0050>~1B---->(-0028>\\000>%04679>^0660>[025>?079>!208>=KM6ZFL>"
"{034>@5>#0052>~1B---->(-0043>\\0=KM6ZFL>"
"{033>@5>#0054>~1B---->(-0033>\\000>%04679>^0660>[025>?079>!208>=KM6ZFL>"
"{032>@5>#0056>~1B---->(-0041>\\000>%04679>^0660>[025>?079>!208>=KM6ZFL>"
"{032>@5>#0058>~1B---->(-0026>\\000>%04679>^0660>[025>?079>!207>=KM6ZFL>"
"{031>@5>#0060>~1B---->(-0035>\\000>%04679>^0660>[025>?079>!208>=KM6ZFL>"
"{030>@5>#0062>~1B---->(-0037>\\000>%04679>^0660>[025>?079>!207>=KM6ZFL>"
"{029>@5>#0064>~1B---->(-0032>\\000>%04679>^0660>[025>?079>!207>=KM6ZFL>"
"{029>@5>#0066>~1B---->(-0035>\\000>%04679>^0660>[025>?079>!207>=KM6ZFL>"
"{028>@5>#0068>~1B---->(-0044>\\000>%04679>^0660>[025>?079>!207>=KM6ZFL>"
"{027>@5>#0070>~1B---->(-0045>\\000>%04679>^0660>[025>?079>!207>=KM6ZFL>"
"{026>@5>#0072>~1B---->(-0035>\\000>%04679>^0660>[025>?079>!206>=KM6ZFL>"
"{025>@5>#0074>~1B---->(-0045>\\000>%04679>^0660>[025>?079>!206>=KM6ZFL>"
"{025>@5>#0076>~1B---->(-0043>\\000>%04679>^0660>[025>?079>!206>=KM6ZFL>"
"{024>@5>#0078>~1B---->(-0046>\\000>%04679>^0660>[025>?079>!206>=KM6ZFL>"
"{023>@5>#0080>~1B---->(-0046>\\000>%04679>^0660>[025>?079>!206>=KM6ZFL>"
"{022>@5>#0082>~1B---->(-0037>\\000>%04679>^0660>[025>?079>!206>=KM6ZFL>"
"{021>@5>#0084>~1B---->(-0039>\\000>%04679>^0660>[025>?079>!205>=KM6ZFL>"
"{020>@5>#0086>~1B---->(-0043>\\000>%04679>^0660>[025>?079>!205>=KM6ZFL>"
"{020>@5>#0088>~1B---->(-0046>\\000>%04679>^0660>[025>?079>!205>=KM6ZFL>"
"{019>@5>#0090>~1B---->(-0037>\\000>%04679>^0660>[025>?079>!205>=KM6ZFL>"
"{018>@5>#0092>~1B---->(-0046>\\000>%04679>^0660>[025>?079>!205>=KM6ZFL>"
"{017>@5>#0094>~1B---->(-0035>\\000>%04679>^0660>[025>?079>!205>=KM6ZFL>"
"{016>@5>#0096>~1B---->(-0046>\\000>%04679>^0660>[025>?079>!205>=KM6ZFL>"
"{016>@5>#0098>~1B---->(-0032>\\000>%04679>^0660>[025>?079>!204>=KM6ZFL>"
"{015>@5>#0100>~1B---->(-0043>\\000>%04679>^0660>[025>?079>!204>=KM6ZFL>"
"{014>@5>#0102>~1B---->(-0037>\\000>%04679>^0660>[025>?079>!204>=KM6ZFL>"
"{013>@5>#0104>~1B---->(-0033>\\000>%04679>^0660>[025>?079>!204>=KM6ZFL>"
"{012>@5>#0106>~1B---->(-0026>\\000>%04679>^0660>[025>?079>!204>=KM6ZFL>"
"{012>@5>#0108>~1B---->(-0029>\\000>%04679>^0660>[025>?079>!204>=KM6ZFL>"
"{011>@5>#0110>~1B---->(-0042>\\000>%04679>^0660>[025>?079>!203>=KM6ZFL>"
"{010>@5>#0112>~1B---->(-0041>\\000>%04679>^0660>[025>?079>!203>=KM6ZFL>"
"{009>@5>#0114>~1B---->(-0037>\\000>%04679>^0660>[025>?079>!203>=KM6ZFL>"
"{009>@5>#0116>~1B---->(-0040>\\000>%04679>^0660>[025>?079>!203>=KM6ZFL>"
"{008>@5>#0118>~1B---->(-0043>\\000>%04679>^0660>[025>?079>!202>=KM6ZFL>";
char *p = str;
EggtimerElement eggt_packet;
WhiteVestsPacket wv_packet;
memset(&wv_packet, 0, sizeof(WhiteVestsPacket));
for (; *p; p++){
if(decode_eggtimer_data(*p, &eggt_packet) && buildPacket(&eggt_packet, &wv_packet)){
sendPacket(&wv_packet);
delay(2000);
}
}
return true;
}
void setup() {
Serial.begin(9600);
Serial.flush();
while (!Serial);
Serial.println();
test();
}
void loop() {
}