FOSSASAT-1B
communication.cpp
1 #include "communication.h"
2 
4  // check interrups are enabled
5  if(!interruptsEnabled) {
6  return;
7  }
8 
9  // set flag
10  dataReceived = true;
11 }
12 
13 int16_t Communication_Set_Modem(uint8_t modem) {
14  int16_t state = ERR_NONE;
15  FOSSASAT_DEBUG_WRITE(modem);
16 
17  // initialize requested modem
18  switch (modem) {
19  case MODEM_LORA:
20  state = radio.begin(LORA_CARRIER_FREQUENCY,
22  LORA_SPREADING_FACTOR,
24  SYNC_WORD,
27  TCXO_VOLTAGE);
28  radio.setCRC(true);
29  radio.setCurrentLimit(LORA_CURRENT_LIMIT);
30  break;
31  case MODEM_FSK: {
32  state = radio.beginFSK(FSK_CARRIER_FREQUENCY,
38  TCXO_VOLTAGE);
39  uint8_t syncWordFSK[2] = {SYNC_WORD, SYNC_WORD};
40  radio.setSyncWord(syncWordFSK, 2);
41  radio.setCRC(2);
42  radio.setDataShaping(FSK_DATA_SHAPING);
43  radio.setCurrentLimit(FSK_CURRENT_LIMIT);
44  } break;
45  default:
46  return(ERR_UNKNOWN);
47  }
48 
49  radio.setWhitening(true, WHITENING_INITIAL);
50 
51  // handle possible error codes
52  FOSSASAT_DEBUG_PRINT(F("Init "));
53  FOSSASAT_DEBUG_PRINTLN(state);
54  FOSSASAT_DEBUG_DELAY(10);
55  if (state != ERR_NONE) {
56  // radio chip failed, restart
58  }
59 
60  // set spreading factor
62 
63  // set TCXO
64  radio.setTCXO(TCXO_VOLTAGE);
65 
66  // save current modem
67  currentModem = modem;
68  return(state);
69 }
70 
71 int16_t Communication_Set_SpreadingFactor(uint8_t sfMode) {
72  uint8_t sfs[] = {LORA_SPREADING_FACTOR, LORA_SPREADING_FACTOR_ALT};
73 
74  // check currently active modem
75  if(currentModem == MODEM_FSK) {
76  return(ERR_WRONG_MODEM);
77  }
78 
79  // update standard/alternative spreading factor
80  int16_t state = radio.setSpreadingFactor(sfs[sfMode]);
81 
82  // only save current spreading factor mode if the change was successful
83  if(state == ERR_NONE) {
84  spreadingFactorMode = sfMode;
85  }
86 
87  return(state);
88 }
89 
90 int16_t Communication_Set_Configuration(uint8_t* optData, uint8_t optDataLen) {
91  // check optDataLen
92  if(!((optDataLen >= 7 + 1) && (optDataLen <= 7 + MAX_STRING_LENGTH))) {
93  return(ERR_PACKET_TOO_LONG);
94  }
95 
96  // check bandwidth value (loaded from array - rest of settings are checked by library)
97  if(optData[0] > 7) {
98  return(ERR_INVALID_BANDWIDTH);
99  }
100 
101  // attempt to change the settings
102  float bws[] = {7.8, 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125.0};
103  uint16_t preambleLength = 0;
104  memcpy(&preambleLength, optData + 3, sizeof(uint16_t));
105  uint16_t state = radio.begin(LORA_CARRIER_FREQUENCY,
106  bws[optData[0]],
107  optData[1],
108  optData[2],
109  SYNC_WORD,
110  optData[6],
112  preambleLength);
113  if(state != ERR_NONE) {
114  return(state);
115  }
116 
117  // set CRC
118  state = radio.setCRC(optData[5]);
119  return(state);
120 }
121 
122 void Communication_Send_Morse_Beacon(float battVoltage) {
123  // initialize Morse client
125 
126  // read callsign
127  uint8_t callsignLen = Persistent_Storage_Read<uint8_t>(EEPROM_CALLSIGN_LEN_ADDR);
128  char callsign[MAX_STRING_LENGTH + 1];
129  System_Info_Get_Callsign(callsign, callsignLen);
130 
131  // send start signals
132  for(int8_t i = 0; i < MORSE_PREAMBLE_LENGTH; i++) {
133  morse.startSignal();
135  }
136 
137  // send callsign
138  for(uint8_t i = 0; i < callsignLen - 1; i++) {
139  morse.print(callsign[i]);
141  }
142 
143  // space
144  morse.print(' ');
146 
147  // send battery voltage code
148  char code = 'A' + (uint8_t)((battVoltage - MORSE_BATTERY_MIN) / MORSE_BATTERY_STEP);
149  morse.println(code);
151 }
152 
153 void Communication_CW_Beep(uint32_t len) {
154  radio.transmitDirect();
155  Power_Control_Delay(len, true);
156  radio.standby();
157 }
158 
160  // build response frame
161  static const uint8_t optDataLen = 6*sizeof(uint8_t) + 3*sizeof(int16_t) + sizeof(uint16_t) + sizeof(int8_t) + sizeof(uint32_t);
162  uint8_t optData[optDataLen];
163  uint8_t* optDataPtr = optData;
164 
165  #ifdef ENABLE_INA226
166  uint8_t batteryVoltage = Power_Control_Get_Battery_Voltage() * (VOLTAGE_UNIT / VOLTAGE_MULTIPLIER);
167  #else
168  uint8_t batteryVoltage = 4.02 * (VOLTAGE_UNIT / VOLTAGE_MULTIPLIER);
169  #endif
170  Communication_Frame_Add<uint8_t>(&optDataPtr, batteryVoltage, "batV");
171  Persistent_Storage_Update_Stats<uint8_t>(EEPROM_BATTERY_VOLTAGE_STATS_ADDR, batteryVoltage);
172 
173  #ifdef ENABLE_INA226
174  int16_t batteryChargingCurrent = Power_Control_Get_Charging_Current() * (CURRENT_UNIT / CURRENT_MULTIPLIER);
175  #else
176  int16_t batteryChargingCurrent = 0.056 * (CURRENT_UNIT / CURRENT_MULTIPLIER);
177  #endif
178  Communication_Frame_Add<int16_t>(&optDataPtr, batteryChargingCurrent, "batChI");
179  Persistent_Storage_Update_Stats<int16_t>(EEPROM_CHARGING_CURRENT_STATS_ADDR, batteryChargingCurrent);
180 
181  #ifdef ENABLE_INA226
182  uint8_t batteryChargingVoltage = Power_Control_Get_Charging_Voltage() * (VOLTAGE_UNIT / VOLTAGE_MULTIPLIER);
183  #else
184  uint8_t batteryChargingVoltage = 3.82 * (VOLTAGE_UNIT / VOLTAGE_MULTIPLIER);
185  #endif
186  Communication_Frame_Add<uint8_t>(&optDataPtr, batteryChargingVoltage, "batChV");
187  Persistent_Storage_Update_Stats<uint8_t>(EEPROM_CHARGING_VOLTAGE_STATS_ADDR, batteryChargingVoltage);
188 
189  // set uptimeCounter
190  uint32_t uptimeCounter = Persistent_Storage_Read<uint32_t>(EEPROM_UPTIME_COUNTER_ADDR);
191  Communication_Frame_Add(&optDataPtr, uptimeCounter, "up");
192 
193  // set powerConfig variable
195  FOSSASAT_DEBUG_PRINTLN(powerConfig.val, BIN);
196  memcpy(optDataPtr, &powerConfig.val, sizeof(uint8_t));
197  optDataPtr += sizeof(uint8_t);
198 
199  // set resetCounter variable
200  uint16_t resetCounter = Persistent_Storage_Read<uint16_t>(EEPROM_RESTART_COUNTER_ADDR);
201  Communication_Frame_Add(&optDataPtr, resetCounter, "rst");
202 
203  uint8_t solarCellAVoltage = Pin_Interface_Read_Voltage(ANALOG_IN_SOLAR_A_VOLTAGE_PIN) * (VOLTAGE_UNIT / VOLTAGE_MULTIPLIER);
204  Communication_Frame_Add<uint8_t>(&optDataPtr, solarCellAVoltage, "sAV");
205  Persistent_Storage_Update_Stats<uint8_t>(EEPROM_CELL_A_VOLTAGE_STATS_ADDR, solarCellAVoltage);
206 
207  // set solarCellBVoltage variable
208  uint8_t solarCellBVoltage = Pin_Interface_Read_Voltage(ANALOG_IN_SOLAR_B_VOLTAGE_PIN) * (VOLTAGE_UNIT / VOLTAGE_MULTIPLIER);
209  Communication_Frame_Add<uint8_t>(&optDataPtr, solarCellBVoltage, "sBV");
210  Persistent_Storage_Update_Stats<uint8_t>(EEPROM_CELL_B_VOLTAGE_STATS_ADDR, solarCellBVoltage);
211 
212  // set solarCellCVoltage variable
213  uint8_t solarCellCVoltage = Pin_Interface_Read_Voltage(ANALOG_IN_SOLAR_C_VOLTAGE_PIN) * (VOLTAGE_UNIT / VOLTAGE_MULTIPLIER);
214  Communication_Frame_Add<uint8_t>(&optDataPtr, solarCellCVoltage, "sCV");
215  Persistent_Storage_Update_Stats<uint8_t>(EEPROM_CELL_C_VOLTAGE_STATS_ADDR, solarCellCVoltage);
216 
217  // set batteryTemperature variable
218  int16_t batteryTemperature = Pin_Interface_Read_Temperature(BATTERY_TEMP_SENSOR_ADDR) * (TEMPERATURE_UNIT / TEMPERATURE_MULTIPLIER);
219  Communication_Frame_Add<int16_t>(&optDataPtr, batteryTemperature, "batT");
220  Persistent_Storage_Update_Stats<int16_t>(EEPROM_BATTERY_TEMP_STATS_ADDR, batteryTemperature);
221 
222  // set boardTemperature variable
223  int16_t boardTemperature = Pin_Interface_Read_Temperature(BOARD_TEMP_SENSOR_ADDR) * (TEMPERATURE_UNIT / TEMPERATURE_MULTIPLIER);
224  Communication_Frame_Add<int16_t>(&optDataPtr, boardTemperature, "brdT");
225  Persistent_Storage_Update_Stats<int16_t>(EEPROM_BOARD_TEMP_STATS_ADDR, boardTemperature);
226 
227  // set mcuTemperature variable (read twice since first value is often nonsense)
229  int8_t mcuTemperature = Pin_Interface_Read_Temperature_Internal();
230  Communication_Frame_Add<int8_t>(&optDataPtr, mcuTemperature, "mcuT");
231  Persistent_Storage_Update_Stats<int8_t>(EEPROM_MCU_TEMP_STATS_ADDR, mcuTemperature);
232 
233  // send as raw bytes
234  Communication_Send_Response(RESP_SYSTEM_INFO, optData, optDataLen);
235 }
236 
237 void Communication_Acknowledge(uint8_t functionId, uint8_t result) {
238  uint8_t optData[] = { functionId, result };
239  Communication_Send_Response(RESP_ACKNOWLEDGE, optData, 2);
240 }
241 
243  /*FOSSASAT_DEBUG_PRINT("Communication_Process_Packet ");
244  FOSSASAT_DEBUG_PRINTLN(freeRam());
245  FOSSASAT_DEBUG_DELAY(100);*/
246 
247  // disable interrupts
248  interruptsEnabled = false;
249 
250  // read data
251  size_t len = radio.getPacketLength();
252  if(len == 0) {
253  dataReceived = false;
254  interruptsEnabled = true;
255  return;
256  }
257  uint8_t frame[MAX_RADIO_BUFFER_LENGTH];
258  int16_t state = radio.readData(frame, len);
259 
260  // check reception state
261  if(state == ERR_NONE) {
262  FOSSASAT_DEBUG_PRINT(F("Frm "));
263  FOSSASAT_DEBUG_PRINTLN(len);
264  FOSSASAT_DEBUG_PRINT_BUFF(frame, len);
265 
266  // check callsign
267  uint8_t callsignLen = Persistent_Storage_Read<uint8_t>(EEPROM_CALLSIGN_LEN_ADDR);
268  char callsign[MAX_STRING_LENGTH + 1];
269  System_Info_Get_Callsign(callsign, callsignLen);
270  if(memcmp(frame, (uint8_t*)callsign, callsignLen - 1) == 0) {
271  // check passed
272  Comunication_Parse_Frame(frame, len);
273  } else {
274  FOSSASAT_DEBUG_PRINTLN(F("CErr"));
276  Communication_Acknowledge(0xFF, 0x01);
277  }
278 
279  } else {
280  FOSSASAT_DEBUG_PRINT(F("RxErr "));
281  FOSSASAT_DEBUG_PRINT(state);
283  Communication_Acknowledge(0xFF, 0x02);
284  }
285 
286  // reset flag
287  dataReceived = false;
288 
289  // enable interrupts
290  interruptsEnabled = true;
291 }
292 
293 void Comunication_Parse_Frame(uint8_t* frame, size_t len) {
294  /*FOSSASAT_DEBUG_PRINT("Comunication_Parse_Frame ");
295  FOSSASAT_DEBUG_PRINTLN(freeRam());
296  FOSSASAT_DEBUG_DELAY(100);*/
297 
298  // read callsign from EEPROM
299  uint8_t callsignLen = Persistent_Storage_Read<uint8_t>(EEPROM_CALLSIGN_LEN_ADDR);
300  char callsign[MAX_STRING_LENGTH + 1];
301  System_Info_Get_Callsign(callsign, callsignLen);
302 
303  // get functionID
304  int16_t functionId = FCP_Get_FunctionID(callsign, frame, len);
305  if(functionId < 0) {
306  FOSSASAT_DEBUG_PRINT(F("IdErr"));
307  FOSSASAT_DEBUG_PRINTLN(functionId);
309  Communication_Acknowledge(0xFF, 0x03);
310  return;
311  }
312  FOSSASAT_DEBUG_PRINT(F("FID="));
313  FOSSASAT_DEBUG_PRINTLN(functionId, HEX);
314 
315  // check encryption
316  int16_t optDataLen = 0;
317  uint8_t optData[MAX_OPT_DATA_LENGTH];
318  if((functionId >= PRIVATE_OFFSET) && (functionId <= (PRIVATE_OFFSET + NUM_PRIVATE_COMMANDS))) {
319  // frame contains encrypted data, decrypt
320 
321  // get optional data length
322  optDataLen = FCP_Get_OptData_Length(callsign, frame, len, encryptionKey, password);
323  if(optDataLen < 0) {
324  FOSSASAT_DEBUG_PRINT(F("DcErr"));
325  FOSSASAT_DEBUG_PRINTLN(optDataLen);
326 
327  // decryption failed, increment invalid frame counter and return
329  Communication_Acknowledge(0xFF, 0x04);
330  return;
331  }
332 
333  // get optional data
334  if(optDataLen > 0) {
335  FCP_Get_OptData(callsign, frame, len, optData, encryptionKey, password);
336  }
337 
338  } else if(functionId < PRIVATE_OFFSET) {
339  // no decryption necessary
340 
341  // get optional data length
342  optDataLen = FCP_Get_OptData_Length(callsign, frame, len);
343  if(optDataLen < 0) {
344  // optional data extraction failed,
345  FOSSASAT_DEBUG_PRINT(F("LenErr"));
346  FOSSASAT_DEBUG_PRINTLN(optDataLen);
347 
348  // increment invalid frame counter
350  Communication_Acknowledge(0xFF, 0x05);
351  return;
352  }
353 
354  // get optional data
355  if(optDataLen > 0) {
356  FCP_Get_OptData(callsign, frame, len, optData);
357  }
358  } else {
359  // unknown function ID
360  FOSSASAT_DEBUG_PRINT(F("UID"));
361  FOSSASAT_DEBUG_PRINTLN(functionId, HEX);
363  Communication_Acknowledge(0xFF, 0x06);
364  return;
365  }
366 
367  // check optional data presence
368  if(optDataLen > 0) {
369  // execute with optional data
370  FOSSASAT_DEBUG_PRINT(F("optLen="));
371  FOSSASAT_DEBUG_PRINTLN(optDataLen);
372  FOSSASAT_DEBUG_PRINT_BUFF(optData, (uint8_t)optDataLen);
373  Communication_Execute_Function(functionId, optData, optDataLen);
374 
375  } else {
376  // execute without optional data
377  Communication_Execute_Function(functionId);
378  }
379 }
380 
381 void Communication_Execute_Function(uint8_t functionId, uint8_t* optData, size_t optDataLen) {
382  /*FOSSASAT_DEBUG_PRINT("Communication_Execute_Function ");
383  FOSSASAT_DEBUG_PRINTLN(freeRam());
384  FOSSASAT_DEBUG_DELAY(100);*/
385 
386  // increment valid frame counter
388 
389  // acknowledge frame
390  Communication_Acknowledge(functionId, 0x00);
391 
392  // execute function based on ID
393  switch(functionId) {
394  case CMD_PING:
395  // send pong
396  Communication_Send_Response(RESP_PONG);
397  break;
398 
399  case CMD_RETRANSMIT: {
400  // check message length
401  if (optDataLen <= MAX_STRING_LENGTH) {
402  // respond with the requested data
403  Communication_Send_Response(RESP_REPEATED_MESSAGE, optData, optDataLen);
404  }
405  } break;
406 
407  case CMD_RETRANSMIT_CUSTOM: {
408  // check message length
409  if((optDataLen >= 8) && (optDataLen <= MAX_STRING_LENGTH + 7)) {
410  // change modem configuration
411  int16_t state = Communication_Set_Configuration(optData, optDataLen);
412 
413  // check if the change was successful
414  if(state != ERR_NONE) {
415  FOSSASAT_DEBUG_PRINT(F("CfgErr"));
416  FOSSASAT_DEBUG_PRINTLN(state);
417  } else {
418  // configuration changed successfully, transmit response
419  Communication_Send_Response(RESP_REPEATED_MESSAGE_CUSTOM, optData + 7, optDataLen - 7, true);
420  }
421  }
422  } break;
423 
424  case CMD_TRANSMIT_SYSTEM_INFO:
425  // send system info via LoRa
427  break;
428 
429  case CMD_GET_PACKET_INFO: {
430  // get last packet info and send it
431  static const uint8_t respOptDataLen = 2*sizeof(uint8_t) + 4*sizeof(uint16_t);
432  uint8_t respOptData[respOptDataLen];
433  uint8_t* respOptDataPtr = respOptData;
434 
435  // SNR
436  int8_t snr = (int8_t)(radio.getSNR() * 4.0);
437  Communication_Frame_Add(&respOptDataPtr, snr, "SNR");
438 
439  // RSSI
440  uint8_t rssi = (uint8_t)(radio.getRSSI() * -2.0);
441  Communication_Frame_Add(&respOptDataPtr, rssi, "RSSI");
442 
443  uint16_t loraValid = Persistent_Storage_Read<uint16_t>(EEPROM_LORA_VALID_COUNTER_ADDR);
444  Communication_Frame_Add(&respOptDataPtr, loraValid, "Lv");
445 
446  uint16_t loraInvalid = Persistent_Storage_Read<uint16_t>(EEPROM_LORA_INVALID_COUNTER_ADDR);
447  Communication_Frame_Add(&respOptDataPtr, loraInvalid, "Li");
448 
449  uint16_t fskValid = Persistent_Storage_Read<uint16_t>(EEPROM_FSK_VALID_COUNTER_ADDR);
450  Communication_Frame_Add(&respOptDataPtr, fskValid, "Fv");
451 
452  uint16_t fskInvalid = Persistent_Storage_Read<uint16_t>(EEPROM_FSK_INVALID_COUNTER_ADDR);
453  Communication_Frame_Add(&respOptDataPtr, fskInvalid, "Fi");
454 
455  Communication_Send_Response(RESP_PACKET_INFO, respOptData, respOptDataLen);
456  } break;
457 
458  case CMD_GET_STATISTICS: {
459  // check optional data is exactly 1 byte
460  if(Communication_Check_OptDataLen(1, optDataLen)) {
461  // response will have maximum of 34 bytes if all stats are included
462  uint8_t respOptData[34];
463  uint8_t respOptDataLen = 1;
464  uint8_t* respOptDataPtr = respOptData;
465 
466  // copy stat flags
467  uint8_t flags = optData[0];
468  memcpy(respOptDataPtr, &flags, sizeof(uint8_t));
469  respOptDataPtr += sizeof(uint8_t);
470 
471  // get required stats from EEPROM
472  if(flags & 0x01) {
473  // charging voltage
474  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<uint8_t>(EEPROM_CHARGING_VOLTAGE_STATS_ADDR), "");
475  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<uint8_t>(EEPROM_CHARGING_VOLTAGE_STATS_ADDR + 1), "");
476  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<uint8_t>(EEPROM_CHARGING_VOLTAGE_STATS_ADDR + 2), "");
477  respOptDataLen += 3;
478  }
479 
480  if(flags& 0x02) {
481  // charging current
482  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<int16_t>(EEPROM_CHARGING_CURRENT_STATS_ADDR), "");
483  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<int16_t>(EEPROM_CHARGING_CURRENT_STATS_ADDR + 2), "");
484  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<int16_t>(EEPROM_CHARGING_CURRENT_STATS_ADDR + 4), "");
485  respOptDataLen += 6;
486  }
487 
488  if(flags & 0x04) {
489  // battery voltage
490  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<uint8_t>(EEPROM_BATTERY_VOLTAGE_STATS_ADDR), "");
491  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<uint8_t>(EEPROM_BATTERY_VOLTAGE_STATS_ADDR + 1), "");
492  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<uint8_t>(EEPROM_BATTERY_VOLTAGE_STATS_ADDR + 2), "");
493  respOptDataLen += 3;
494  }
495 
496  if(flags & 0x08) {
497  // cell A voltage
498  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<uint8_t>(EEPROM_CELL_A_VOLTAGE_STATS_ADDR), "");
499  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<uint8_t>(EEPROM_CELL_A_VOLTAGE_STATS_ADDR + 1), "");
500  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<uint8_t>(EEPROM_CELL_A_VOLTAGE_STATS_ADDR + 2), "");
501  respOptDataLen += 3;
502  }
503 
504  if(flags & 0x10) {
505  // cell B voltage
506  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<uint8_t>(EEPROM_CELL_B_VOLTAGE_STATS_ADDR), "");
507  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<uint8_t>(EEPROM_CELL_B_VOLTAGE_STATS_ADDR + 1), "");
508  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<uint8_t>(EEPROM_CELL_B_VOLTAGE_STATS_ADDR + 2), "");
509  respOptDataLen += 3;
510  }
511 
512  if(flags & 0x20) {
513  // cell C voltage
514  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<uint8_t>(EEPROM_CELL_C_VOLTAGE_STATS_ADDR), "");
515  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<uint8_t>(EEPROM_CELL_C_VOLTAGE_STATS_ADDR + 1), "");
516  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<uint8_t>(EEPROM_CELL_C_VOLTAGE_STATS_ADDR + 2), "");
517  respOptDataLen += 3;
518  }
519 
520  if(flags & 0x40) {
521  // battery temperature
522  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<int16_t>(EEPROM_BATTERY_TEMP_STATS_ADDR), "");
523  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<int16_t>(EEPROM_BATTERY_TEMP_STATS_ADDR + 2), "");
524  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<int16_t>(EEPROM_BATTERY_TEMP_STATS_ADDR + 4), "");
525  respOptDataLen += 6;
526  }
527 
528  if(flags & 0x80) {
529  // board temperature
530  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<int16_t>(EEPROM_BOARD_TEMP_STATS_ADDR), "");
531  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<int16_t>(EEPROM_BOARD_TEMP_STATS_ADDR + 2), "");
532  Communication_Frame_Add(&respOptDataPtr, Persistent_Storage_Read<int16_t>(EEPROM_BOARD_TEMP_STATS_ADDR + 4), "");
533  respOptDataLen += 6;
534  }
535 
536  // send response
537  Communication_Send_Response(RESP_STATISTICS, respOptData, respOptDataLen);
538  }
539  } break;
540 
541  // private function IDs
542  case CMD_DEPLOY: {
543  // run deployment sequence
545 
546  // get deployment counter value and send it
547  uint8_t counter = Persistent_Storage_Read<uint8_t>(EEPROM_DEPLOYMENT_COUNTER_ADDR);
548  Communication_Send_Response(RESP_DEPLOYMENT_STATE, &counter, 1);
549  } break;
550 
551  case CMD_RESTART:
552  // restart satellite
554  break;
555 
556  case CMD_WIPE_EEPROM:
557  // wipe EEPROM and reset all EEPROM variables to default values
559  break;
560 
561  case CMD_SET_TRANSMIT_ENABLE: {
562  // check optional data is exactly 1 byte
563  if(Communication_Check_OptDataLen(1, optDataLen)) {
564  // load power config from EEPROM
566 
567  // update transmit enable flag
568  powerConfig.bits.transmitEnabled = optData[0];
569 
570  // save power config from EEPROM
572  }
573  } break;
574 
575  case CMD_SET_CALLSIGN: {
576  // check optional data is less than limit
577  if(optDataLen <= MAX_STRING_LENGTH) {
578  // get callsign from frame
579  char newCallsign[MAX_STRING_LENGTH + 1];
580  memcpy(newCallsign, optData, optDataLen);
581  newCallsign[optDataLen] = '\0';
582 
583  // update callsign
584  System_Info_Set_Callsign(newCallsign);
585  FOSSASAT_DEBUG_PRINTLN(newCallsign);
586  }
587  } break;
588 
589  case CMD_SET_SF_MODE: {
590  // check optional data is exactly 1 byte
591  if(Communication_Check_OptDataLen(1, optDataLen)) {
592  // update spreading factor mode
593  spreadingFactorMode = optData[0];
595  }
596  } break;
597 
598  case CMD_SET_MPPT_MODE: {
599  // check optional data is exactly 2 bytes
600  if(Communication_Check_OptDataLen(2, optDataLen)) {
601  // load power config from EEPROM
603 
604  // update MPPT mode
605  powerConfig.bits.mpptTempSwitchEnabled = optData[0];
606  powerConfig.bits.mpptKeepAliveEnabled = optData[1];
607 
608  // save power config from EEPROM
610  }
611  } break;
612 
613  case CMD_SET_LOW_POWER_ENABLE: {
614  // check optional data is exactly 1 byte
615  if(Communication_Check_OptDataLen(1, optDataLen)) {
616  // load power config from EEPROM
618 
619  // update low power enable flag
620  powerConfig.bits.lowPowerModeEnabled = optData[0];
621 
622  // save power config from EEPROM
624  }
625  } break;
626 
627  case CMD_SET_RECEIVE_WINDOWS: {
628  // check optional data is exactly 2 bytes
629  if(Communication_Check_OptDataLen(2, optDataLen)) {
630  // set FSK receive length
631  Persistent_Storage_Write<uint8_t>(EEPROM_FSK_RECEIVE_LEN_ADDR, optData[0]);
632 
633  // set LoRa receive length
634  Persistent_Storage_Write<uint8_t>(EEPROM_LORA_RECEIVE_LEN_ADDR, optData[1]);
635 
636  // check if there will be still some receive window open
637  if((Persistent_Storage_Read<uint8_t>(EEPROM_LORA_RECEIVE_LEN_ADDR) == 0) && (Persistent_Storage_Read<uint8_t>(EEPROM_FSK_RECEIVE_LEN_ADDR) == 0)) {
638  FOSSASAT_DEBUG_PRINT(F("Res FSK"));
639  Persistent_Storage_Write<uint8_t>(EEPROM_FSK_RECEIVE_LEN_ADDR, FSK_RECEIVE_WINDOW_LENGTH);
640  }
641  }
642  } break;
643 
644  case CMD_RECORD_SOLAR_CELLS: {
645  // check optional data is exactly 3 bytes
646  if(Communication_Check_OptDataLen(3, optDataLen)) {
647  uint16_t numSamples = optData[0];
648 
649  // check number of samples is less than limit
650  if(numSamples > 40) {
651  FOSSASAT_DEBUG_PRINT(F(">40"));
652  break;
653  }
654 
655  // get sample period
656  uint16_t period = 0;
657  memcpy(&period, optData + 1, 2);
658  FOSSASAT_DEBUG_PRINT(F("Rec"));
659 
660  // record all data
661  uint8_t respoOptDataLen = 3 * numSamples;
662  uint8_t respOptData[MAX_OPT_DATA_LENGTH];
663  for(uint16_t i = 0; i < 3 * numSamples; i += 3) {
664  // check if the battery is good enough to continue
665  #ifdef ENABLE_INTERVAL_CONTROL
667  // battery check failed, stop measurement and send what we have
668  respoOptDataLen = i;
669  break;
670  }
671  #endif
672 
673  // read voltages
674  respOptData[i] = Pin_Interface_Read_Voltage(ANALOG_IN_SOLAR_A_VOLTAGE_PIN) * (VOLTAGE_UNIT / VOLTAGE_MULTIPLIER);
675  respOptData[i + 1] = Pin_Interface_Read_Voltage(ANALOG_IN_SOLAR_B_VOLTAGE_PIN) * (VOLTAGE_UNIT / VOLTAGE_MULTIPLIER);
676  respOptData[i + 2] = Pin_Interface_Read_Voltage(ANALOG_IN_SOLAR_C_VOLTAGE_PIN) * (VOLTAGE_UNIT / VOLTAGE_MULTIPLIER);
677 
678  // wait for for the next measurement
679  Power_Control_Delay(period * SLEEP_LENGTH_CONSTANT, true, true);
680  }
681 
682  // send response
683  Communication_Send_Response(RESP_RECORDED_SOLAR_CELLS, respOptData, respoOptDataLen);
684  }
685  } break;
686 
687  case CMD_ROUTE:
688  // just transmit the optional data
689  Communication_Transmit(optData, optDataLen);
690  break;
691  }
692 }
693 
694 int16_t Communication_Send_Response(uint8_t respId, uint8_t* optData, size_t optDataLen, bool overrideModem) {
695  /*FOSSASAT_DEBUG_PRINT("Communication_Send_Response ");
696  FOSSASAT_DEBUG_PRINTLN(freeRam());
697  FOSSASAT_DEBUG_DELAY(100);*/
698 
699  // get callsign from EEPROM
700  uint8_t callsignLen = Persistent_Storage_Read<uint8_t>(EEPROM_CALLSIGN_LEN_ADDR);
701  char callsign[MAX_STRING_LENGTH + 1];
702  System_Info_Get_Callsign(callsign, callsignLen);
703 
704  // build response frame
705  uint8_t len = FCP_Get_Frame_Length(callsign, optDataLen);
706  uint8_t frame[MAX_RADIO_BUFFER_LENGTH];
707  FCP_Encode(frame, callsign, respId, optDataLen, optData);
708 
709  // delay before responding
710  FOSSASAT_DEBUG_DELAY(100);
712 
713  // send response
714  int16_t state = Communication_Transmit(frame, len, overrideModem);
715 
716  return(state);
717 }
718 
719 int16_t Communication_Transmit(uint8_t* data, uint8_t len, bool overrideModem) {
720  /*FOSSASAT_DEBUG_PRINT("Communication_Transmit ");
721  FOSSASAT_DEBUG_PRINTLN(freeRam());
722  FOSSASAT_DEBUG_DELAY(100);*/
723 
724  // check transmit enable flag
725  #ifdef ENABLE_TRANSMISSION_CONTROL
727  if(!powerConfig.bits.transmitEnabled) {
728  FOSSASAT_DEBUG_PRINTLN(F("Tx 0 cmd"));
729  return(ERR_TX_TIMEOUT);
730  }
731  #endif
732 
733  // disable receive interrupt
734  radio.clearDio1Action();
735 
736  // print frame for debugging
737  FOSSASAT_DEBUG_PRINT(F("Send "));
738  FOSSASAT_DEBUG_PRINTLN(len);
739  FOSSASAT_DEBUG_PRINT_BUFF(data, len);
740 
741  // check if modem should be switched - required for transmissions with custom settings
742  uint8_t modem = currentModem;
743  FOSSASAT_DEBUG_PRINT(F("Use "));
744  if(overrideModem) {
745  FOSSASAT_DEBUG_WRITE(MODEM_LORA);
746  FOSSASAT_DEBUG_PRINTLN(F(" (ovr)"));
747  Communication_Set_Modem(MODEM_LORA);
748  } else {
749  FOSSASAT_DEBUG_WRITE(modem);
750  FOSSASAT_DEBUG_PRINTLN();
751  }
752 
753  // get timeout
754  uint32_t timeout = 0;
755  if(currentModem == MODEM_FSK) {
756  timeout = (float)radio.getTimeOnAir(len) * 5.0;
757  } else {
758  timeout = (float)radio.getTimeOnAir(len) * 1.5;
759  }
760  FOSSASAT_DEBUG_PRINT(F("T/O="));
761  FOSSASAT_DEBUG_PRINTLN(timeout);
762 
763  // start transmitting
764  int16_t state = radio.startTransmit(data, len);
765  if(state != ERR_NONE) {
766  FOSSASAT_DEBUG_PRINT(F("TxErr"));
767  FOSSASAT_DEBUG_PRINTLN(state);
768  return(state);
769  }
770 
771  // wait for transmission finish
772  uint32_t start = micros();
773  uint32_t lastBeat = 0;
774  while(!digitalRead(RADIO_DIO1)) {
775  // pet watchdog every second
776  if(micros() - lastBeat > (uint32_t)WATCHDOG_LOOP_HEARTBEAT_PERIOD * (uint32_t)1000) {
778 
779  // check whether voltage dropped below low power level
780  #ifdef ENABLE_INTERVAL_CONTROL
781  if(powerConfig.bits.lowPowerModeActive) {
782  // we're below low power level, stop the transmission
783  FOSSASAT_DEBUG_PRINTLN(F("Tx 0 bat"));
784  radio.standby();
785  return(ERR_INVALID_DATA_RATE);
786  }
787  #endif
788 
789  lastBeat = micros();
790  }
791 
792  // check timeout
793  if(micros() - start > timeout) {
794  // timed out while transmitting
795  radio.standby();
797  FOSSASAT_DEBUG_PRINTLN(F("Tx t/o"));
798  return(ERR_TX_TIMEOUT);
799  }
800  }
801 
802  // transmission done, set mode standby
803  state = radio.standby();
804 
805  // restore modem
806  if(overrideModem) {
808  }
809 
810  // set receive ISR
811  radio.setDio1Action(Communication_Receive_Interrupt);
812 
813  return(state);
814 }
815 
816 bool Communication_Check_OptDataLen(uint8_t expected, uint8_t actual) {
817  if(expected != actual) {
818  // received length of optional data does not match expected
819  return(false);
820  }
821 
822  return(true);
823 }
#define FSK_CARRIER_FREQUENCY
void System_Info_Get_Callsign(char *buff, uint8_t len)
Gets the callsign which is current set in EEPROM.
Definition: system_info.cpp:21
This system is the main interface that is used to transmit message, configure the radio and process r...
#define LORA_BANDWIDTH
#define EEPROM_LORA_INVALID_COUNTER_ADDR
Start AddressEnd Address 0x000E0x000F
bool Communication_Check_OptDataLen(uint8_t expected, uint8_t actual)
Helper functions to check two variables are equal, with debug prints.
#define LORA_CURRENT_LIMIT
void Power_Control_Delay(uint32_t ms, bool sleep, bool sleepRadio=false)
This function delays the program execution for the given number of milliseconds, while maintaining th...
#define EEPROM_FSK_RECEIVE_LEN_ADDR
Start AddressEnd Address 0x00050x0005
#define EEPROM_MCU_TEMP_STATS_ADDR
Start AddressEnd Address 0x00610x0063
void Communication_Send_System_Info()
Send the satellite&#39;s information via the configured radio settings.
#define RESPONSE_DELAY
#define BOARD_TEMP_SENSOR_ADDR
#define EEPROM_FSK_VALID_COUNTER_ADDR
Start AddressEnd Address 0x00100x0011
#define TCXO_VOLTAGE
#define EEPROM_LORA_RECEIVE_LEN_ADDR
Start AddressEnd Address 0x00060x0006
#define WHITENING_INITIAL
#define EEPROM_UPTIME_COUNTER_ADDR
Start AddressEnd Address 0x00070x000A
#define EEPROM_CALLSIGN_LEN_ADDR
Start AddressEnd Address 0x000140x00014
uint8_t spreadingFactorMode
void Deployment_Deploy()
This function deploys the antenna by powering the MOSFET1/2 for 1200 seconds.
Definition: deployment.cpp:3
bool Power_Control_Check_Battery_Limit()
Checks whether battery voltage is below low power limit. Will enable low power mode if that is the ca...
#define EEPROM_CHARGING_VOLTAGE_STATS_ADDR
Start AddressEnd Address 0x00400x0042
#define SLEEP_LENGTH_CONSTANT
Definition: configuration.h:77
void Pin_Interface_Watchdog_Heartbeat(bool manageBattery=false)
This function toggles the signal to the watchdog and writes it to the pin.
#define FSK_FREQUENCY_DEVIATION
#define FSK_OUTPUT_POWER
#define EEPROM_CELL_B_VOLTAGE_STATS_ADDR
Start AddressEnd Address 0x004F0x0051
#define EEPROM_CELL_A_VOLTAGE_STATS_ADDR
Start AddressEnd Address 0x004C0x004E
float Power_Control_Get_Charging_Current()
Gets the amperage which is being provided to the battery.
#define FSK_RX_BANDWIDTH
float Pin_Interface_Read_Temperature(uint8_t sensorAddr)
This function reads the TMP100&#39;s value from its wire address.
#define MAX_RADIO_BUFFER_LENGTH
Definition: configuration.h:31
void Communication_Acknowledge(uint8_t functionId, uint8_t result)
This function sends acknowledge for a received frame.
void Power_Control_Save_Configuration()
Saves the configuration bytes from RAM into EEPROM.
float Power_Control_Get_Battery_Voltage()
Get the battery voltage by switching the MPPT off and then on again after reading is taken...
#define FSK_BIT_RATE
const uint8_t encryptionKey[]
void Power_Control_Load_Configuration()
Load the configuration bytes from the EEPROM into RAM.
#define MORSE_BATTERY_STEP
#define EEPROM_BOARD_TEMP_STATS_ADDR
Start AddressEnd Address 0x005B0x0060
volatile bool interruptsEnabled
int16_t Communication_Set_Modem(uint8_t modem)
This function configures the radio to the given modem.
#define MORSE_SPEED
#define LORA_CARRIER_FREQUENCY
int16_t Communication_Set_Configuration(uint8_t *optData, uint8_t optDataLen)
This function sets the configuration of the radio, which is used to Radio.Begin().
int16_t Communication_Transmit(uint8_t *data, uint8_t len, bool overrideModem=true)
Transmits the given data.
#define LORA_PREAMBLE_LENGTH
void Communication_Receive_Interrupt()
This function is called by the ISR when a transmission is received.
float Pin_Interface_Read_Voltage(uint8_t pin)
Read the voltage of a given pin.
#define EEPROM_BATTERY_TEMP_STATS_ADDR
Start AddressEnd Address 0x00550x005A
uint8_t currentModem
#define ANALOG_IN_SOLAR_C_VOLTAGE_PIN
#define LORA_CODING_RATE
powerConfig_t powerConfig
The current power configuration settings.
#define EEPROM_FSK_INVALID_COUNTER_ADDR
Start AddressEnd Address 0x00120x0013
void Communication_Execute_Function(uint8_t functionId, uint8_t *optData=NULL, size_t optDataLen=0)
This function executes the given function id provided with the given data.
void Communication_CW_Beep(uint32_t len)
This function transmits a continous wave "BEEP"".
void Comunication_Parse_Frame(uint8_t *frame, size_t len)
This function parses the internal contents of the message using the FOSSA COMMS Protocol.
int16_t Communication_Send_Response(uint8_t respId, uint8_t *optData=nullptr, size_t optDataLen=0, bool overrideModem=false)
Responds to a given function id execution (internally used).
void Pin_Interface_Watchdog_Restart()
Restarts the watchdog.
void Communication_Send_Morse_Beacon(float battVoltage)
This function transmits a morse beacon message.
#define FSK_CURRENT_LIMIT
#define SYNC_WORD
float Power_Control_Get_Charging_Voltage()
Gets the charging voltage (from the solar panels) by not switching the MPPT off and taking an INA226 ...
void Communication_Process_Packet()
This function reads the contents of the radio when it receives a transmission.
#define EEPROM_CELL_C_VOLTAGE_STATS_ADDR
Start AddressEnd Address 0x00520x0054
#define WATCHDOG_LOOP_HEARTBEAT_PERIOD
Definition: configuration.h:75
#define RADIO_DIO1
void Communication_Frame_Add(uint8_t **buffPtr, T val, const char *name)
This function adds frame entry to a frame.
Definition: communication.h:81
#define EEPROM_LORA_VALID_COUNTER_ADDR
Start AddressEnd Address 0x000C0x000D
#define FSK_PREAMBLE_LENGTH
void Persistent_Storage_Wipe()
This functions clears the EEPROM by writing EEPROM_RESET_VALUE to each memory addres.
#define MAX_STRING_LENGTH
Definition: configuration.h:29
#define EEPROM_DEPLOYMENT_COUNTER_ADDR
Start AddressEnd Address 0x00000x0000
#define MORSE_BATTERY_MIN
void System_Info_Set_Callsign(char *newCallsign)
Set the callsign to be used for each transmission to EEPROM.
Definition: system_info.cpp:3
#define BATTERY_TEMP_SENSOR_ADDR
#define ANALOG_IN_SOLAR_B_VOLTAGE_PIN
#define FSK_RECEIVE_WINDOW_LENGTH
int16_t Communication_Set_SpreadingFactor(uint8_t sfMode)
This function sets the spreading factor of the radio.
volatile bool dataReceived
void Persistent_Storage_Increment_Frame_Counter(bool valid)
This functions increments one of frame counters of the currently active modem in EEPROM.
int8_t Pin_Interface_Read_Temperature_Internal()
This function reads the MCU&#39;s internal temperature.
#define FSK_DATA_SHAPING
#define MAX_OPT_DATA_LENGTH
Definition: configuration.h:30
#define EEPROM_CHARGING_CURRENT_STATS_ADDR
Start AddressEnd Address 0x00430x0048
#define EEPROM_BATTERY_VOLTAGE_STATS_ADDR
Start AddressEnd Address 0x00490x004B
#define ANALOG_IN_SOLAR_A_VOLTAGE_PIN
#define EEPROM_RESTART_COUNTER_ADDR
Start AddressEnd Address 0x00030x0004
const char * password
MorseClient morse
#define MORSE_PREAMBLE_LENGTH
SX1268 radio
#define LORA_OUTPUT_POWER