├── .gitignore ├── README.md ├── examples ├── CommandResponse │ └── CommandResponse.ino ├── GetPacketInfo │ └── GetPacketInfo.ino ├── GetSystemInfo │ └── GetSystemInfo.ino ├── PingPong │ └── PingPong.ino ├── RepeatMessage │ └── RepeatMessage.ino └── RepeatMessageCustom │ └── RepeatMessageCustom.ino ├── keywords.txt ├── library.properties └── src ├── FOSSA-Comms.cpp └── FOSSA-Comms.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Arduino Library Development file 2 | .development 3 | 4 | # Atom 5 | *.tags 6 | *.tags1 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FOSSA-Comms 2 | 3 | This library is intended to be used with the FOSSASAT-1 Satellite developed by Fossa Systems. 4 | 5 | ## IMPORTANT! 6 | 7 | Before any attempt of communication with FOSSASAT-1, please consult [the Guide document](https://github.com/FOSSASystems/FOSSASAT-1/blob/master/FOSSASAT-1%20Comms%20Guide.pdf) in the FOSSASAT-1 repository and [the current status of FOSSASAT-1.](http://groundstationdatabase.com/index.php) 8 | 9 | ## Reference implementation of FOSSASAT Communication Protocol (FCP) 10 | 11 | For details and specification of FCP, please see [the Guide document](https://github.com/FOSSASystems/FOSSASAT-1/blob/master/FOSSASAT-1%20Comms%20Guide.pdf) in FOSSASAT-1 repository. 12 | 13 | For released versions, see https://github.com/FOSSASystems/FOSSA-Comms/releases 14 | 15 | ## Current Dependencies 16 | * tiny-AES-c: https://github.com/FOSSASystems/tiny-AES-c 17 | 18 | ## Legacy Dependencies 19 | * FOSSA-Comms 1.0.0 20 | * AESLib: https://github.com/DavyLandman/AESLib 21 | -------------------------------------------------------------------------------- /examples/CommandResponse/CommandResponse.ino: -------------------------------------------------------------------------------- 1 | // include the libraries 2 | #include 3 | #include 4 | 5 | // SX1262 has the following connections: 6 | // NSS pin: 10 7 | // DIO1 pin: 2 8 | // BUSY pin: 9 9 | SX1262 radio = new Module(10, 2, 9); 10 | 11 | // satellite callsign 12 | char callsign[] = "FOSSASAT-1"; 13 | 14 | // last transmission timestamp 15 | uint32_t lastTransmit = 0; 16 | 17 | // transmission period in ms 18 | const uint32_t transmitPeriod = 4000; 19 | 20 | // interrupt flags 21 | volatile bool receivedFlag = false; 22 | volatile bool enableInterrupt = true; 23 | 24 | // interrupt service routine for data reception 25 | void setFlag(void) { 26 | if(!enableInterrupt) { 27 | return; 28 | } 29 | 30 | receivedFlag = true; 31 | } 32 | 33 | void setup() { 34 | Serial.begin(9600); 35 | 36 | // initialize SX1262 37 | Serial.print(F("Initializing ... ")); 38 | int state = radio.begin(436.7, 125.0, 11, 8, 0x0F0F); 39 | if (state == ERR_NONE) { 40 | Serial.println(F("success!")); 41 | } else { 42 | Serial.print(F("failed, code ")); 43 | Serial.println(state); 44 | while (true); 45 | } 46 | 47 | // set interrupt service routine 48 | radio.setDio1Action(setFlag); 49 | 50 | // start listening 51 | radio.startReceive(); 52 | } 53 | 54 | void loop() { 55 | // check if it's time to transmit 56 | if(millis() - lastTransmit >= transmitPeriod) { 57 | // disable reception interrupt 58 | enableInterrupt = false; 59 | detachInterrupt(digitalPinToInterrupt(2)); 60 | 61 | // save timestamp 62 | lastTransmit = millis(); 63 | 64 | Serial.println(F("Transmitting packet ... ")); 65 | 66 | // data to transmit 67 | uint8_t functionId = CMD_RETRANSMIT; 68 | char optData[] = "Hello there"; 69 | uint8_t optDataLen = strlen(optData); 70 | 71 | // build frame 72 | uint8_t len = FCP_Get_Frame_Length(callsign, optDataLen); 73 | uint8_t* frame = new uint8_t[len]; 74 | FCP_Encode(frame, callsign, functionId, optDataLen, (uint8_t*)optData); 75 | PRINT_BUFF(frame, len); 76 | 77 | // send data 78 | int state = radio.transmit(frame, len); 79 | delete[] frame; 80 | 81 | // check transmission success 82 | if (state == ERR_NONE) { 83 | Serial.println(F("Success!")); 84 | } 85 | 86 | // set radio mode to reception 87 | Serial.println(F("Waiting for response ... ")); 88 | radio.setDio1Action(setFlag); 89 | radio.startReceive(); 90 | enableInterrupt = true; 91 | } 92 | 93 | // check if new data were received 94 | if(receivedFlag) { 95 | // disable reception interrupt 96 | enableInterrupt = false; 97 | receivedFlag = false; 98 | 99 | // read received data 100 | size_t respLen = radio.getPacketLength(); 101 | uint8_t* respFrame = new uint8_t[respLen]; 102 | int state = radio.readData(respFrame, respLen); 103 | 104 | // check reception success 105 | if (state == ERR_NONE) { 106 | // print raw data 107 | Serial.print(F("Received ")); 108 | Serial.print(respLen); 109 | Serial.println(F(" bytes:")); 110 | PRINT_BUFF(respFrame, respLen); 111 | 112 | // get function ID 113 | uint8_t functionId = FCP_Get_FunctionID(callsign, respFrame, respLen); 114 | Serial.print(F("Function ID: 0x")); 115 | Serial.println(functionId, HEX); 116 | 117 | // check optional data 118 | uint8_t respOptDataLen = FCP_Get_OptData_Length(callsign, respFrame, respLen); 119 | if(respOptDataLen > 0) { 120 | // frame contains optional data 121 | uint8_t* respOptData = new uint8_t[respOptDataLen]; 122 | FCP_Get_OptData(callsign, respFrame, respLen, respOptData); 123 | 124 | // print optional data 125 | Serial.print(F("Optional data (")); 126 | Serial.print(respOptDataLen); 127 | Serial.println(F(" bytes):")); 128 | PRINT_BUFF(respOptData, respOptDataLen); 129 | delete[] respOptData; 130 | } 131 | 132 | } else { 133 | Serial.println(F("Reception failed, code ")); 134 | Serial.println(state); 135 | 136 | } 137 | 138 | // enable reception interrupt 139 | delete[] respFrame; 140 | enableInterrupt = true; 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /examples/GetPacketInfo/GetPacketInfo.ino: -------------------------------------------------------------------------------- 1 | // include the libraries 2 | #include 3 | #include 4 | 5 | // SX1262 has the following connections: 6 | // NSS pin: 10 7 | // DIO1 pin: 2 8 | // BUSY pin: 9 9 | SX1262 radio = new Module(10, 2, 9); 10 | 11 | // satellite callsign 12 | char callsign[] = "FOSSASAT-1"; 13 | 14 | // last transmission timestamp 15 | uint32_t lastTransmit = 0; 16 | 17 | // transmission period in ms 18 | const uint32_t transmitPeriod = 4000; 19 | 20 | // interrupt flags 21 | volatile bool receivedFlag = false; 22 | volatile bool enableInterrupt = true; 23 | 24 | // interrupt service routine for data reception 25 | void setFlag(void) { 26 | if(!enableInterrupt) { 27 | return; 28 | } 29 | 30 | receivedFlag = true; 31 | } 32 | 33 | void setup() { 34 | Serial.begin(9600); 35 | 36 | // initialize SX1262 37 | Serial.print(F("Initializing ... ")); 38 | int state = radio.begin(436.7, 125.0, 11, 8, 0x0F0F); 39 | if (state == ERR_NONE) { 40 | Serial.println(F("success!")); 41 | } else { 42 | Serial.print(F("failed, code ")); 43 | Serial.println(state); 44 | while (true); 45 | } 46 | 47 | // set interrupt service routine 48 | radio.setDio1Action(setFlag); 49 | 50 | // start listening 51 | radio.startReceive(); 52 | } 53 | 54 | void loop() { 55 | // check if it's time to transmit 56 | if(millis() - lastTransmit >= transmitPeriod) { 57 | // disable reception interrupt 58 | enableInterrupt = false; 59 | detachInterrupt(digitalPinToInterrupt(2)); 60 | 61 | // save timestamp 62 | lastTransmit = millis(); 63 | 64 | Serial.println(F("Transmitting packet ... ")); 65 | 66 | // data to transmit 67 | uint8_t functionId = CMD_GET_PACKET_INFO; 68 | 69 | // build frame 70 | uint8_t len = FCP_Get_Frame_Length(callsign); 71 | uint8_t* frame = new uint8_t[len]; 72 | FCP_Encode(frame, callsign, functionId); 73 | PRINT_BUFF(frame, len); 74 | 75 | // send data 76 | int state = radio.transmit(frame, len); 77 | delete[] frame; 78 | 79 | // check transmission success 80 | if (state == ERR_NONE) { 81 | Serial.println(F("Success!")); 82 | } 83 | 84 | // set radio mode to reception 85 | Serial.println(F("Waiting for response ... ")); 86 | radio.setDio1Action(setFlag); 87 | radio.startReceive(); 88 | enableInterrupt = true; 89 | } 90 | 91 | // check if new data were received 92 | if(receivedFlag) { 93 | // disable reception interrupt 94 | enableInterrupt = false; 95 | receivedFlag = false; 96 | 97 | // read received data 98 | size_t respLen = radio.getPacketLength(); 99 | uint8_t* respFrame = new uint8_t[respLen]; 100 | int state = radio.readData(respFrame, respLen); 101 | 102 | // check reception success 103 | if (state == ERR_NONE) { 104 | // print raw data 105 | Serial.print(F("Received ")); 106 | Serial.print(respLen); 107 | Serial.println(F(" bytes:")); 108 | PRINT_BUFF(respFrame, respLen); 109 | 110 | // get function ID 111 | uint8_t functionId = FCP_Get_FunctionID(callsign, respFrame, respLen); 112 | Serial.print(F("Function ID: 0x")); 113 | Serial.println(functionId, HEX); 114 | 115 | // check optional data 116 | uint8_t respOptDataLen = FCP_Get_OptData_Length(callsign, respFrame, respLen); 117 | if(respOptDataLen > 0) { 118 | // frame contains optional data 119 | uint8_t* respOptData = new uint8_t[respOptDataLen]; 120 | FCP_Get_OptData(callsign, respFrame, respLen, respOptData); 121 | 122 | // check packet info response 123 | if(functionId == RESP_PACKET_INFO) { 124 | Serial.println(F("Last packet info:")); 125 | 126 | Serial.print(F("SNR = ")); 127 | Serial.print(respOptData[0] / 4.0); 128 | Serial.println(F(" dB")); 129 | 130 | Serial.print(F("RSSI = ")); 131 | Serial.print(respOptData[1] / -2.0); 132 | Serial.println(F(" dBm")); 133 | } 134 | 135 | delete[] respOptData; 136 | } 137 | 138 | } else { 139 | Serial.println(F("Reception failed, code ")); 140 | Serial.println(state); 141 | 142 | } 143 | 144 | // enable reception interrupt 145 | delete[] respFrame; 146 | enableInterrupt = true; 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /examples/GetSystemInfo/GetSystemInfo.ino: -------------------------------------------------------------------------------- 1 | // include the libraries 2 | #include 3 | #include 4 | 5 | // SX1262 has the following connections: 6 | // NSS pin: 10 7 | // DIO1 pin: 2 8 | // BUSY pin: 9 9 | SX1262 radio = new Module(10, 2, 9); 10 | 11 | // satellite callsign 12 | char callsign[] = "FOSSASAT-1"; 13 | 14 | // last transmission timestamp 15 | uint32_t lastTransmit = 0; 16 | 17 | // transmission period in ms 18 | const uint32_t transmitPeriod = 4000; 19 | 20 | // interrupt flags 21 | volatile bool receivedFlag = false; 22 | volatile bool enableInterrupt = true; 23 | 24 | // interrupt service routine for data reception 25 | void setFlag(void) { 26 | if(!enableInterrupt) { 27 | return; 28 | } 29 | 30 | receivedFlag = true; 31 | } 32 | 33 | void setup() { 34 | Serial.begin(9600); 35 | 36 | // initialize SX1262 37 | Serial.print(F("Initializing ... ")); 38 | int state = radio.begin(436.7, 125.0, 11, 8, 0x0F0F); 39 | if (state == ERR_NONE) { 40 | Serial.println(F("success!")); 41 | } else { 42 | Serial.print(F("failed, code ")); 43 | Serial.println(state); 44 | while (true); 45 | } 46 | 47 | // set interrupt service routine 48 | radio.setDio1Action(setFlag); 49 | 50 | // start listening 51 | radio.startReceive(); 52 | } 53 | 54 | void loop() { 55 | // check if it's time to transmit 56 | if(millis() - lastTransmit >= transmitPeriod) { 57 | // disable reception interrupt 58 | enableInterrupt = false; 59 | detachInterrupt(digitalPinToInterrupt(2)); 60 | 61 | // save timestamp 62 | lastTransmit = millis(); 63 | 64 | Serial.println(F("Transmitting packet ... ")); 65 | 66 | // data to transmit 67 | uint8_t functionId = CMD_TRANSMIT_SYSTEM_INFO; 68 | 69 | // build frame 70 | uint8_t len = FCP_Get_Frame_Length(callsign); 71 | uint8_t* frame = new uint8_t[len]; 72 | FCP_Encode(frame, callsign, functionId); 73 | PRINT_BUFF(frame, len); 74 | 75 | // send data 76 | int state = radio.transmit(frame, len); 77 | delete[] frame; 78 | 79 | // check transmission success 80 | if (state == ERR_NONE) { 81 | Serial.println(F("Success!")); 82 | } 83 | 84 | // set radio mode to reception 85 | Serial.println(F("Waiting for response ... ")); 86 | radio.setDio1Action(setFlag); 87 | radio.startReceive(); 88 | enableInterrupt = true; 89 | } 90 | 91 | // check if new data were received 92 | if(receivedFlag) { 93 | // disable reception interrupt 94 | enableInterrupt = false; 95 | receivedFlag = false; 96 | 97 | // read received data 98 | size_t respLen = radio.getPacketLength(); 99 | uint8_t* respFrame = new uint8_t[respLen]; 100 | int state = radio.readData(respFrame, respLen); 101 | 102 | // check reception success 103 | if (state == ERR_NONE) { 104 | // print raw data 105 | Serial.print(F("Received ")); 106 | Serial.print(respLen); 107 | Serial.println(F(" bytes:")); 108 | PRINT_BUFF(respFrame, respLen); 109 | 110 | // get function ID 111 | uint8_t functionId = FCP_Get_FunctionID(callsign, respFrame, respLen); 112 | Serial.print(F("Function ID: 0x")); 113 | Serial.println(functionId, HEX); 114 | 115 | // check optional data 116 | uint8_t respOptDataLen = FCP_Get_OptData_Length(callsign, respFrame, respLen); 117 | if(respOptDataLen > 0) { 118 | // frame contains optional data 119 | uint8_t* respOptData = new uint8_t[respOptDataLen]; 120 | FCP_Get_OptData(callsign, respFrame, respLen, respOptData); 121 | 122 | // check system info response 123 | if(functionId == RESP_SYSTEM_INFO) { 124 | Serial.println(F("System info:")); 125 | 126 | Serial.print(F("batteryChargingVoltage = ")); 127 | Serial.println(FCP_Get_Battery_Charging_Voltage(respOptData)); 128 | 129 | Serial.print(F("batteryChargingCurrent = ")); 130 | Serial.println(FCP_Get_Battery_Charging_Current(respOptData), 4); 131 | 132 | Serial.print(F("batteryVoltage = ")); 133 | Serial.println(FCP_Get_Battery_Voltage(respOptData)); 134 | 135 | Serial.print(F("solarCellAVoltage = ")); 136 | Serial.println(FCP_Get_Solar_Cell_Voltage(0, respOptData)); 137 | 138 | Serial.print(F("solarCellBVoltage = ")); 139 | Serial.println(FCP_Get_Solar_Cell_Voltage(1, respOptData)); 140 | 141 | Serial.print(F("solarCellCVoltage = ")); 142 | Serial.println(FCP_Get_Solar_Cell_Voltage(2, respOptData)); 143 | 144 | Serial.print(F("batteryTemperature = ")); 145 | Serial.println(FCP_Get_Battery_Temperature(respOptData)); 146 | 147 | Serial.print(F("boardTemperature = ")); 148 | Serial.println(FCP_Get_Board_Temperature(respOptData)); 149 | 150 | Serial.print(F("mcuTemperature = ")); 151 | Serial.println(FCP_Get_MCU_Temperature(respOptData)); 152 | 153 | Serial.print(F("resetCounter = ")); 154 | Serial.println(FCP_Get_Reset_Counter(respOptData)); 155 | 156 | Serial.print(F("powerConfig = 0b")); 157 | Serial.println(FCP_Get_Power_Configuration(respOptData), BIN); 158 | } 159 | 160 | delete[] respOptData; 161 | } 162 | 163 | } else { 164 | Serial.println(F("Reception failed, code ")); 165 | Serial.println(state); 166 | 167 | } 168 | 169 | // enable reception interrupt 170 | delete[] respFrame; 171 | enableInterrupt = true; 172 | } 173 | 174 | } 175 | -------------------------------------------------------------------------------- /examples/PingPong/PingPong.ino: -------------------------------------------------------------------------------- 1 | // include the libraries 2 | #include 3 | #include 4 | 5 | // SX1262 has the following connections: 6 | // NSS pin: 10 7 | // DIO1 pin: 2 8 | // BUSY pin: 9 9 | SX1262 radio = new Module(10, 2, 9); 10 | 11 | // satellite callsign 12 | char callsign[] = "FOSSASAT-1"; 13 | 14 | // last transmission timestamp 15 | uint32_t lastTransmit = 0; 16 | 17 | // transmission period in ms 18 | const uint32_t transmitPeriod = 4000; 19 | 20 | // interrupt flags 21 | volatile bool receivedFlag = false; 22 | volatile bool enableInterrupt = true; 23 | 24 | // interrupt service routine for data reception 25 | void setFlag(void) { 26 | if(!enableInterrupt) { 27 | return; 28 | } 29 | 30 | receivedFlag = true; 31 | } 32 | 33 | void setup() { 34 | Serial.begin(9600); 35 | 36 | // initialize SX1262 37 | Serial.print(F("Initializing ... ")); 38 | int state = radio.begin(436.7, 125.0, 11, 8, 0x0F0F); 39 | if (state == ERR_NONE) { 40 | Serial.println(F("success!")); 41 | } else { 42 | Serial.print(F("failed, code ")); 43 | Serial.println(state); 44 | while (true); 45 | } 46 | 47 | // set interrupt service routine 48 | radio.setDio1Action(setFlag); 49 | 50 | // start listening 51 | radio.startReceive(); 52 | } 53 | 54 | void loop() { 55 | // check if it's time to transmit 56 | if(millis() - lastTransmit >= transmitPeriod) { 57 | // disable reception interrupt 58 | enableInterrupt = false; 59 | detachInterrupt(digitalPinToInterrupt(2)); 60 | 61 | // save timestamp 62 | lastTransmit = millis(); 63 | 64 | Serial.println(F("Transmitting packet ... ")); 65 | 66 | // data to transmit 67 | uint8_t functionId = CMD_PING; 68 | 69 | // build frame 70 | uint8_t len = FCP_Get_Frame_Length(callsign); 71 | uint8_t* frame = new uint8_t[len]; 72 | FCP_Encode(frame, callsign, functionId); 73 | PRINT_BUFF(frame, len); 74 | 75 | // send data 76 | int state = radio.transmit(frame, len); 77 | delete[] frame; 78 | Serial.println(F("Ping!")); 79 | 80 | // check transmission success 81 | if (state == ERR_NONE) { 82 | Serial.println(F("Success!")); 83 | } 84 | 85 | // set radio mode to reception 86 | Serial.println(F("Waiting for response ... ")); 87 | radio.setDio1Action(setFlag); 88 | radio.startReceive(); 89 | enableInterrupt = true; 90 | } 91 | 92 | // check if new data were received 93 | if(receivedFlag) { 94 | // disable reception interrupt 95 | enableInterrupt = false; 96 | receivedFlag = false; 97 | 98 | // read received data 99 | size_t respLen = radio.getPacketLength(); 100 | uint8_t* respFrame = new uint8_t[respLen]; 101 | int state = radio.readData(respFrame, respLen); 102 | 103 | // check reception success 104 | if (state == ERR_NONE) { 105 | // print raw data 106 | Serial.print(F("Received ")); 107 | Serial.print(respLen); 108 | Serial.println(F(" bytes:")); 109 | PRINT_BUFF(respFrame, respLen); 110 | 111 | // get function ID 112 | uint8_t functionId = FCP_Get_FunctionID(callsign, respFrame, respLen); 113 | Serial.print(F("Function ID: 0x")); 114 | Serial.println(functionId, HEX); 115 | 116 | if(functionId == RESP_PONG) { 117 | Serial.println(F("Pong!")); 118 | } 119 | 120 | } else { 121 | Serial.println(F("Reception failed, code ")); 122 | Serial.println(state); 123 | 124 | } 125 | 126 | // enable reception interrupt 127 | delete[] respFrame; 128 | enableInterrupt = true; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /examples/RepeatMessage/RepeatMessage.ino: -------------------------------------------------------------------------------- 1 | // include the libraries 2 | #include 3 | #include 4 | 5 | // SX1262 has the following connections: 6 | // NSS pin: 10 7 | // DIO1 pin: 2 8 | // BUSY pin: 9 9 | SX1268 radio = new Module(10, 2, 9); 10 | 11 | // satellite callsign 12 | char callsign[] = "FOSSASAT-1"; 13 | 14 | // last transmission timestamp 15 | uint32_t lastTransmit = 0; 16 | 17 | // transmission period in ms 18 | const uint32_t transmitPeriod = 4000; 19 | 20 | // interrupt flags 21 | volatile bool receivedFlag = false; 22 | volatile bool enableInterrupt = true; 23 | 24 | // interrupt service routine for data reception 25 | void setFlag(void) { 26 | if(!enableInterrupt) { 27 | return; 28 | } 29 | 30 | receivedFlag = true; 31 | } 32 | 33 | void setup() { 34 | Serial.begin(9600); 35 | 36 | // initialize SX1262 37 | Serial.print(F("Initializing ... ")); 38 | int state = radio.begin(436.7, 125.0, 11, 8, 0x0F0F); 39 | if (state == ERR_NONE) { 40 | Serial.println(F("success!")); 41 | } else { 42 | Serial.print(F("failed, code ")); 43 | Serial.println(state); 44 | while (true); 45 | } 46 | 47 | // set interrupt service routine 48 | radio.setDio1Action(setFlag); 49 | 50 | // start listening 51 | radio.startReceive(); 52 | } 53 | 54 | void loop() { 55 | // check if it's time to transmit 56 | if(millis() - lastTransmit >= transmitPeriod) { 57 | // disable reception interrupt 58 | enableInterrupt = false; 59 | detachInterrupt(digitalPinToInterrupt(2)); 60 | 61 | // save timestamp 62 | lastTransmit = millis(); 63 | 64 | Serial.println(F("Transmitting packet ... ")); 65 | 66 | // data to transmit 67 | uint8_t functionId = CMD_RETRANSMIT; 68 | char optData[] = "Hello there"; 69 | uint8_t optDataLen = strlen(optData); 70 | 71 | // build frame 72 | uint8_t len = FCP_Get_Frame_Length(callsign, optDataLen); 73 | uint8_t* frame = new uint8_t[len]; 74 | FCP_Encode(frame, callsign, functionId, optDataLen, (uint8_t*)optData); 75 | PRINT_BUFF(frame, len); 76 | 77 | // send data 78 | int state = radio.transmit(frame, len); 79 | delete[] frame; 80 | 81 | // check transmission success 82 | if (state == ERR_NONE) { 83 | Serial.println(F("Success!")); 84 | } 85 | 86 | // set radio mode to reception 87 | Serial.println(F("Waiting for response ... ")); 88 | radio.setDio1Action(setFlag); 89 | radio.startReceive(); 90 | enableInterrupt = true; 91 | } 92 | 93 | // check if new data were received 94 | if(receivedFlag) { 95 | // disable reception interrupt 96 | enableInterrupt = false; 97 | receivedFlag = false; 98 | 99 | // read received data 100 | size_t respLen = radio.getPacketLength(); 101 | uint8_t* respFrame = new uint8_t[respLen]; 102 | int state = radio.readData(respFrame, respLen); 103 | 104 | // check reception success 105 | if (state == ERR_NONE) { 106 | // print raw data 107 | Serial.print(F("Received ")); 108 | Serial.print(respLen); 109 | Serial.println(F(" bytes:")); 110 | PRINT_BUFF(respFrame, respLen); 111 | 112 | // get function ID 113 | uint8_t functionId = FCP_Get_FunctionID(callsign, respFrame, respLen); 114 | Serial.print(F("Function ID: 0x")); 115 | Serial.println(functionId, HEX); 116 | 117 | // check optional data 118 | uint8_t respOptDataLen = FCP_Get_OptData_Length(callsign, respFrame, respLen); 119 | if(respOptDataLen > 0) { 120 | // frame contains optional data 121 | uint8_t* respOptData = new uint8_t[respOptDataLen]; 122 | FCP_Get_OptData(callsign, respFrame, respLen, respOptData); 123 | 124 | // print optional data 125 | Serial.print(F("Optional data (")); 126 | Serial.print(respOptDataLen); 127 | Serial.println(F(" bytes):")); 128 | PRINT_BUFF(respOptData, respOptDataLen); 129 | delete[] respOptData; 130 | } 131 | 132 | } else { 133 | Serial.println(F("Reception failed, code ")); 134 | Serial.println(state); 135 | 136 | } 137 | 138 | // enable reception interrupt 139 | delete[] respFrame; 140 | enableInterrupt = true; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /examples/RepeatMessageCustom/RepeatMessageCustom.ino: -------------------------------------------------------------------------------- 1 | // include the libraries 2 | #include 3 | #include 4 | 5 | // modulation properties to be used in transmission 6 | #define BANDWIDTH 7 // 125 kHz - see array below 7 | #define SPREADING_FACTOR 12 // SF 12 8 | #define CODING_RATE 7 // CR 4/7 9 | #define PREAMBLE_LENGTH 32 // symbols 10 | #define CRC_ENABLED 1 11 | #define OUTPUT_POWER 10 // dBm 12 | 13 | // array of allowed bandwidth values in kHz 14 | float bws[] = {7.8, 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125.0}; 15 | 16 | // SX1262 has the following connections: 17 | // NSS pin: 10 18 | // DIO1 pin: 2 19 | // BUSY pin: 9 20 | SX1262 radio = new Module(10, 2, 9); 21 | 22 | // satellite callsign 23 | char callsign[] = "FOSSASAT-1"; 24 | 25 | // last transmission timestamp 26 | uint32_t lastTransmit = 0; 27 | 28 | // transmission period in ms 29 | const uint32_t transmitPeriod = 6000; 30 | 31 | // interrupt flags 32 | volatile bool receivedFlag = false; 33 | volatile bool enableInterrupt = true; 34 | 35 | // interrupt service routine for data reception 36 | void setFlag(void) { 37 | if(!enableInterrupt) { 38 | return; 39 | } 40 | 41 | receivedFlag = true; 42 | } 43 | 44 | void setup() { 45 | Serial.begin(9600); 46 | 47 | // initialize SX1262 48 | Serial.print(F("Initializing ... ")); 49 | int state = radio.begin(436.7, 125.0, 11, 8, 0x0F0F, 21, 120, 16); 50 | if (state == ERR_NONE) { 51 | Serial.println(F("success!")); 52 | } else { 53 | Serial.print(F("failed, code ")); 54 | Serial.println(state); 55 | while (true); 56 | } 57 | 58 | // set interrupt service routine 59 | radio.setDio1Action(setFlag); 60 | 61 | // start listening 62 | radio.startReceive(); 63 | } 64 | 65 | void loop() { 66 | // check if it's time to transmit 67 | if(millis() - lastTransmit >= transmitPeriod) { 68 | // disable reception interrupt 69 | enableInterrupt = false; 70 | detachInterrupt(digitalPinToInterrupt(2)); 71 | 72 | // save timestamp 73 | lastTransmit = millis(); 74 | 75 | Serial.println(F("Transmitting packet ... ")); 76 | 77 | // data to transmit 78 | uint8_t functionId = CMD_RETRANSMIT_CUSTOM; 79 | char message[] = "I'm a message!"; 80 | uint8_t optDataLen = 7 + strlen(message); 81 | uint8_t* optData = new uint8_t[optDataLen]; 82 | optData[0] = BANDWIDTH; 83 | optData[1] = SPREADING_FACTOR; 84 | optData[2] = CODING_RATE; 85 | optData[3] = (uint8_t)(PREAMBLE_LENGTH & 0xFF); 86 | optData[4] = (uint8_t)((PREAMBLE_LENGTH >> 8) & 0xFF); 87 | optData[5] = CRC_ENABLED; 88 | optData[6] = (uint8_t)OUTPUT_POWER; 89 | memcpy(optData + 7, message, strlen(message)); 90 | 91 | // build frame 92 | uint8_t len = FCP_Get_Frame_Length(callsign, optDataLen); 93 | uint8_t* frame = new uint8_t[len]; 94 | FCP_Encode(frame, callsign, functionId, optDataLen, optData); 95 | delete[] optData; 96 | PRINT_BUFF(frame, len); 97 | 98 | // send data with default configuration 99 | radio.begin(436.7, 125.0, 11, 8, 0x0F0F, 21, 120, 16); 100 | radio.setCRC(true); 101 | int state = radio.transmit(frame, len); 102 | delete[] frame; 103 | 104 | // check transmission success 105 | if (state == ERR_NONE) { 106 | Serial.println(F("Success!")); 107 | } 108 | 109 | // set radio mode to reception with custom configuration 110 | Serial.println(F("Waiting for response ... ")); 111 | radio.begin(436.7, bws[BANDWIDTH], SPREADING_FACTOR, CODING_RATE, 0x0F0F, OUTPUT_POWER, 120, PREAMBLE_LENGTH); 112 | radio.setCRC(CRC_ENABLED); 113 | radio.setDio1Action(setFlag); 114 | radio.startReceive(); 115 | enableInterrupt = true; 116 | } 117 | 118 | // check if new data were received 119 | if(receivedFlag) { 120 | // disable reception interrupt 121 | enableInterrupt = false; 122 | receivedFlag = false; 123 | 124 | // read received data 125 | size_t respLen = radio.getPacketLength(); 126 | uint8_t* respFrame = new uint8_t[respLen]; 127 | int state = radio.readData(respFrame, respLen); 128 | 129 | // check reception success 130 | if (state == ERR_NONE) { 131 | // print raw data 132 | Serial.print(F("Received ")); 133 | Serial.print(respLen); 134 | Serial.println(F(" bytes:")); 135 | PRINT_BUFF(respFrame, respLen); 136 | 137 | // get function ID 138 | uint8_t functionId = FCP_Get_FunctionID(callsign, respFrame, respLen); 139 | Serial.print(F("Function ID: 0x")); 140 | Serial.println(functionId, HEX); 141 | 142 | if(functionId == RESP_REPEATED_MESSAGE_CUSTOM) { 143 | Serial.println(F("Got response with custom configuration!")); 144 | 145 | // check optional data 146 | uint8_t respOptDataLen = FCP_Get_OptData_Length(callsign, respFrame, respLen); 147 | if(respOptDataLen > 0) { 148 | // frame contains optional data 149 | uint8_t* respOptData = new uint8_t[respOptDataLen]; 150 | FCP_Get_OptData(callsign, respFrame, respLen, respOptData); 151 | 152 | // print optional data 153 | Serial.print(F("Optional data (")); 154 | Serial.print(respOptDataLen); 155 | Serial.println(F(" bytes):")); 156 | PRINT_BUFF(respOptData, respOptDataLen); 157 | delete[] respOptData; 158 | } 159 | } 160 | 161 | } else { 162 | Serial.println(F("Reception failed, code ")); 163 | Serial.println(state); 164 | 165 | } 166 | 167 | // enable reception interrupt 168 | delete[] respFrame; 169 | enableInterrupt = true; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | FOSSA-Comms KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | 16 | 17 | ####################################### 18 | # Constants (LITERAL1) 19 | ####################################### 20 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=FOSSA-Comms 2 | version=1.0.0 3 | author=Jan Gromes 4 | maintainer=Jan Gromes 5 | sentence=Arduino library for communication with FOSSA satellites 6 | paragraph=Implementation of communication protocols used on FOSSA satellites. 7 | category=Communication 8 | url= 9 | architectures=* 10 | includes=FOSSA-Comms.h 11 | -------------------------------------------------------------------------------- /src/FOSSA-Comms.cpp: -------------------------------------------------------------------------------- 1 | #include "FOSSA-Comms.h" 2 | 3 | int16_t FCP_Get_Frame_Length(char* callsign, uint8_t optDataLen, const char* password) { 4 | // check callsign 5 | if(callsign == NULL) { 6 | return(ERR_CALLSIGN_INVALID); 7 | } 8 | 9 | // callsign and function ID fields are always present 10 | int16_t frameLen = strlen(callsign) + 1; 11 | 12 | // optDataLen and optData might be present 13 | if(optDataLen > 0) { 14 | frameLen += 1 + optDataLen; 15 | } 16 | 17 | // check if the frame is encrpyted 18 | if(password != NULL) { 19 | frameLen += strlen(password); 20 | if(optDataLen == 0) { 21 | frameLen++; 22 | } 23 | frameLen += 16 - ((1 + optDataLen + strlen(password)) % 16); 24 | } 25 | 26 | return(frameLen); 27 | } 28 | 29 | int16_t FCP_Get_OptData_Length(char* callsign, uint8_t* frame, uint8_t frameLen, const uint8_t* key, const char* password) { 30 | // check callsign 31 | if(callsign == NULL) { 32 | return(ERR_CALLSIGN_INVALID); 33 | } 34 | 35 | // check frame buffer 36 | if(frame == NULL) { 37 | return(ERR_FRAME_INVALID); 38 | } 39 | 40 | // check frame length 41 | if(frameLen < strlen(callsign) + 1) { 42 | return(ERR_FRAME_INVALID); 43 | } else if(frameLen == strlen(callsign) + 1) { 44 | return(0); 45 | } 46 | 47 | // check encryption 48 | if((key != NULL) && (password != NULL)) { 49 | // encrypted frame 50 | 51 | // extract encrypted section 52 | uint8_t encSectionLen = frameLen - strlen(callsign) - 1; 53 | uint8_t* encSection = new uint8_t[encSectionLen]; 54 | memcpy(encSection, frame + strlen(callsign) + 1, encSectionLen); 55 | 56 | // decrypt 57 | struct AES_ctx ctx; 58 | AES_init_ctx(&ctx, key); 59 | uint8_t numBlocks = encSectionLen / 16; 60 | for(uint8_t i = 0; i < numBlocks; i++) { 61 | AES_ECB_decrypt(&ctx, encSection + (i * 16)); 62 | } 63 | 64 | // check password 65 | if(memcmp(encSection + 1, password, strlen(password)) == 0) { 66 | // check passed 67 | int16_t optDataLen = encSection[0] - strlen(password); 68 | 69 | // deallocate memory 70 | delete[] encSection; 71 | return(optDataLen); 72 | } 73 | 74 | // deallocate memory 75 | delete[] encSection; 76 | 77 | } else { 78 | // unencrypted frame 79 | int16_t optDataLen = frame[strlen(callsign) + 1]; 80 | 81 | // check if optDataLen field matches the expected length 82 | if(optDataLen != (uint8_t)(frameLen - strlen(callsign) - 2)) { 83 | // length mismatch 84 | return(ERR_LENGTH_MISMATCH); 85 | } 86 | 87 | return(optDataLen); 88 | } 89 | 90 | return(ERR_INCORRECT_PASSWORD); 91 | } 92 | 93 | int16_t FCP_Get_FunctionID(char* callsign, uint8_t* frame, uint8_t frameLen) { 94 | // check callsign 95 | if(callsign == NULL) { 96 | return(ERR_CALLSIGN_INVALID); 97 | } 98 | 99 | // check frame buffer 100 | if(frame == NULL) { 101 | return(ERR_FRAME_INVALID); 102 | } 103 | 104 | // check frame length 105 | if(frameLen < strlen(callsign) + 1) { 106 | return(ERR_FRAME_INVALID); 107 | } 108 | 109 | return((int16_t)frame[strlen(callsign)]); 110 | } 111 | 112 | int16_t FCP_Get_OptData(char* callsign, uint8_t* frame, uint8_t frameLen, uint8_t* optData, const uint8_t* key, const char* password) { 113 | // check callsign 114 | if(callsign == NULL) { 115 | return(ERR_CALLSIGN_INVALID); 116 | } 117 | 118 | // check frame 119 | if(frame == NULL) { 120 | return(ERR_FRAME_INVALID); 121 | } 122 | 123 | // get frame pointer 124 | uint8_t* framePtr = frame; 125 | 126 | // check callsign 127 | if(memcmp(framePtr, callsign, strlen(callsign)) != 0) { 128 | // incorrect callsign 129 | return(ERR_CALLSIGN_INVALID); 130 | } 131 | framePtr += strlen(callsign); 132 | 133 | // skip function ID 134 | framePtr += 1; 135 | 136 | // check encryption 137 | if((key != NULL) && (password != NULL)) { 138 | // encrypted frame 139 | 140 | // extract encrypted section 141 | uint8_t encSectionLen = frameLen - strlen(callsign) - 1; 142 | uint8_t* encSection = new uint8_t[encSectionLen]; 143 | memcpy(encSection, framePtr, encSectionLen); 144 | 145 | // decrypt 146 | struct AES_ctx ctx; 147 | AES_init_ctx(&ctx, key); 148 | uint8_t numBlocks = encSectionLen / 16; 149 | for(uint8_t i = 0; i < numBlocks; i++) { 150 | AES_ECB_decrypt(&ctx, encSection + (i * 16)); 151 | } 152 | uint8_t* encSectionPtr = encSection; 153 | 154 | // get optional data length 155 | uint8_t optDataLen = *encSectionPtr; 156 | encSectionPtr += 1; 157 | 158 | // check password 159 | if(memcmp(encSectionPtr, password, strlen(password)) == 0) { 160 | // check passed 161 | 162 | // get optional data 163 | encSectionPtr += strlen(password); 164 | memcpy(optData, encSectionPtr, optDataLen - strlen(password)); 165 | 166 | // deallocate memory 167 | delete[] encSection; 168 | return(ERR_NONE); 169 | } 170 | 171 | // deallocate memory 172 | delete[] encSection; 173 | 174 | } else { 175 | // unencrypted frame 176 | 177 | // get optional data (if present) 178 | if(frameLen > strlen(callsign) + 1) { 179 | if(optData == NULL) { 180 | return(ERR_FRAME_INVALID); 181 | } 182 | 183 | // get option data length 184 | uint8_t optDataLen = *framePtr; 185 | framePtr += 1; 186 | 187 | // get optional data 188 | memcpy(optData, framePtr, optDataLen); 189 | framePtr += optDataLen; 190 | } 191 | 192 | return(ERR_NONE); 193 | } 194 | 195 | return(ERR_INCORRECT_PASSWORD); 196 | } 197 | 198 | int16_t FCP_Encode(uint8_t* frame, char* callsign, uint8_t functionId, uint8_t optDataLen, uint8_t* optData, const uint8_t* key, const char* password) { 199 | // check callsign 200 | if(callsign == NULL) { 201 | return(ERR_CALLSIGN_INVALID); 202 | } 203 | 204 | // check some optional data are provided when optional data length is not 0 (and vice versa) 205 | if(((optDataLen > 0) && (optData == NULL)) || ((optData != NULL) && (optDataLen == 0))) { 206 | return(ERR_FRAME_INVALID); 207 | } 208 | 209 | // check frame buffer 210 | if(frame == NULL) { 211 | return(ERR_FRAME_INVALID); 212 | } 213 | 214 | // get frame pointer 215 | uint8_t* framePtr = frame; 216 | 217 | // set callsign 218 | memcpy(framePtr, callsign, strlen(callsign)); 219 | framePtr += strlen(callsign); 220 | 221 | // set function ID 222 | *framePtr = functionId; 223 | framePtr += 1; 224 | 225 | // check encryption 226 | if((key != NULL) && (password != NULL)) { 227 | // encrypted frame 228 | 229 | // create encrypted section 230 | uint8_t encSectionLen = 1 + strlen(password) + optDataLen; 231 | uint8_t paddingLen = 16 - (encSectionLen % 16); 232 | uint8_t* encSection = new uint8_t[encSectionLen + paddingLen]; 233 | uint8_t* encSectionPtr = encSection; 234 | 235 | // set optional data length 236 | *encSectionPtr = optDataLen + strlen(password); 237 | encSectionPtr += 1; 238 | 239 | // set password 240 | memcpy(encSectionPtr, password, strlen(password)); 241 | encSectionPtr += strlen(password); 242 | 243 | // set optional data 244 | if(optData != NULL) { 245 | memcpy(encSectionPtr, optData, optDataLen); 246 | encSectionPtr += optDataLen; 247 | } 248 | 249 | // add padding 250 | for(uint8_t i = 0; i < paddingLen; i++) { 251 | *(encSectionPtr + i) = (uint8_t)random(0x00, 0x100); 252 | } 253 | 254 | // encrypt 255 | struct AES_ctx ctx; 256 | AES_init_ctx(&ctx, key); 257 | uint8_t numBlocks = (encSectionLen + paddingLen) / 16; 258 | for(uint8_t i = 0; i < numBlocks; i++) { 259 | AES_ECB_encrypt(&ctx, encSection + (i * 16)); 260 | } 261 | 262 | // copy encrypted section into frame buffer 263 | memcpy(framePtr, encSection, encSectionLen + paddingLen); 264 | 265 | // deallocate memory 266 | delete[] encSection; 267 | return(ERR_NONE); 268 | 269 | } else { 270 | // unencrypted frame 271 | 272 | // check option data presence 273 | if(optDataLen > 0) { 274 | // set optional data length 275 | *framePtr = optDataLen; 276 | framePtr += 1; 277 | 278 | // set optional data 279 | memcpy(framePtr, optData, optDataLen); 280 | framePtr += optDataLen; 281 | } 282 | 283 | return(ERR_NONE); 284 | } 285 | 286 | return(ERR_FRAME_INVALID); 287 | } 288 | 289 | float FCP_Get_Battery_Voltage(uint8_t* optData) { 290 | return(FCP_System_Info_Get_Voltage(optData, 0)); 291 | } 292 | 293 | float FCP_Get_Battery_Charging_Current(uint8_t* optData) { 294 | return(FCP_System_Info_Get_Current(optData, 1)); 295 | } 296 | 297 | float FCP_Get_Battery_Charging_Voltage(uint8_t* optData) { 298 | return(FCP_System_Info_Get_Voltage(optData, 3)); 299 | } 300 | 301 | uint32_t FCP_Get_Uptime_Counter(uint8_t* optData) { 302 | if(optData == NULL) { 303 | return(0); 304 | } 305 | 306 | uint32_t val; 307 | memcpy(&val, optData + 4, sizeof(uint32_t)); 308 | return(val); 309 | } 310 | 311 | uint8_t FCP_Get_Power_Configuration(uint8_t* optData) { 312 | if(optData == NULL) { 313 | return(0); 314 | } 315 | 316 | return(optData[8]); 317 | } 318 | 319 | uint16_t FCP_Get_Reset_Counter(uint8_t* optData) { 320 | if(optData == NULL) { 321 | return(0); 322 | } 323 | 324 | uint16_t val; 325 | memcpy(&val, optData + 9, sizeof(uint16_t)); 326 | return(val); 327 | } 328 | 329 | float FCP_Get_Solar_Cell_Voltage(uint8_t cell, uint8_t* optData) { 330 | if(cell > 2) { 331 | return(0); 332 | } 333 | 334 | return(FCP_System_Info_Get_Voltage(optData, 11 + cell)); 335 | } 336 | 337 | float FCP_Get_Battery_Temperature(uint8_t* optData) { 338 | return(FCP_System_Info_Get_Temperature(optData, 14)); 339 | } 340 | 341 | float FCP_Get_Board_Temperature(uint8_t* optData) { 342 | return(FCP_System_Info_Get_Temperature(optData, 16)); 343 | } 344 | 345 | int8_t FCP_Get_MCU_Temperature(uint8_t* optData) { 346 | if(optData == NULL) { 347 | return(0); 348 | } 349 | 350 | int8_t val; 351 | memcpy(&val, optData + 18, sizeof(int8_t)); 352 | return(val); 353 | } 354 | 355 | float FCP_System_Info_Get_Voltage(uint8_t* optData, uint8_t pos) { 356 | if(optData == NULL) { 357 | return(0); 358 | } 359 | 360 | return((float)optData[pos] * ((float)VOLTAGE_MULTIPLIER / (float)VOLTAGE_UNIT)); 361 | } 362 | 363 | float FCP_System_Info_Get_Temperature(uint8_t* optData, uint8_t pos) { 364 | if(optData == NULL) { 365 | return(0); 366 | } 367 | 368 | int16_t raw; 369 | memcpy(&raw, optData + pos, sizeof(int16_t)); 370 | return((float)raw * ((float)TEMPERATURE_MULTIPLIER / (float)TEMPERATURE_UNIT)); 371 | } 372 | 373 | float FCP_System_Info_Get_Current(uint8_t* optData, uint8_t pos) { 374 | if(optData == NULL) { 375 | return(0); 376 | } 377 | 378 | int16_t raw; 379 | memcpy(&raw, optData + pos, sizeof(int16_t)); 380 | return((float)raw * ((float)CURRENT_MULTIPLIER / (float)CURRENT_UNIT)); 381 | } 382 | -------------------------------------------------------------------------------- /src/FOSSA-Comms.h: -------------------------------------------------------------------------------- 1 | #ifndef _FOSSA_COMMS_H 2 | #define _FOSSA_COMMS_H 3 | 4 | #include 5 | #include 6 | 7 | #if ARDUINO >= 100 8 | #include "Arduino.h" 9 | #else 10 | #error "Unsupported Arduino version (< 1.0.0)" 11 | #endif 12 | 13 | // version definitions 14 | #define FCP_VERSION_MAJOR (0x01) 15 | #define FCP_VERSION_MINOR (0x00) 16 | #define FCP_VERSION_PATCH (0x00) 17 | #define FCP_VERSION_EXTRA (0x00) 18 | 19 | #define FCP_VERSION ((FCP_VERSION_MAJOR << 24) | (FCP_VERSION_MINOR << 16) | (FCP_VERSION_PATCH << 8) | (FCP_VERSION_EXTRA)) 20 | 21 | // constants 22 | #define VOLTAGE_MULTIPLIER 20 // 20 mV resolution 23 | #define VOLTAGE_UNIT 1000 24 | #define CURRENT_MULTIPLIER 10 // 10 uA resolution 25 | #define CURRENT_UNIT 1000000 26 | #define TEMPERATURE_MULTIPLIER 10 // 0.01 deg C resolution 27 | #define TEMPERATURE_UNIT 1000 28 | 29 | // RESP_STATISTICS flags 30 | #define STATS_FLAGS_TEMPERATURES 0b00000001 31 | #define STATS_FLAGS_CURRENTS 0b00000010 32 | #define STATS_FLAGS_VOLTAGES 0b00000100 33 | #define STATS_FLAGS_LIGHT 0b00001000 34 | #define STATS_FLAGS_IMU 0b00010000 35 | #define STATS_FLAGS_POWER 0b00100000 36 | 37 | // status codes 38 | #define ERR_NONE 0 39 | #define ERR_CALLSIGN_INVALID -1 40 | #define ERR_FRAME_INVALID -2 41 | #define ERR_INCORRECT_PASSWORD -3 42 | #define ERR_LENGTH_MISMATCH -4 43 | 44 | // communication protocol definitions 45 | #define RESPONSE_OFFSET 0x20 46 | #define PRIVATE_OFFSET 0x40 47 | 48 | // public commands (unencrypted uplink messages) 49 | #define CMD_PING (0x00) 50 | #define CMD_RETRANSMIT (0x01) 51 | #define CMD_RETRANSMIT_CUSTOM (0x02) 52 | #define CMD_TRANSMIT_SYSTEM_INFO (0x03) 53 | #define CMD_GET_PACKET_INFO (0x04) 54 | #define CMD_GET_STATISTICS (0x05) 55 | #define CMD_GET_FULL_SYSTEM_INFO (0x06) 56 | #define CMD_STORE_AND_FORWARD_ADD (0x07) 57 | #define CMD_STORE_AND_FORWARD_REQUEST (0x08) 58 | #define CMD_REQUEST_PUBLIC_PICTURE (0x09) 59 | 60 | #define NUM_PUBLIC_COMMANDS (0x0A) 61 | 62 | // public responses (unencrypted downlink messages) 63 | #define RESP_PONG (CMD_PING + RESPONSE_OFFSET) 64 | #define RESP_REPEATED_MESSAGE (CMD_RETRANSMIT + RESPONSE_OFFSET) 65 | #define RESP_REPEATED_MESSAGE_CUSTOM (CMD_RETRANSMIT_CUSTOM + RESPONSE_OFFSET) 66 | #define RESP_SYSTEM_INFO (CMD_TRANSMIT_SYSTEM_INFO + RESPONSE_OFFSET) 67 | #define RESP_PACKET_INFO (CMD_GET_PACKET_INFO + RESPONSE_OFFSET) 68 | #define RESP_STATISTICS (CMD_GET_STATISTICS + RESPONSE_OFFSET) 69 | #define RESP_FULL_SYSTEM_INFO (CMD_GET_FULL_SYSTEM_INFO + RESPONSE_OFFSET) 70 | #define RESP_STORE_AND_FORWARD_ASSIGNED_SLOT (CMD_STORE_AND_FORWARD_ADD + RESPONSE_OFFSET) 71 | #define RESP_FORWARDED_MESSAGE (CMD_STORE_AND_FORWARD_REQUEST + RESPONSE_OFFSET) 72 | #define RESP_PUBLIC_PICTURE (CMD_REQUEST_PUBLIC_PICTURE + RESPONSE_OFFSET) 73 | #define RESP_DEPLOYMENT_STATE (NUM_PUBLIC_COMMANDS + RESPONSE_OFFSET) 74 | #define RESP_RECORDED_SOLAR_CELLS (NUM_PUBLIC_COMMANDS + 1 + RESPONSE_OFFSET) 75 | #define RESP_CAMERA_STATE (NUM_PUBLIC_COMMANDS + 2 + RESPONSE_OFFSET) 76 | #define RESP_RECORDED_IMU (NUM_PUBLIC_COMMANDS + 3 + RESPONSE_OFFSET) 77 | #define RESP_MANUAL_ACS_RESULT (NUM_PUBLIC_COMMANDS + 4 + RESPONSE_OFFSET) 78 | #define RESP_GPS_LOG (NUM_PUBLIC_COMMANDS + 5 + RESPONSE_OFFSET) 79 | #define RESP_GPS_LOG_STATE (NUM_PUBLIC_COMMANDS + 6 + RESPONSE_OFFSET) 80 | #define RESP_FLASH_CONTENTS (NUM_PUBLIC_COMMANDS + 7 + RESPONSE_OFFSET) 81 | #define RESP_CAMERA_PICTURE (NUM_PUBLIC_COMMANDS + 8 + RESPONSE_OFFSET) 82 | #define RESP_CAMERA_PICTURE_LENGTH (NUM_PUBLIC_COMMANDS + 9 + RESPONSE_OFFSET) 83 | #define RESP_GPS_COMMAND_RESPONSE (NUM_PUBLIC_COMMANDS + 10 + RESPONSE_OFFSET) 84 | 85 | // ACK is the last public function ID 86 | #define RESP_ACKNOWLEDGE (PRIVATE_OFFSET - 1) 87 | 88 | // private commands (encrypted uplink messages) 89 | #define CMD_DEPLOY (0x00 + PRIVATE_OFFSET) 90 | #define CMD_RESTART (0x01 + PRIVATE_OFFSET) 91 | #define CMD_WIPE_EEPROM (0x02 + PRIVATE_OFFSET) 92 | #define CMD_SET_TRANSMIT_ENABLE (0x03 + PRIVATE_OFFSET) 93 | #define CMD_SET_CALLSIGN (0x04 + PRIVATE_OFFSET) 94 | #define CMD_SET_SF_MODE (0x05 + PRIVATE_OFFSET) 95 | #define CMD_SET_MPPT_MODE (0x06 + PRIVATE_OFFSET) 96 | #define CMD_SET_LOW_POWER_ENABLE (0x07 + PRIVATE_OFFSET) 97 | #define CMD_SET_RECEIVE_WINDOWS (0x08 + PRIVATE_OFFSET) 98 | #define CMD_RECORD_SOLAR_CELLS (0x09 + PRIVATE_OFFSET) 99 | #define CMD_CAMERA_CAPTURE (0x0A + PRIVATE_OFFSET) 100 | #define CMD_SET_POWER_LIMITS (0x0B + PRIVATE_OFFSET) 101 | #define CMD_SET_RTC (0x0C + PRIVATE_OFFSET) 102 | #define CMD_RECORD_IMU (0x0D + PRIVATE_OFFSET) 103 | #define CMD_RUN_MANUAL_ACS (0x0E + PRIVATE_OFFSET) 104 | #define CMD_LOG_GPS (0x0F + PRIVATE_OFFSET) 105 | #define CMD_GET_GPS_LOG (0x10 + PRIVATE_OFFSET) 106 | #define CMD_GET_FLASH_CONTENTS (0x11 + PRIVATE_OFFSET) 107 | #define CMD_GET_PICTURE_LENGTH (0x12 + PRIVATE_OFFSET) 108 | #define CMD_GET_PICTURE_BURST (0x13 + PRIVATE_OFFSET) 109 | #define CMD_ROUTE (0x14 + PRIVATE_OFFSET) 110 | #define CMD_SET_FLASH_CONTENTS (0x15 + PRIVATE_OFFSET) 111 | #define CMD_SET_TLE (0x16 + PRIVATE_OFFSET) 112 | #define CMD_GET_GPS_LOG_STATE (0x17 + PRIVATE_OFFSET) 113 | #define CMD_RUN_GPS_COMMAND (0x18 + PRIVATE_OFFSET) 114 | #define CMD_SET_SLEEP_INTERVALS (0x19 + PRIVATE_OFFSET) 115 | #define CMD_ABORT (0x1A + PRIVATE_OFFSET) 116 | #define CMD_MANEUVER (0x1B + PRIVATE_OFFSET) 117 | #define CMD_SET_ADCS_PARAMETERS (0x1C + PRIVATE_OFFSET) 118 | #define CMD_ERASE_FLASH (0x1D + PRIVATE_OFFSET) 119 | #define CMD_SET_ADCS_CONTROLLER (0x1E + PRIVATE_OFFSET) 120 | #define CMD_SET_ADCS_EPHEMERIDES (0x1F + PRIVATE_OFFSET) 121 | #define CMD_DETUMBLE (0x20 + PRIVATE_OFFSET) 122 | #define CMD_SET_IMU_OFFSET (0x21 + PRIVATE_OFFSET) 123 | #define CMD_SET_IMU_CALIBRATION (0x22 + PRIVATE_OFFSET) 124 | 125 | #define NUM_PRIVATE_COMMANDS (0x23) 126 | 127 | #define PRINT_BUFF(BUFF, LEN) { \ 128 | for(size_t i = 0; i < LEN; i++) { \ 129 | Serial.print(F("0x")); \ 130 | Serial.print(BUFF[i], HEX); \ 131 | Serial.print('\t'); \ 132 | Serial.write(BUFF[i]); \ 133 | Serial.println(); \ 134 | } } 135 | 136 | int16_t FCP_Get_Frame_Length(char* callsign, uint8_t optDataLen = 0, const char* password = NULL); 137 | int16_t FCP_Get_FunctionID(char* callsign, uint8_t* frame, uint8_t frameLen); 138 | int16_t FCP_Get_OptData_Length(char* callsign, uint8_t* frame, uint8_t frameLen, const uint8_t* key = NULL, const char* password = NULL); 139 | int16_t FCP_Get_OptData(char* callsign, uint8_t* frame, uint8_t frameLen, uint8_t* optData, const uint8_t* key = NULL, const char* password = NULL); 140 | 141 | int16_t FCP_Encode(uint8_t* frame, char* callsign, uint8_t functionId, uint8_t optDataLen = 0, uint8_t* optData = NULL, const uint8_t* key = NULL, const char* password = NULL); 142 | 143 | float FCP_Get_Battery_Voltage(uint8_t* optData); 144 | float FCP_Get_Battery_Charging_Current(uint8_t* optData); 145 | float FCP_Get_Battery_Charging_Voltage(uint8_t* optData); 146 | uint32_t FCP_Get_Uptime_Counter(uint8_t* optData); 147 | uint8_t FCP_Get_Power_Configuration(uint8_t* optData); 148 | uint16_t FCP_Get_Reset_Counter(uint8_t* optData); 149 | float FCP_Get_Solar_Cell_Voltage(uint8_t cell, uint8_t* optData); 150 | float FCP_Get_Battery_Temperature(uint8_t* optData); 151 | float FCP_Get_Board_Temperature(uint8_t* optData); 152 | int8_t FCP_Get_MCU_Temperature(uint8_t* optData); 153 | 154 | float FCP_System_Info_Get_Voltage(uint8_t* optData, uint8_t pos); 155 | float FCP_System_Info_Get_Temperature(uint8_t* optData, uint8_t pos); 156 | float FCP_System_Info_Get_Current(uint8_t* optData, uint8_t pos); 157 | 158 | #endif 159 | --------------------------------------------------------------------------------