Skip to content

Instantly share code, notes, and snippets.

@assyrianic
Last active March 21, 2024 03:49
Show Gist options
  • Save assyrianic/0d7381c2d00414adc2338e7b626f96d6 to your computer and use it in GitHub Desktop.
Save assyrianic/0d7381c2d00414adc2338e7b626f96d6 to your computer and use it in GitHub Desktop.
Flight Data Recorder arduino code for the Glendale Community College [Arizona] course ECE294 aka NASA ASCEND Project.
/*
Code runs on the Flight DataF"Modem
Recorder board
12/10/2023
Fdr.cpp Version number added.
The version contains experimental code that should permit this .cpp code to print the path of the associated .ino file
*/
#define reduceMenu //saves program store
//#define DiagPrint1
//#define DiagPrint12
//#define gpstest
//#define nearFarTest
//#define pingTest
//#define rejectedData
//#define print1
//#define runOnceTest
//#define newGPScodeDiag
//#define GNSSprint22
//#define DiagPrint47
//#define DiagPrint477
//#define GNSSprint
//#define GNSSprint
//#define pointerTest
//#define debugParceTime
//#define ebugLoopAround
//#define ModemStatusTesting
//#define modemNGtest
//#define gpsRunTimeStudy
#include "Arduino.h"
#include "Fdr.h"
#include <Wire.h> //I2C interface library
unsigned int GPSbaudRateUInt = 9600;//baud rate of GPS defaults to 9600. Change this value if your GPS is at another rate. If I short pins 13 and 14 together, baud goes up to 38400
//#define RealtimeClockCalibration; //leave this defined to prevent EEPROM init problems
float versionFloat = 9.03; //Fdr version is 7. Version 1 was not numbered. In version 2, the library version number prints out when H is entered by user. Version 5 contains changes from Kevin. Version 6 contains bug fix to inhibit loop time warning when data dumped out the USB. Version 8 contains bug fix for invalid Iridium() command plus rework of GPS code. Version 9 has new modem command, statuUInt which returns the current returnCode so the user can decide when to send the next command. When the modem is idle, it will return ModemReadyForUseUInt. 9.03 fixed bug where a bad modem does not cause Modem to print so I never get Modem NG. Changed to print Modem OK, fails setup, or fails ping.
#define DataBlockSize 16 //size of one block of sensor data. 9.01 modem setup wasn't running due to //out too many lines. Changed version number to a float so I can have decimal.
#define EEPROM_ADR_LOW_BLOCK 0x50 //FDR's EEPROM 0
#define EEPROM_ADR_HIGH_BLOCK 0x54 //FDR's EEPROM 0
#define SEEPROM_ADR_LOW_BLOCK 0x51 //Student's EEPROM 1 4.4
#define SEEPROM_ADR_HIGH_BLOCK 0x55 //Student's EEPROM 1 4.4
#define FailureRangeMinUInt 1U
#define FailureRangeMaxUInt 299U
#define StatusRangeMinUInt 300U
#define StatusRangeMaxUInt 399U
#define SuccessRangeMinUInt 400U//includes ping and modem set up
#define TransmitSuccessRangeMinUInt 402U//only includes transmit related successes
#define SuccessRangeMaxUInt 499U
#define MinimumTransmitCycleTimeSecondsUInt 60U
/***********************************************
MODEM COMMANDS
***********************************************/
#define PingUInt 0U
#define SetUpModemUInt 1U
#define PerformTransmitUInt 2U
#define getReceivedDataUInt 3U
#define SendBackFirstBlockOfReceivedArrayUInt 4U
#define SendBackSecondBlockOfReceivedArrayUInt 5U
#define setLoopAroundUInt 6U//added 2/2/2024
#define clearLoopAroundUint 7U//added 2/2/2024
#define statusUInt 8U //returns the current returnCode
/***********************************************/
#define PingThroughMPM_AndModemSuccessUInt 400U
#define ModemReadyForUseUInt 401U
#define TransmitSuccessfulAndNoReceiveUInt 402U
#define TransmitAndReceiveSuccessfulUInt 403U
#define TransmitAndReceiveSuccessfulPlusReceivePendingUInt 404U
#define dataLoopAroundEnabledUInt 405U//added 2/2/2024
#define dataLoopAroundDisabledUInt 406U //added 2/2/2024
#define receiveDataPlacedInReceiveArrayUint 407U
/***********************************************/
#define idleUInt 311U
#define SentPerformTransmitUInt 323U
#define SentgetReceivedDataUInt 324U
#define AboutToStartTransmitProcessUInt 326U
#define InitialReturnCodeValueUInt 328U
#define InitialMPM_ResponseValueUInt 329U
#define BusySettingUpModemUInt 330U
#define PerformingPingUInt 331U
#define MPM_Busy_TransmitCommandPendingUInt 334U
#define ModemSetupProceedingUInt 335U
#define ModemDefaultsSetUInt 336U
#define SentPingUInt 337U
#define SBD_MessageSuccessfullyWrittenUInt 338U
#define MT_MessagePendingUInt 339U
#define MT_MessagesPendingUInt 340U
/***********************************************/
#define ModemStatus_TimedOutUInt 101U
#define InvalidCommandUInt 120U
#define PingToMPM_SuccessButToModemFailedUInt 276U
#define PingToMPM_TimedOutUInt 275U
#define TimeOutWaitingForgetReceivedDataUInt 279U
#define NoPingMPM_BusyUInt 280U
#define ModemFailedAtSetupUInt 281U
#define UnexpectedResponseFromModemDuringSetupUInt 282U
#define ModemSetupFailedBecauseMPM_BusyUInt 283U
#define ModemFailedAtSetupTimeOutUInt 284U
#define MPM_BusyWhenFDR_AskedForSetupUInt 285U
#define MPM_DidNotRespondToRequestForDataUInt 286U
#define SoftwareError1UInt 287U
#define MPM_BusyUInt 288U
#define AskedForPingResultTooSoon_DoPingAgainUInt 289U
#define PingToMPM_DidNotRespondUInt 290U
#define WrongModemConnectedCheckSerialNumberUInt 291U
#define RequestedTransmitTooSoonUInt 292U
#define NoFunctioningModemPresentUInt 293U
#define TimeOutAfterSendingMessageUInt 294U
#define SBD_MessageTimeOutByModemUInt 295U
#define SBD_MessageChecksumWrongUInt 296U
#define SBD_MessageSizeWrongUInt 297U
#define UnexpectedResponseAfterWritingDataToMobileOriginatedBufferUInt 298U
#define SBD_MessageSizeTooBigOrTooSmallUInt 299U
Fdr::Fdr(){ //Constructor. This is similar to setup() but is in the library and only refers to its own class.
OneTimeRunBool = true;
ErrorCodeAddressByte = 10;//diag
//I need to init this array: LogicalAnalogPinByte[8]={8,7,10,0,1,2,3,9};
LogicalAnalogPinByte[0]= 8;
LogicalAnalogPinByte[1]= 7;
LogicalAnalogPinByte[2]= 10;
LogicalAnalogPinByte[3]= 0;
LogicalAnalogPinByte[4]= 1;
LogicalAnalogPinByte[5]= 2;
LogicalAnalogPinByte[6]= 3;
LogicalAnalogPinByte[7]= 9;
pinMode(ExternalLED, OUTPUT);
pinMode(RunCamControl, OUTPUT); //4.3
Wire.begin(); //set up communications as FDR
Wire.setClock(400000); //set clock rate for EEPROMs and Modem Pro Micro (MPM)
Serial.begin(19200); //set up communications path between Arduino and USB; specify data rate
//set all analog ports to inputs
pinMode(A10, INPUT);
pinMode(A0, INPUT);
pinMode(A1, INPUT);
pinMode(A2, INPUT);
pinMode(A3, INPUT);
pinMode(A9, INPUT);
/* port references
Port0 is A8
Port1 is A7
Port2 is A10
Port3 is A0
Port4 is A1
Port5 is A2
Port6 is A3
Port7 is A9
*/
//I_Two_C_BusAvailableQ(); //verify bus is not held by MPM
//diag to test corrupted control block
//ZeroControlBlock();
//diag to test corrupted time calibration value detection
//SetIllegalTimerValue();
//check if control block has any out of range values. If so, initialize and tell user to power cycle.
}//end of Fdr::Fdr()
void Fdr::Pointers(intptr_t *PointerArray){ //Kevin suggested changing data type from int to intptr_t so it correctly holds addresses that may not be integers
/******************************************
LIBRARY VARIABLES TO LINK TO USER'S VARIABLES
*******************************************/
IridiumTransmitDataByte = reinterpret_cast< byte(*) [45] > (PointerArray[0]);//IridiumTransmitDataByte points to the same address as the user's IridiumTransmitData[0]
IridiumReceiveDataByte = reinterpret_cast< byte(*)[45] > (PointerArray[1]);
NavigationDataByte = reinterpret_cast< byte(*)[16] > (PointerArray[2]);
GNSS_Bool = reinterpret_cast< bool* > (PointerArray[3]);//GNSS_Bool points to the same address as the user's GNSS
Modem_Bool = reinterpret_cast< bool* > (PointerArray[4]);//PointerArray[4] holds the address of Modem defined in FDR.ino. I use it toset the address of Modem_Bool
#ifdef pointerTest
delay(2000);//time for Terra Term to stabilize
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
Serial.print(F("Modem_Bool = "));
byte modemBoolTranslatedToByte = 1;
if(*Modem_Bool == false)modemBoolTranslatedToByte = 0;
Serial.println(modemBoolTranslatedToByte);//print can't handle printing of bool so convert to byte
Serial.print(F("GNSS_Bool = "));
byte gpsBoolTranslatedToByte = 1;
if(*GNSS_Bool == false)gpsBoolTranslatedToByte = 0;
Serial.println(gpsBoolTranslatedToByte);//print can't handle printing of bool so convert to byte
#endif
AutomaticVideoRecordAtPowerUpBool = reinterpret_cast< bool* >(PointerArray[5]);
filePathChar = reinterpret_cast< char* >(PointerArray[6]); //added 11/7/2023 and later modified
nearFarModemSNbyte = reinterpret_cast< byte* > (PointerArray[7]);//from Kevin
#ifdef nearFarTest
delay(2000);
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
Serial.print(F("nearFarModemSNbyte = "));
Serial.println(*nearFarModemSNbyte);
#endif
#ifdef gpstest
delay(2000);
byte translateBoolToByte = 3;
if(*GNSS_Bool){
translateBoolToByte = 1;
}else{
translateBoolToByte = 0;
}
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
Serial.print(F("GNSS bool = "));
Serial.println(translateBoolToByte);
#endif
}
void Fdr::PutInLoop(){
LoopCycleTimeMonitor();//generates one message if it takes more than LoopCycleTimeLimitMS_UInt for one cycle. No monitoring for first 30 seconds to allow for hardware start up delays RGS1.5
LapseTimerSeconds(); //only advance time if ControlTheFlightParameters() has run due to power up. Otherwise, time will advance before variables that depend on being initialized have been set. CR2.0
/* modem command timing no longer done; modem will complain if driven too fast
if(*Modem_Bool){//I removed the * from in front of Modem to be consistent with all other uses
ModemTransmitTiming();//RGS1.0
}
*/
if(*GNSS_Bool){
#ifdef pointerTest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
//delay(2000);
#endif
GNSS_Timing();//not currently used
}
RefreshTimeCalibrationConstantQ();
LED_Cadences();
OnTheGround(); //tasks performed on the ground or during simulated flight
InTheAir(); //tasks mostly done in the air but can be done on the ground during testing
}
void Fdr::OnTheGround(){
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
#endif
ControlTheFlightParameters();
#ifdef DiagPrint12
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
#endif
ScanForCommand();
#ifdef DiagPrint12
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
#endif
OutputMenuQ(); //M command
#ifdef DiagPrint12
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
#endif
SingleDisplayDataQ(); //D command
PrepareForLaunchQ(); //P command
ContinuousDisplayDataQ(); //A command CR1.7
OutputDataFileQ(); //O command
StopCollectingDataQ(); //S command
#ifdef RealtimeClockCalibration
CalibrateClockQ(); //C command
#endif
DiagnoticOutputControlBlockQ(); //H command
GenerateTestPatternQ(); //T command
StudentDefinedMenuQ();//R command
if(IridiumModemPresentBool){
PingModemQ();//I command
}
FlashLED();
#ifdef DiagPrint12
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
#endif
}
void Fdr::InTheAir(){
#ifdef DiagPrint9
ErrorCodeByte = ReadEEPROM_ControlBlock(ErrorCodeAddressByte);
if (ErrorCodeByte != OldErrorCodeByte){
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ErrorCodeByte = "));
Serial.println( ErrorCodeByte );
OldErrorCodeByte = ErrorCodeByte;
}
#endif
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" JustPoweredUpToBeUsedByRunCamFlag = "));
Serial.println( JustPoweredUpToBeUsedByRunCamFlag );
#endif
if(*AutomaticVideoRecordAtPowerUpBool){
VideoRecordAtPowerUp();
}
if(JustPoweredUpFlag == true)return; //if we just powered up, data recording parameters have not yet been initialized. This is done in ControlTheFlightParameters () which is within OnTheGround() CR2.0
#ifdef DiagPrint9
ErrorCodeByte = ReadEEPROM_ControlBlock(ErrorCodeAddressByte);
if (ErrorCodeByte != OldErrorCodeByte)
{
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ErrorCodeByte = "));
Serial.println( ErrorCodeByte );
OldErrorCodeByte = ErrorCodeByte;
}
#endif
DataIn(); //record, process, and save time and sensor data to EEPROM
#ifdef DiagPrint9
ErrorCodeByte = ReadEEPROM_ControlBlock(ErrorCodeAddressByte);
if (ErrorCodeByte != OldErrorCodeByte)
{
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ErrorCodeByte = "));
Serial.println( ErrorCodeByte );
OldErrorCodeByte = ErrorCodeByte;
}
#endif
#ifdef GNSStest
GlobalNavigationSatelliteSystem();//output is *(*NavigationDataByte)[ ]. Results can be stored in SEEPROM. If no GNSS device is connected, *(*NavigationDataByte)[ ] will have an error flag. RGS1.0
#endif
#ifdef GNSStest
PrintOutGNSS_Array();//diag RGS1.0
#endif
/* this function no longer needed since modem will return error codes if requests come too often
if(*Modem_Bool){
IridiumModemSatelliteSystem();//only operational if the correct modem is connected and enough time has transpired since last successful transmit. RGS1.0
}
*/
#ifdef nearFarTest
ErrorCodeByte = ReadEEPROM_ControlBlock(ErrorCodeAddressByte);
if (ErrorCodeByte != OldErrorCodeByte)
{
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ErrorCodeByte = "));
Serial.println( ErrorCodeByte );
OldErrorCodeByte = ErrorCodeByte;
}
#endif
}//end of InTheAir()
/********************************************************************************
L E V E L 2 S U B R O U T I N E S
*********************************************************************************/
void Fdr::RefreshTimeCalibrationConstantQ(){
if ((ReadEEPROM_ControlBlock(ProgramStateAddressByte ) <TestForInFlightByte )){ //then we are in flight and it is worth refreshing the calibration constant in case it was corrupted
OneSecondMillisecondsUnsignedLong = ReadEEPROM_ControlBlock(OneSecondMillisecondsBaseAddressByte) + (ReadEEPROM_ControlBlock(OneSecondMillisecondsBaseAddressByte + 1)*256); //assemble this constant from bytes in the control block at the start of each cycle of loop () rather than each time I need it
}
}
void Fdr::ControlTheFlightParameters()
{
#ifdef DiagPrint12
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
#endif
if (JustPoweredUpFlag == true){ //executes only when power first comes up
JustPoweredUpFlag = false;
StartTimeMS_UnsignedLong = millis();// application of power initializes the Start Time to the current value of the real time clock.
#ifdef DiagPrint12
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
#endif
if (ReadEEPROM_ControlBlock(ProgramStateAddressByte ) == PreFlightByte)
{ //if ReadyForLaunch true, set InFlightFlag state
#ifdef DiagPrint12
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
#endif
WriteEEPROM_ControlBlock(ProgramStateAddressByte,InFlightByte);//RGS1.2
//verify writes worked
if (ReadEEPROM_ControlBlock(ProgramStateAddressByte) != InFlightByte){
#ifdef DiagPrint12
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
#endif
WriteEEPROM_ControlBlock(ErrorCodeAddressByte, ControlTheFlightParametersReadyForLaunchReadBackFailureFlagByte); //RGS1.2
}
#ifdef DiagPrint12
ErrorCodeByte = ReadEEPROM_ControlBlock(ErrorCodeAddressByte);
if (ErrorCodeByte != OldErrorCodeByte)
{
Serial.print(F("ControlTheFlightParameters(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ErrorCodeByte = "));
Serial.println( ErrorCodeByte );
OldErrorCodeByte = ErrorCodeByte;
}
#endif
return;
}
#ifdef DiagPrint12
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
#endif
if (ReadEEPROM_ControlBlock(ProgramStateAddressByte) == InFlightByte){//if InFlight, set state to DisruptedPower.
#ifdef DiagPrint12
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
#endif
WriteEEPROM_ControlBlock(ProgramStateAddressByte, InFlightWithPowerDisruptionByte);//RGS1.2
#ifdef DiagPrint12
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
#endif
//verify writes worked
if (ReadEEPROM_ControlBlock(ProgramStateAddressByte) != InFlightWithPowerDisruptionByte){
#ifdef DiagPrint12
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
#endif
WriteEEPROM_ControlBlock(ErrorCodeAddressByte, ControlTheFlightParametersInFlightFlagReadBackFailureFlagByte);//RGS1.2
}
#ifdef DiagPrint12
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
#endif
return;
}
#ifdef DiagPrint12
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
#endif
}
#ifdef DiagPrint12
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
#endif
}//end of ControlTheFlightParameters()
void Fdr::LoopCycleTimeMonitor()//alarms once when loop() cycle time is too long which effects code interfacing in real time. Starts monitoring 30 seconds after power up. If loop is held up because we are waiting for a user response, no warning is generated.
{
//Serial.print(" LoopCycleTimeMonitor ");//diag
if ((millis() - StartForTimeStampULong)< 30000)return;//don't monitor cycle time for first 30 seconds to give time for one time time delays to run.
if(LoopCycleTimeWarningRaisedBool)return;//prevents flood of warnings
if(resetLoopTimerBool){//user has held up the loop so don't generate software warning. Reset loop timer.
LastLoopTimeStampMS_UInt = millis();//reset loop timer
resetLoopTimerBool = false;//clear loop timer reset flag
return;
}
LoopCycleTimeMS_UInt = millis() - LastLoopTimeStampMS_UInt;
if(LoopCycleTimeMS_UInt > LoopCycleTimeLimitMS_UInt)
{
Serial.print(F("Excessive loop() cycle time "));
Serial.print (millis());
Serial.println(F(" ms since power up."));
Serial.print(F("Cycle time was "));
Serial.print (LoopCycleTimeMS_UInt);
Serial.println(F(" ms."));
LoopCycleTimeWarningRaisedBool = true;
}
LastLoopTimeStampMS_UInt = millis();
}//end of LoopCycleTimeMonitor()
void Fdr::LapseTimerSeconds()
{
if (JustPoweredUpFlag == true)
{
#ifdef DiagPrint9
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
return; //do not advance time if we just powered up because OnTheGround()//ControlTheFlightParameters() has not run yet to initialize related parameters
}
TimeNowSecondsInt = int((millis() - StartTimeMS_UnsignedLong)/OneSecondMillisecondsUnsignedLong); //millis() must always be larger than StartTimeMS_UnsignedLong which was set to millis() previously so I should never have the case where two unsigned longs produce a negative number
if ((TimeNowSecondsInt - Last_TimeNow_SecondsInt) > (SamplingRateSecondsByte - 1))
{ //we have to advance SamplingRateSecondsByte second in order to say we have gone SamplingRateSecondsByte seconds since last reading of ports. CR3.2
TimeToTakeSampleBool = true; //set seconds flag if we just started new second
//TimeToTakeSampleBool set false just before ports scanned
Last_TimeNow_SecondsInt = TimeNowSecondsInt;
}
//if TimeNowSeconds is equal to LastTimeNowSeconds, there has not been an advanced to the next second so do nothing
}//end of LapseTimerSeconds()
void Fdr::LED_Cadences()
{
InFlightCadence(); //1 flash
DisruptedPowerCadence(); //2 flashes
MemoryLockedCadence(); //3 flashes
PreFlightCadence(); //4 flashes
FaultCadence(); //5 flashes
}
void Fdr::ScanForCommand()
{
//detect if laptop connected. If so, check for incoming command. If there is one, set flags for requested tasks
if (Serial.available() > 0)
{ //if there is at least one character in the buffer, process first one
Command = Serial.read();
if ((Command != 'D') && (Command != 'd')&&(Command != 'A') && (Command != 'a')&&(Command != 'C') && (Command != 'c')&&(Command != 'S') && (Command != 's')&& (Command != 'P') && (Command != 'p')&&(Command != 'O')&&(Command != 'o')&&(Command != 'H')&&(Command != 'h')&&(Command != 'T')&&(Command != 't')&&(Command != 'R')&&(Command != 'r'))
{
Command = 'M'; //map any character except D, A, C, S, P, O, H, T, and R to "M" for display of Menu. Set to "N" when done processing command.
PrintLine();
}
if ((Command == 'c')||(Command == 'C'))//if time calibration turned off, C will map to 'M' RGS1.5
{
Command = 'M';
PrintLine();
}
if (Command == '\n')
{
Command = 'M';
PrintLine();//RGS1.2
}
}
}
#ifdef RealtimeClockCalibration
void Fdr::CalibrateClockQ()
{ //CR2.0
DeepReturnBool = false; //when set true by a lower level subroutine, we will return from CalibrateClockQ()
C_CommandQ();
if (DeepReturnBool == true)return;
while (Serial.available()==0)FlickerLEDforCalibration(); // waiting for Abort or G command; replace goto CR1.9
if (Serial.available() > 0) Command = Serial.read(); //we have a character so save it to the variable Command
SerialAvailableResponseErrorCheck(); //if Serial.available() returns a negative number, it is a fault
//first command was a C. Now have a second command which will be either A,G, or neither
FirstAbortCommandQ();
if (DeepReturnBool == true)return;
//to get here, command cannot be A
G_CommandQ();
if (DeepReturnBool == true)return; //command was not G or g
//to get here, command must be G or g
Command = 'N'; //clear command back to null
PrintCalibrationStartedText();
StartOfCalibrationTimeMS_UnsignedLong = millis(); //record start in ms.
//calibration interval just started. Watch for end or abort command
EndOfCalibration(); //responds to commands I, A, E.
}
void Fdr::C_CommandQ()
{
if ((Command == 'C') || (Command == 'c')){
Command = 'N'; //clear Command back to null
PrintCalibrationIntroText();
}else{
DeepReturnBool = true; //causes return from CalibrateClockQ()
return;
} //if command is "C", wait for user response
}
#ifdef RealtimeClockCalibration
void Fdr::FirstAbortCommandQ(){
if ((Command == 'A') || (Command == 'a')){
Command = 'N'; //clear Command back to null
A_CommandText();
DeepReturnBool = true; //causes return from CalibrateClockQ()
return;
}
}
#endif
#endif
void Fdr::G_CommandQ()
{
if ((Command != 'G')&&(Command != 'g')){//command is not G so tell user and abort
Command = 'N'; //clear Command back to null
InvalidCommandText();
DeepReturnBool = true; //causes return from CalibrateClockQ()
return; //false means not G or g
}
}
#ifdef RealtimeClockCalibration
void Fdr::PrintCalibrationIntroText()
{
PrintLine();//RGS1.2
Serial.println(F("Clock Calibration Function"));
Serial.println();
Serial.println(F("This task takes 3 hours to run. Do not proceed unless"));
Serial.println(F("you can complete the job"));
Serial.println();
Serial.println(F("Type 'A' now to Abort"));
Serial.println();
//Serial.println(F("Use https://time.is/ for the best timing source."));
Serial.println();
Serial.println(F("When ready, type 'G'"));
Serial.println();
}
void Fdr::FlickerLEDforCalibration()
{
digitalWrite(ExternalLED, ExternalLED_OnByte); //turn external LED on
delay(50);
digitalWrite(ExternalLED, ExternalLED_OffByte);//turn external LED off
delay(50);
}
#endif
void Fdr::SerialAvailableResponseErrorCheck()
{
if (Serial.available() < 0) {
WriteEEPROM_ControlBlock(ErrorCodeAddressByte,SerialAvailableReturnCodeOutOfRangeByte); //added error check and generate error code if return value invalid RGS1.2
//Serial.print(F(" USB error detected"));
}
}
void Fdr::PrintCalibrationStartedText()
{
Serial.println(F("G"));
Serial.println();
Serial.println(F("Time calibration has started"));
Serial.println(F("Type 'E' after exactly 3 hours to End or 'A' to Abort"));
Serial.println(F("'I' to initialize the timer constant to 1000"));
}
#ifdef RealtimeClockCalibration
void Fdr::CalibrationUserStatus(){
while(Serial.available() == 0){ //while no characters…
DurationMS_UnsignedLong = millis() - StartOfCalibrationTimeMS_UnsignedLong;
if ((DurationMS_UnsignedLong % 1000) == 0)CalibrationLEDFlicker();//at every second flicker LED once
if ((DurationMS_UnsignedLong != 0)&&(DurationMS_UnsignedLong % 60000) == 0){//every minute after start
Serial.print(F("."));
if ((DurationMS_UnsignedLong != 0)&&(DurationMS_UnsignedLong % 3600000 == 0)){//every hour after start
Serial.println();
Serial.println(F("1 hour has passed"));
}
}
}
}
#endif
#ifdef RealtimeClockCalibration
void Fdr::CalibrationLEDFlicker(){
digitalWrite(ExternalLED, ExternalLED_OnByte); //turn external LED on
delay(100);
digitalWrite(ExternalLED, ExternalLED_OffByte); //turn external LED off
}
void Fdr::I_CommandText(){
Command = 'N'; //clear command back to null
Serial.println(F("I"));
Serial.println();
Serial.println(F("This will initialize the timer constant"));
Serial.println(F("Do this only if the Pro Micro or EEPROM are changed out"));
Serial.println(F("Proceed? Y/N"));
}
#endif
#ifdef RealtimeClockCalibration
void Fdr::A_CommandText(){
PrintLine();//RGS1.2
Serial.println(F("Clock calibration aborted."));
}
#endif
#ifdef RealtimeClockCalibration
void Fdr::InvalidCommandText(){
Serial.println(F("Neither A (abort) or G (go) received."));
Serial.println();
Serial.println(F("Clock calibration aborted."));
}
#endif
void Fdr::StopCollectingDataQ(){
if ((Command == 'S')||(Command == 's')){
Serial.println();
Serial.println();
#ifndef reduceMenu
Serial.println(F("Data collection plus camera recording has stopped and the EEPROM is locked.")); //RGS1.2
#endif
#ifdef reduceMenu
Serial.println(F("stopped"));
#endif
Serial.println();
WriteEEPROM_ControlBlock(ProgramStateAddressByte,MemoryLockedByte);//RGS1.2
GoToVideoStandby();//this assumes the camera is recording because there is no way to verify the state. Given camera is recording and shutter button is pushed, it should stop recording. RGS1.5
Command = 'N'; //clear Command
}
}
#ifdef RealtimeClockCalibration
void Fdr::EndOfCalibration(){
FoundCommandQ = false;
while (FoundCommandQ == false){ //CR1.9
while(Serial.available() == 0)CalibrationUserStatus(); //while no characters coming in from USB, display progress with LED and on screen
//We fell out of the above while loop so have a character
SerialAvailableResponseErrorCheck(); //if Serial.available() returns a negative number, it is a fault
if (Serial.available() > 0) Command = Serial.read(); //have Command while in calibration interval
//see if valid
EndOfCalibrationCommandValidityCheck();
//have new command and it is A, E, or I.
}//if no valid command found, keep looking; otherwise, new command found so exit while()
IandY_CommandQ(); //abort 3 hour timer calibration and set constant to 1000 if Yes is input; otherwise keep timing.
//evaluate the received Command
if (DeepReturnBool == true)return; //I and Y seen so abort calibration
A_CommandQ(); //abort timer calibration
if (DeepReturnBool == true)return;
E_Command(); //marks end of 3 hour timer interval so save resulting calibration constant. No "Q" because it has to be E.
}
#ifdef RealtimeClockCalibration
void Fdr::EndOfCalibrationCommandValidityCheck(){
if ((Command != 'A')&&(Command != 'E') && (Command != 'a')&&(Command != 'e') && (Command != 'I')&&(Command != 'i')){ //then it is not valid
Serial.print(Command);
Command = 'N'; //clear command back to null
Serial.println(F(" is not valid. Enter E, A, or I"));
//command not valid so leave FoundCommandQ at false and try again
}else{
FoundCommandQ = true;//command is valid so stop looking
}
}
#endif
void Fdr::IandY_CommandQ(){
if ((Command == 'I')||(Command == 'i')){
Command = 'N'; //clear command back to null
I_CommandText();
FoundCommandQ = false; //now looking for yes or anything else which will be read as no
while (FoundCommandQ == false){ //CR1.9
SerialAvailableResponseErrorCheck(); //if Serial.available() returns a negative number, it is a fault
if (Serial.available() > 0) { //read a response
Command = Serial.read();
FoundCommandQ = true;
}
}
Y_CommandQ(); //if yes, calibration constant is set to the nominal value. if no, we continue to look for Abort or End
if (DeepReturnBool == true)return; //if calibration constant was set to the nominal value, stop measuring timing interval
}
}
#ifdef RealtimeClockCalibration
void Fdr::Y_CommandQ(){
if ((Command == 'Y')||(Command == 'y')){
Serial.println(F("Time correction constant has been set to nominal"));
SetNominalTimerValue();
DeepReturnBool = true; //do return from calling subroutine because there is no need to continue timing
}else{ //if not Y or y, take it as a no and abort the setting of the time correction value to 1000 plus keep timing
Serial.println(F("Time correction constant initialization has been aborted. 3 hour interval still going"));
}
}
#endif
void Fdr::SetNominalTimerValue(){//this is still used to init control block
OneSecondMillisecondsUnsignedLong = 1000L;//nominal value
WriteEEPROM_ControlBlock(OneSecondMillisecondsBaseAddressByte, lowByte(OneSecondMillisecondsUnsignedLong)); //save to EEPROM RGS1.2
WriteEEPROM_ControlBlock(OneSecondMillisecondsBaseAddressByte+1, highByte(OneSecondMillisecondsUnsignedLong)); //save to EEPROM with write subroutine that allows write to timer constant. all other write subrotines do not. RGS1.2
}
void Fdr::SetIllegalTimerValue(){//diag RGS1.2
//out of range value
WriteEEPROM_ControlBlock(OneSecondMillisecondsBaseAddressByte, lowByte(500L)); //save to EEPROM
WriteEEPROM_ControlBlock(OneSecondMillisecondsBaseAddressByte+1, highByte(500L)); //save to EEPROM with write subroutine that allows write to timer constant. all other write subrotines do not.
}
#ifdef RealtimeClockCalibration
void Fdr::A_CommandQ(){
if ((Command == 'A')||(Command == 'a')){
Serial.println(F("A"));
Serial.println();
Command = 'N'; //clear command back to null
Serial.println(F("Clock calibration aborted"));
DeepReturnBool = true; //do return from calling subroutine
return;
}
}
#endif
#ifdef RealtimeClockCalibration
void Fdr::E_Command(){ //command must be E so no need to check for it
Serial.println(F("E"));
Serial.println();
Command = 'N'; //clear command back to null
Serial.println(F("Time Calibration interval has ended"));
CalculateAndFormatCalibrationValue();
//range test calibration value and tell user
LimitTestFloat = float(OneSecondMillisecondsUnsignedLong)/1000L;
if ((LimitTestFloat < 0.9) || (LimitTestFloat > 1.1)){
Serial.println();
Serial.println(F("Calibration value outside expected limits so rejected"));
return; //don't need to do DeepReturnBool because E_CommandQ() is the last line of the calling subroutine
}
//to get here, calibration must be within 10% of nominal
SaveTimerCalibrationValueToEEPROM();
TellUserAboutTimerCalibrationValue();
}
#endif
#ifdef RealtimeClockCalibration
void Fdr::CalculateAndFormatCalibrationValue(){
DurationMS_UnsignedLong = millis() - StartOfCalibrationTimeMS_UnsignedLong; //now have count of internal milliseconds per second
OneSecondMillisecondsUnsignedLong = DurationMS_UnsignedLong/IdealCalibrationTimeSeconds_UnsignedLong; //calculate calibration value
}
#endif
#ifdef RealtimeClockCalibration
void Fdr::SaveTimerCalibrationValueToEEPROM(){
WriteEEPROM_ControlBlock(OneSecondMillisecondsBaseAddressByte, lowByte(OneSecondMillisecondsUnsignedLong)); //save to EEPROM
WriteEEPROM_ControlBlock(OneSecondMillisecondsBaseAddressByte+1, highByte(OneSecondMillisecondsUnsignedLong)); //save to EEPROM with write subrotine that alows write to timer constant. all other write subrotines do not.
}
#endif
#ifdef RealtimeClockCalibration
void Fdr::TellUserAboutTimerCalibrationValue(){
Serial.println(F("Timer calibration done"));
Serial.print(F("Calibration showed internal clock is off by "));
OneSecondMillisecondsFloat = float(OneSecondMillisecondsUnsignedLong); //convert long to float; not clear why I did this since it is not used in this subrotine
ErrorPercentageFloat = 100*(LimitTestFloat -1);
Serial.print(ErrorPercentageFloat, 4);//display to 4 places past decimal
Serial.println(F("%"));
Serial.println();
}
#endif
#endif
void Fdr::OutputDataFileQ(){
if ((Command == 'O')|| (Command == 'o')){ //Output data to file on laptop
OutputDataToFile();
Command = 'N'; //clear Command
}
}
void Fdr::DiagnoticOutputControlBlockQ(){
if ((Command == 'H')|| (Command == 'h')){ //Output control block
DiagnoticOutputControlBlock();
Command = 'N'; //clear Command
}
}
void Fdr::SingleDisplayDataQ(){
if ((Command == 'D')|| (Command == 'd') ){//Display battery voltage and all port voltages
OutputSingleLiveScan();
Command = 'N'; //clear Command
}
}
void Fdr::ContinuousDisplayDataQ(){ //Sets flag to display ports 0 through 6 voltages as they are read CR1.7
if ((Command == 'A')|| (Command == 'a') ){
ContinuousDisplayDataFlag = true; //flag used by DataIn; flag cleared by power cycle
OutputContinuousDisplayDataStateChange(); //tell user they will see port readings as they are collected
Command = 'N'; //clear Command
}
}
void Fdr::OutputMenuQ(){
if ((Command == 'M')||(Command == 'm') ){
OutputMenuOfCommandsAndLED_Cadences();
Command = 'N'; //clear Command
}
}
void Fdr::FlashLED(){
if (ReadEEPROM_ControlBlock(ErrorCodeAddressByte) != 0)
{
if (FaultLED_CadenceFlag == true){
digitalWrite(ExternalLED, ExternalLED_OnByte); //turn external LED on
}else{
digitalWrite(ExternalLED, ExternalLED_OffByte);//turn external LED off
}
return; //fault cadence supersedes all others
}
if(ReadEEPROM_ControlBlock(ProgramStateAddressByte) == PreFlightByte ){ //in pre-flight state
if (PreFlightLED_CadenceFlag == true){
digitalWrite(ExternalLED, ExternalLED_OnByte); //turn external LED on
}else{
digitalWrite(ExternalLED, ExternalLED_OffByte);//turn external LED off
}
return;
}
if (ReadEEPROM_ControlBlock(ProgramStateAddressByte) == MemoryLockedByte){
if (MemoryLockedLED_CadenceFlag == true){
digitalWrite(ExternalLED, ExternalLED_OnByte); //turn external LED on
}else{
digitalWrite(ExternalLED, ExternalLED_OffByte);//turn external LED off
}
return;
}
if (ReadEEPROM_ControlBlock(ProgramStateAddressByte) == InFlightByte){
if (InFlightLED_CadenceFlag == true){
digitalWrite(ExternalLED, ExternalLED_OnByte); //turn external LED on
}else{
digitalWrite(ExternalLED, ExternalLED_OffByte);//turn external LED off
}
return;
}
if (ReadEEPROM_ControlBlock(ProgramStateAddressByte) == InFlightWithPowerDisruptionByte){
if (DisruptedPowerLED_CadenceFlag == true){
digitalWrite(ExternalLED, ExternalLED_OnByte); //turn external LED on
}else{
digitalWrite(ExternalLED, ExternalLED_OffByte);//turn external LED off
}
return;
}
}
void Fdr::PrepareForLaunchQ(){
if ((Command == 'P') || (Command == 'p')){
SendPrepareForLaunchWarning();
ActOnResponseToPrepareForLaunch();
Command = 'N'; //clear Command
}
}
void Fdr::GenerateTestPatternQ(){//CR3.1
if ((Command == 'T') || (Command == 't')){
GenerateTestPatternWarning();
ActOnResponseToGenerateTestPattern();
Command = 'N'; //clear Command
}
}
void Fdr::PingModemQ()
{
if ((Command == 'I') || (Command == 'i'))
{
ReturnCodeUInt = Iridium(PingUInt);
if(ReturnCodeUInt == PingThroughMPM_AndModemSuccessUInt)
{
Serial.println();
Serial.println(F("Modem is operational."));
}else{
Serial.println();
Serial.println(F("Modem is not operational."));
}
Command = 'N'; //clear Command
}
}
void Fdr::DataIn()
{
if (ReadEEPROM_ControlBlock(ProgramStateAddressByte)== MemoryLockedByte)return;
if (ReadEEPROM_ControlBlock(ProgramStateAddressByte) <TestForInFlightByte )
{
//if true, we are in flight RGS1.1
#ifdef DiagPrint9
ErrorCodeByte = ReadEEPROM_ControlBlock(ErrorCodeAddressByte);
if (ErrorCodeByte != OldErrorCodeByte)
{
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ErrorCodeByte = "));
Serial.println( ErrorCodeByte );
OldErrorCodeByte = ErrorCodeByte;
}
#endif
if(TimeToTakeSampleBool == true)
{
//if true, it is time to take a sample from all ports except port 6. We will use the peak for port 6.
#ifdef DiagPrint9
ErrorCodeByte = ReadEEPROM_ControlBlock(ErrorCodeAddressByte);
if (ErrorCodeByte != OldErrorCodeByte)
{
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ErrorCodeByte = "));
Serial.println( ErrorCodeByte );
OldErrorCodeByte = ErrorCodeByte;
}
#endif
RecordData();
#ifdef DiagPrint9
ErrorCodeByte = ReadEEPROM_ControlBlock(ErrorCodeAddressByte);
if (ErrorCodeByte != OldErrorCodeByte)
{
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ErrorCodeByte = "));
Serial.println( ErrorCodeByte );
OldErrorCodeByte = ErrorCodeByte;
}
#endif
}else{
//if not time to take samples, take a reading from port 6 and see if it is a peak
Port6Peak();
#ifdef DiagPrint9
ErrorCodeByte = ReadEEPROM_ControlBlock(ErrorCodeAddressByte);
if (ErrorCodeByte != OldErrorCodeByte)
{
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ErrorCodeByte = "));
Serial.println( ErrorCodeByte );
OldErrorCodeByte = ErrorCodeByte;
}
#endif
}
}
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
#ifdef DiagPrint9
ErrorCodeByte = ReadEEPROM_ControlBlock(ErrorCodeAddressByte);
if (ErrorCodeByte != OldErrorCodeByte)
{
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ErrorCodeByte = "));
Serial.println( ErrorCodeByte );
OldErrorCodeByte = ErrorCodeByte;
}
#endif
}//end of DataIn()
/********************************************************************************
L E V E L 3 S U B R O U T I N E S
*********************************************************************************/
void Fdr::RecordData(){
#ifdef DiagPrint6
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
#ifdef DiagPrint9
ErrorCodeByte = ReadEEPROM_ControlBlock(ErrorCodeAddressByte);
if (ErrorCodeByte != OldErrorCodeByte)
{
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ErrorCodeByte = "));
Serial.println( ErrorCodeByte );
OldErrorCodeByte = ErrorCodeByte;
}
#endif
TimeToTakeSampleBool = false;
ReadEEPROM_Pointer(CalledByRecordDataByte);//updates StartOfNextMemoryBlockLong
#ifdef DiagPrint9
ErrorCodeByte = ReadEEPROM_ControlBlock(ErrorCodeAddressByte);
if (ErrorCodeByte != OldErrorCodeByte)
{
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ErrorCodeByte = "));
Serial.println( ErrorCodeByte );
OldErrorCodeByte = ErrorCodeByte;
}
#endif
RecordPortReadings();
#ifdef DiagPrint9
ErrorCodeByte = ReadEEPROM_ControlBlock(ErrorCodeAddressByte);
if (ErrorCodeByte != OldErrorCodeByte)
{
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ErrorCodeByte = "));
Serial.println( ErrorCodeByte );
OldErrorCodeByte = ErrorCodeByte;
}
#endif
IncrementEEPROM_Pointer();//it reads the pointer in the EEPROM and increments it by the block size but does not write it back into the EEPROM
#ifdef DiagPrint9
ErrorCodeByte = ReadEEPROM_ControlBlock(ErrorCodeAddressByte);
if (ErrorCodeByte != OldErrorCodeByte)
{
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ErrorCodeByte = "));
Serial.println( ErrorCodeByte );
OldErrorCodeByte = ErrorCodeByte;
}
#endif
WriteEEPROM_Pointer(); //writes StartOfNextMemoryBlockLong
#ifdef DiagPrint9
ErrorCodeByte = ReadEEPROM_ControlBlock(ErrorCodeAddressByte);
if (ErrorCodeByte != OldErrorCodeByte)
{
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ErrorCodeByte = "));
Serial.println( ErrorCodeByte );
OldErrorCodeByte = ErrorCodeByte;
}
#endif
}//end of RecordData()
void Fdr::PreFlightCadence(){ //4 flasher per cycle
PreFlightCadenceIntervalMS_Int = int(millis() - StartOfPreFlightCadenceMS_UnsignedLong);
if ((PreFlightCadenceIntervalMS_Int > 0) && (PreFlightCadenceIntervalMS_Int < 500)){
PreFlightLED_CadenceFlag = true;
return;
}
if ((PreFlightCadenceIntervalMS_Int > 500) && (PreFlightCadenceIntervalMS_Int < 1000)){
PreFlightLED_CadenceFlag = false;
return;
}
if ((PreFlightCadenceIntervalMS_Int > 1000) && (PreFlightCadenceIntervalMS_Int < 1500)){
PreFlightLED_CadenceFlag = true;
return;
}
if ((PreFlightCadenceIntervalMS_Int > 1500) && (PreFlightCadenceIntervalMS_Int < 2000)){
PreFlightLED_CadenceFlag = false;
return;
}
if ((PreFlightCadenceIntervalMS_Int > 2000) && (PreFlightCadenceIntervalMS_Int < 2500)){
PreFlightLED_CadenceFlag = true;
return;
}
if ((PreFlightCadenceIntervalMS_Int > 2500) && (PreFlightCadenceIntervalMS_Int < 3000)){
PreFlightLED_CadenceFlag = false;
return;
}
if ((PreFlightCadenceIntervalMS_Int > 3000) && (PreFlightCadenceIntervalMS_Int < 3500)){
PreFlightLED_CadenceFlag = true;
return;
}
if ((PreFlightCadenceIntervalMS_Int > 3500) && (PreFlightCadenceIntervalMS_Int < 4000)){
PreFlightLED_CadenceFlag = false;
return;
}
if (PreFlightCadenceIntervalMS_Int > 9000) {
StartOfPreFlightCadenceMS_UnsignedLong = millis(); //restart timer
return;
}
}
void Fdr::InFlightCadence(){ //1 pulse per cycle
InFlightCadenceIntervalMS_Int = int(millis() - StartOfInFlightCadenceMS_ULong);
if ((InFlightCadenceIntervalMS_Int > 0) && (InFlightCadenceIntervalMS_Int < 500)){
if(BatteryOnQ() == true)
{
InFlightLED_CadenceFlag = true;//normal flashing if battery is on
}else{
InFlightLED_CadenceFlag = false;//inverted flashing if battery is off
}
return;
}
if ((InFlightCadenceIntervalMS_Int > 500) && (InFlightCadenceIntervalMS_Int < 1000)){
if(BatteryOnQ() == true)
{
InFlightLED_CadenceFlag = false;//normal flashing if battery is on
}else{
InFlightLED_CadenceFlag = true;//inverted flashing if battery is off
}
return;
}
if (InFlightCadenceIntervalMS_Int > 5500) {
StartOfInFlightCadenceMS_ULong = millis(); //restart timer
return;
}
}
void Fdr::DisruptedPowerCadence(){ //2 pulses per cycle
DisruptedPowerCadenceIntervalMS_Int = int(millis() - StartOfDisruptedPowerCadenceMS_UnsignedLong);
if ((DisruptedPowerCadenceIntervalMS_Int > 0) && (DisruptedPowerCadenceIntervalMS_Int < 500)){
if(BatteryOnQ() == true)
{
DisruptedPowerLED_CadenceFlag = true;//normal flashing if battery is on
}else{
DisruptedPowerLED_CadenceFlag = false;//inverted flashing if battery is off
}
return;
}
if ((DisruptedPowerCadenceIntervalMS_Int > 500) && (DisruptedPowerCadenceIntervalMS_Int < 1000)){
if(BatteryOnQ() == true)
{
DisruptedPowerLED_CadenceFlag = false;//normal flashing if battery is on
}else{
DisruptedPowerLED_CadenceFlag = true;//inverted flashing if battery is off
}
return;
}
if ((DisruptedPowerCadenceIntervalMS_Int > 1000) && (DisruptedPowerCadenceIntervalMS_Int < 1500)){
if(BatteryOnQ() == true)
{
DisruptedPowerLED_CadenceFlag = true;//normal flashing if battery is on
}else{
DisruptedPowerLED_CadenceFlag = false;//inverted flashing if battery is off
}
return;
}
if ((DisruptedPowerCadenceIntervalMS_Int > 1500) && (DisruptedPowerCadenceIntervalMS_Int < 2000)){
if(BatteryOnQ() == true)
{
DisruptedPowerLED_CadenceFlag = false;//normal flashing if battery is on
}else{
DisruptedPowerLED_CadenceFlag = true;//inverted flashing if battery is off
}
return;
}
if (DisruptedPowerCadenceIntervalMS_Int > 7000) {
StartOfDisruptedPowerCadenceMS_UnsignedLong = millis(); //restart timer
return;
}
}
void Fdr::MemoryLockedCadence(){ //3 pulses per cycle
MemoryLockedCadenceIntervalMS_Int = int(millis() - StartOfMemoryLockedLED_CadenceMS_UnsignedLong); //added int() to convert from unsignedlong CR2.0
if ((MemoryLockedCadenceIntervalMS_Int > 0) && (MemoryLockedCadenceIntervalMS_Int < 500)){
if(BatteryOnQ() == true)
{
MemoryLockedLED_CadenceFlag = true;//normal flashing if battery is on
}else{
MemoryLockedLED_CadenceFlag = false;//inverted flashing if battery is off
}
return;
}
if ((MemoryLockedCadenceIntervalMS_Int > 500) && (MemoryLockedCadenceIntervalMS_Int < 1000)){
if(BatteryOnQ() == true)
{
MemoryLockedLED_CadenceFlag = false;//normal flashing if battery is on
}else{
MemoryLockedLED_CadenceFlag = true;//inverted flashing if battery is off
}
return;
}
if ((MemoryLockedCadenceIntervalMS_Int > 1000) && (MemoryLockedCadenceIntervalMS_Int < 1500)){
if(BatteryOnQ() == true)
{
MemoryLockedLED_CadenceFlag = true;//normal flashing if battery is on
}else{
MemoryLockedLED_CadenceFlag = false;//inverted flashing if battery is off
}
return;
}
if ((MemoryLockedCadenceIntervalMS_Int > 1500) && (MemoryLockedCadenceIntervalMS_Int < 2000)){
if(BatteryOnQ() == true)
{
MemoryLockedLED_CadenceFlag = false;//normal flashing if battery is on
}else{
MemoryLockedLED_CadenceFlag = true;//inverted flashing if battery is off
}
return;
}
if ((MemoryLockedCadenceIntervalMS_Int > 2000) && (MemoryLockedCadenceIntervalMS_Int < 2500)){
if(BatteryOnQ() == true)
{
MemoryLockedLED_CadenceFlag = true;//normal flashing if battery is on
}else{
MemoryLockedLED_CadenceFlag = false;//inverted flashing if battery is off
}
return;
}
if ((MemoryLockedCadenceIntervalMS_Int > 2500) && (MemoryLockedCadenceIntervalMS_Int < 3000)){
if(BatteryOnQ() == true)
{
MemoryLockedLED_CadenceFlag = false;//normal flashing if battery is on
}else{
MemoryLockedLED_CadenceFlag = true;//inverted flashing if battery is off
}
return;
}
if (MemoryLockedCadenceIntervalMS_Int > 7500) {
StartOfMemoryLockedLED_CadenceMS_UnsignedLong = millis(); //restart timer
return;
}
}
void Fdr::FaultCadence(){ //5 pulses in set and 5 seconds between
FaultCadenceIntervalMS_Int = int(millis() - StartOfFaultLED_CadenceMS_UnsignedLong);
if ((FaultCadenceIntervalMS_Int > 0) && (FaultCadenceIntervalMS_Int < 500)){
if(BatteryOnQ() == true)
{
FaultLED_CadenceFlag = true;//normal flashing if battery is on
}else{
FaultLED_CadenceFlag = false;//inverted flashing if battery is off
}
return;
}
if ((FaultCadenceIntervalMS_Int > 500) && (FaultCadenceIntervalMS_Int < 1000)){
if(BatteryOnQ() == true)
{
FaultLED_CadenceFlag = false;//normal flashing if battery is on
}else{
FaultLED_CadenceFlag = true;//inverted flashing if battery is off
}
return;
}
if ((FaultCadenceIntervalMS_Int > 1000) && (FaultCadenceIntervalMS_Int < 1500)){
if(BatteryOnQ() == true)
{
FaultLED_CadenceFlag = true;//normal flashing if battery is on
}else{
FaultLED_CadenceFlag = false;//inverted flashing if battery is off
}
return;
}
if ((FaultCadenceIntervalMS_Int > 1500) && (FaultCadenceIntervalMS_Int < 2000)){
if(BatteryOnQ() == true)
{
FaultLED_CadenceFlag = false;//normal flashing if battery is on
}else{
FaultLED_CadenceFlag = true;//inverted flashing if battery is off
}
return;
}
if ((FaultCadenceIntervalMS_Int > 2000) && (FaultCadenceIntervalMS_Int < 2500)){
if(BatteryOnQ() == true)
{
FaultLED_CadenceFlag = true;//normal flashing if battery is on
}else{
FaultLED_CadenceFlag = false;//inverted flashing if battery is off
}
return;
}
if ((FaultCadenceIntervalMS_Int > 2500) && (FaultCadenceIntervalMS_Int < 3000)){
if(BatteryOnQ() == true)
{
FaultLED_CadenceFlag = false;//normal flashing if battery is on
}else{
FaultLED_CadenceFlag = true;//inverted flashing if battery is off
}
return;
}
if ((FaultCadenceIntervalMS_Int > 3000) && (FaultCadenceIntervalMS_Int < 3500)){
if(BatteryOnQ() == true)
{
FaultLED_CadenceFlag = true;//normal flashing if battery is on
}else{
FaultLED_CadenceFlag = false;//inverted flashing if battery is off
}
return;
}
if ((FaultCadenceIntervalMS_Int > 3500) && (FaultCadenceIntervalMS_Int < 4000)){
if(BatteryOnQ() == true)
{
FaultLED_CadenceFlag = false;//normal flashing if battery is on
}else{
FaultLED_CadenceFlag = true;//inverted flashing if battery is off
}
return;
}
if ((FaultCadenceIntervalMS_Int > 4000) && (FaultCadenceIntervalMS_Int < 4500)){
if(BatteryOnQ() == true)
{
FaultLED_CadenceFlag = true;//normal flashing if battery is on
}else{
FaultLED_CadenceFlag = false;//inverted flashing if battery is off
}
return;
}
if ((FaultCadenceIntervalMS_Int > 4500) && (FaultCadenceIntervalMS_Int < 5000)){
if(BatteryOnQ() == true)
{
FaultLED_CadenceFlag = false;//normal flashing if battery is on
}else{
FaultLED_CadenceFlag = true;//inverted flashing if battery is off
}
return;
}
if (FaultCadenceIntervalMS_Int > 10000) {
StartOfFaultLED_CadenceMS_UnsignedLong = millis(); //restart timer
return;
}
}
void Fdr::OutputMenuOfCommandsAndLED_Cadences(){
#ifdef reduceMenu
Serial.println(F("M D P A O S H T"));
#endif
#ifndef reduceMenu
Serial.println();
Serial.println(F("Glendale Community College Flight Data Recorder"));
Serial.println(F(" by R.G. Sparber"));
Serial.println();
Serial.println(F("M This menu."));
Serial.println(F("D Display all port voltages and set the pre-flight state."));
Serial.println(F("P Prepare for launch!"));
Serial.println(F("A Use only while in flight state during"));
Serial.println(F(" on the ground testing: Display all port voltages.")); //RGS1.2
Serial.println(F("O Output data file."));
Serial.println(F("S Stop data collection and camera recording.")); //RGS1.2
#ifdef RealtimeClockCalibration
//Serial.println(F("C Calibrate internal hardware clock."));
#endif
Serial.println(F("H Diagnostic: Output control block."));
Serial.println(F("T Diagnostic: Generate test pattern in data memory."));
//Serial.println(F("R Student defined menu.")); //RGS1.5
if (IridiumModemPresentBool)Serial.println(F("I Diagnostic: Ping the Iridium modem."));
Serial.println();
Serial.println(F("LED mostly off: battery supplying power.")); //RGS1.3
Serial.println(F("LED mostly on: only USB supplying power. No sensor power available.")); //RGS1.3
Serial.println();
Serial.println(F("LED flash per cycle description"));
Serial.println(F(" 1 normal data collection"));
Serial.println(F(" 2 normal data collection after power disruption"));
Serial.println(F(" 3 memory locked either manually or because it is full"));///CR2.0
Serial.println(F(" flicker pre-flight"));
Serial.println(F(" 5 hardware or software fault detected"));
Serial.println();
Serial.println();
Serial.print(F("Source file: "));
Serial.println(SourceFile);
Serial.println();
#endif
}
void Fdr::OutputSingleLiveScan(){
byte PortNumberByte = 0; //CR2.0
float PortVoltageFloat = 0; //CR2.0
float BateryVoltageFloat = 0; //CR2.0
byte Port7 = 7;
BateryVoltageFloat = LivePortVoltageReadingFloat(Port7)*2; //port 7 reads half due to voltage divider so multiply by 2 before displaying.
//if a fault is present, explain it now
DisplayErrorStateQ(); //if any error detected, tell user
PrintLine();//RGS1.2
Serial.println(F("Display all Port Voltages and Pre-flight State"));
Serial.println();
Serial.print(F("Battery: ")); //CR1.7
#ifndef reduceMenu
if (BateryVoltageFloat < 5){ //when battery is off, we see less than 5V and sensors do not get 5V
Serial.println(F(" disconnected"));
Serial.println(F("WARNING: Sensors may not operate correctly."));
Serial.println(F("WARNING: RunCam2 camera is off."));//RGS1.4
}else{
Serial.print(BateryVoltageFloat,3); //output format x.xxx
Serial.println(F(" volts"));
}
Serial.println();
#endif
Serial.println(F("Port Voltage"));
Serial.println(F("==== ======="));
for (PortNumberByte = 0; PortNumberByte < 7; PortNumberByte++){
Serial.print(F(" "));
Serial.print(PortNumberByte);
Serial.print(F(" "));
PortVoltageFloat = LivePortVoltageReadingFloat(PortNumberByte);
Serial.println(PortVoltageFloat,3); //output format x.xxx
}
Serial.println();
#ifdef RealtimeClockCalibration
Serial.print(F("Time calibration is "));
Serial.print(int(ReadEEPROM_ControlBlock(OneSecondMillisecondsBaseAddressByte)) + int (256* ReadEEPROM_ControlBlock(OneSecondMillisecondsBaseAddressByte +1)));
Serial.println(F(" internal"));
Serial.println(F("millisecond counts per actual second"));
Serial.println();
Serial.println(F("If not between 900 and 1100, run time calibration"));
#endif
Serial.println();
Serial.println();
JustWait(); //when port voltages are displayed, we automatically go into the just wait state.
}
void Fdr::JustWait(){
WriteEEPROM_ControlBlock(ProgramStateAddressByte,MemoryLockedByte);
WriteEEPROM_ControlBlock(ErrorCodeAddressByte,SystemNormalFlagByte);
//verify program state write worked; I don't check write to error code because if it fails, no point in writing error to it. Therefore, I must assume that write worked or there is no point in proceeding.
if ((ReadEEPROM_ControlBlock(ProgramStateAddressByte)!= MemoryLockedByte) || (ReadEEPROM_ControlBlock(ErrorCodeAddressByte) != SystemNormalFlagByte)){
WriteEEPROM_ControlBlock(ErrorCodeAddressByte,JustWaitReadBackFailureFlagByte);
}
}
void Fdr::OutputContinuousDisplayDataStateChange(){
if (ReadEEPROM_ControlBlock(ProgramStateAddressByte)== MemoryLockedByte){
Serial.println();
Serial.println(F("Mem locked,no new data"));
return;
}
if (ReadEEPROM_ControlBlock(ProgramStateAddressByte )<TestForInFlightByte ){
#ifndef reduceMenu
Serial.println();
Serial.println();
Serial.println(F("WARNING: A battery voltage less than 6V means"));
Serial.println(F("sensors may not operate correctly."));
Serial.println();
#endif
Serial.println(F("Port0 Port1 Port2 || Port3 Port4 Port5 || Port6 battery"));
}
}
void Fdr::OutputDataToFile(){ //data was collected every SamplingRateSecondsByte (must be 2) seconds but we will output data every second by over sampling CR3.0 4.3 RGS1.3
//output line format is
//Seconds,Port0,Port1,Port2,Port3,Port4,Port5,Port6 peak,Battery,
resetLoopTimerBool = true;//outputting the data disrupts loop() timing so reset the loop timer to prevent the warning message
float VoltageFloat = 0; //CR2.0
int TimeStampSecondsInt = -1;// CR2.0 RGS1.3
byte port = 0; //RGS1.3
WriteEEPROM_ControlBlock(ProgramStateAddressByte,MemoryLockedByte); //lock memory so no data can be overwritten
Serial.println();
Serial.println(F("Seconds,Port0,Port1,Port2,Port3,Port4,Port5,Port6 peak,Batt,"));//output titles of the columns. Port6 is a peak reading found between port scans RGS1.1
ReadEEPROM_Pointer(CalledByOutputDataToFileByte); //get pointer to end of data which is called. The 7 is the calling subroutine's ID StartOfNextMemoryBlockLong
for (long EEPROM_PointerLong = StartOfDataMemoryLong; EEPROM_PointerLong < StartOfNextMemoryBlockLong;EEPROM_PointerLong = EEPROM_PointerLong + DataBlockSize){//move to start of each block of data and sequence through all of the blocks
//first output field is the time stamp unless we had power hit
if (ReadEEPROM(EEPROM_PointerLong + 14L) + ReadEEPROM(EEPROM_PointerLong + 15L) == 0){ //0 means power hit recorded
Serial.println();
Serial.println(F("Power was disrupted."));
TimeStampSecondsInt = -1; //when battery voltage reads 0, it means power hit recorded so restart time stamp count. I add 2 to this value before using it so by starting at -1, my first line of data is marked +1 second. RGS1.3
}else{
TimeStampSecondsInt = TimeStampSecondsInt + int(SamplingRateSecondsByte); //blocks are built every SamplingRateSecondsByte seconds so block count is time; first block gets time stamp of SamplingRateSecondsByte.
//building a Comma Separated Volume (CSV) output
Serial.print(TimeStampSecondsInt);
Serial.print(",");
for (port = 0; port<8;port++){ //ports 0-7
VoltageFloat = (float(256L*(ReadEEPROM(EEPROM_PointerLong + (2L*long(port)))) + ReadEEPROM(EEPROM_PointerLong + 1L + (2L*long(port))))*0.004888); //read high byte and low byte, convert to float, then convert to voltage with 5V for Vref divided by 1023 = 0.004888 CR3.0
if (port == 7)VoltageFloat = 2*VoltageFloat;//battery reading, multiply by 2 because voltage divider cut battery voltage in half
Serial.print(VoltageFloat,3);//3 places past decimal point CR3.2
Serial.print(",");
}
Serial.println();//end of block of data so add line feed
//output same data again but show time stamp plus 1 second. This gives an output file with data every second which will match up with ANSWR's GPS data RGS 1.3
Serial.print(TimeStampSecondsInt +1);
Serial.print(",");
for (port = 0; port<8;port++){ //ports 0-7
VoltageFloat = (float(256L*(ReadEEPROM(EEPROM_PointerLong + (2L*long(port)))) + ReadEEPROM(EEPROM_PointerLong + 1L + (2L*long(port))))*0.004888); //read high byte and low byte, convert to float, then convert to voltage with 5V for Vref divided by 1023 = 0.004888 CR3.0
if (port == 7)VoltageFloat = 2*VoltageFloat;//battery reading, multiply by 2 because voltage divider cut battery voltage in half
Serial.print(VoltageFloat,3);//3 places past decimal point CR3.2
Serial.print(",");
}
Serial.println();//end of block of duplicated data so add line feed
}
}
}
void Fdr::DiagnoticOutputControlBlock(){ //english output version
//output Control Block plus file name
float TotalRunTimeFloat = 0;
int PercentFullInt = 0;
Serial.println();
Serial.println();
Serial.println(F("Control Block"));
#ifndef reduceMenu
Serial.println(F("======================"));
#endif
#ifdef reduceMenu
Serial.println();
#endif
if((ReadEEPROM(ProgramStateAddressByte) == PreFlightByte))Serial.println(F("In Pre-flight"));
if((ReadEEPROM(ProgramStateAddressByte) == InFlightByte))Serial.println(F("In flight"));
if((ReadEEPROM(ProgramStateAddressByte) == InFlightWithPowerDisruptionByte))Serial.println(F("In flight with pwr hit"));
if(ReadEEPROM(ProgramStateAddressByte) == MemoryLockedByte)Serial.println(F("Mem locked & in Data Readout"));
if(ReadEEPROM(ErrorCodeAddressByte) == SystemNormalFlagByte){
Serial.println(F("No faults"));
}else{
Serial.print(F("fault #"));
Serial.print(ReadEEPROM(ErrorCodeAddressByte));
Serial.println(F(" detected"));
Serial.println();
Serial.println(F("Clear fault with Pre-flight"));
}
Serial.print(F("Start Next Mem Bk @ "));//CR3.0
ReadEEPROM_Pointer(CalledByDiagnoticOutputControlBlockByte);
Serial.print(StartOfNextMemoryBlockLong);
Serial.print(F(" ("));
PercentFullInt = int(0.5+((StartOfNextMemoryBlockLong-StartOfDataMemoryLong)*100L)/(131072L-StartOfDataMemoryLong));//round to the nearest percent CR3.2
Serial.print(PercentFullInt);
Serial.println(F("% full)"));
//Serial.print(F("Time calibration value = "));
//OneSecondMillisecondsUnsignedLong = ReadEEPROM_ControlBlock(OneSecondMillisecondsBaseAddressByte) + ( ReadEEPROM_ControlBlock(OneSecondMillisecondsBaseAddressByte + 1)*256);
//Serial.println(OneSecondMillisecondsUnsignedLong);
//Serial.println();
Serial.print(F("Data every "));
if(SamplingRateSecondsByte == 1) {
Serial.print(F("second"));
}else{
Serial.print(SamplingRateSecondsByte);
Serial.print(F(" secs"));
}
Serial.println(F(" for tot "));
Serial.print(F("runtime= "));
float SampleRateFloat = SamplingRateSecondsByte;
TotalRunTimeFloat = 8191*SampleRateFloat/3600; //maximum run time in decimal hours
Serial.print(TotalRunTimeFloat);
Serial.println(F(" hrs"));
Serial.println();
Serial.print(F("Compilation on "));
Serial.println(__DATE__);
Serial.println();
Serial.println(__FILE__);
#ifndef reduceMenu
Serial.println(F("======================"));
Serial.println();
#endif
Serial.print(F("Fdr.cpp ver "));
Serial.print(versionFloat);
Serial.println();
#ifndef reduceMenu
Serial.print(F("The calling program came from "));
//use filePathChar to print the .ino path
#endif
#ifdef reduceMenu
Serial.print(F("FDR: "));
#endif
char *programPathChar = filePathChar; //define a local string that contains my .ino program path
Serial.println(programPathChar);//the intent is to print the .ino's program path
}
void Fdr::SendPrepareForLaunchWarning(){
PrintLine();//RGS1.2
#ifndef reduceMenu
Serial.println(F("You asked to prepare for launch."));
Serial.println(F("This will erase all data. Are you sure? Y/N"));//verify user wants to do this
#endif
#ifdef reduceMenu
Serial.println(F("Sure? Y/N"));
#endif
}
void Fdr::GenerateTestPatternWarning(){ //CR3.1
#ifndef reduceMenu
PrintLine();//RGS1.2
Serial.println(F("You asked to generate a test pattern in the data"));
Serial.println(F("This will erase all data. Are you sure? Y/N"));//verify user wants to do this
#endif
#ifdef reduceMenu
Serial.println(F("Sure? Y/N"));
#endif
}
void Fdr::ActOnResponseToPrepareForLaunch(){
while (true){//wait until a character comes in
if (Serial.available() > 0){
Command = Serial.read();
if ((Command == 'Y')|| (Command == 'y')){
PrepareForLaunch();
PrintLine();//RGS1.2
#ifndef reduceMenu
Serial.println(F("Ready to launch: Turn power off"));
Serial.println();
Serial.println(F("Reminder: At the end of the flight, use the S command")); //RGS1.2
Serial.println (F("before removing power to prevent the loss of video."));
Serial.println();
Serial.println(F("No further commands accepted until after power turned off and then back on.")); //replace "power cycle" RGS1.2
#endif
#ifdef reduceMenu
Serial.println (F("OK"));
#endif
NonFaultStopProgram();
}
if ((Command == 'N')|| (Command == 'n')){//previously, this would abort if command was not Y or y and that caused an abort when newline was automatically added to carriarge return. Now logic only responds to yes and no. All other characters are ignored. RGS1.2
Serial.println (F("rqst abtd"));
resetLoopTimerBool = true;//the loop timer may detect this long delay as we wait for the user to respond and then abort. This flag resets the loop timer so we don't get a warning.
return;
}
}
}
}
void Fdr::ActOnResponseToGenerateTestPattern(){
while (true){//wait until a character comes in CR3.1
if (Serial.available() > 0){
Command = Serial.read();
if ((Command == 'Y')|| (Command == 'y'))
{
PrintLine();//RGS1.2
#ifndef reduceMenu
Serial.println (F("Pattern generation started."));
#endif
GenerateTestPattern();
#ifndef reduceMenu
Serial.println();
Serial.println (F("Done"));
#endif
}else{
#ifndef reduceMenu
Serial.println (F("Pattern request aborted"));
#endif
#ifdef reduceMenu
Serial.println (F("aborted"));
#endif
}
resetLoopTimerBool = true;//the loop timer may detect this long delay as we wait for the user to respond and then abort. This flag resets the loop timer so we don't get a warning.
return;
}
}
}
void Fdr::GenerateTestPattern(){//CR3.1
/*********************************************************************
I need to be able to generate a test pattern in the data. LSB is about 0.005V but I display down to 0.01V. This means I should increment starting at bit 2 which means increment in steps of 2. I don’t want any data to be the same as adjacent data so can start first port at 0*2 so will show 0, the second port at 1*2 and should shown 0.01v, third 2*3 and should show 0.03, etc. On next block, the first port will be at 2+16 = 18.
***********************************************************************/
long NumberOfTestBlocksLong = 10L; //CR3.2
long TopOfMemoryLong = 0;
int StartingValueInt = 0;
int PortStepSizeInt = 1; //since bit 2, this value is multiplied by 2
//int BlockStepSizeInt = 2;//leave room for oversampled value that fits between blocks (feature not used yet)
byte TestValueHighByte = 0;
byte TestValueLowByte = 0;
unsigned long AddressUnsignedLong = 0;
int TestValueInt = StartingValueInt;
int TestValueBatteryInt = 0;
WriteEEPROM_ControlBlock(ProgramStateAddressByte,InFlightByte); //change to in-flight so memory can be written
for (TopOfMemoryLong = StartOfDataMemoryLong; TopOfMemoryLong < ((NumberOfTestBlocksLong*DataBlockSize)+StartOfDataMemoryLong);TopOfMemoryLong = TopOfMemoryLong+DataBlockSize){//move across the blocks of data to fill all of memory
for (byte PortPointerByte = 0; PortPointerByte < 7; PortPointerByte=PortPointerByte+1){//move along current block of data but stop before battery value
AddressUnsignedLong = TopOfMemoryLong + (2*PortPointerByte);//generate address of next byte to be written
TestValueHighByte = highByte(TestValueInt);
TestValueLowByte = lowByte(TestValueInt);//parce test value into its bytes
WriteEEPROM_Data(AddressUnsignedLong,TestValueHighByte); //write test bytes to EEPROM
WriteEEPROM_Data(AddressUnsignedLong+1L,TestValueLowByte);
TestValueInt = TestValueInt + PortStepSizeInt*2;//advance test value
}
TestValueBatteryInt = (TestValueInt/2) + 512;//battery reading which must be >5 to avoid battery off warning. 512 is equiv to 2.5V which is multiplied by 2 in output code
AddressUnsignedLong = TopOfMemoryLong + (2*7); //battery's address
TestValueHighByte = highByte(TestValueBatteryInt);//output multiplies this value by 2 so we will stay above 5V
TestValueLowByte = lowByte(TestValueBatteryInt);
WriteEEPROM_Data(AddressUnsignedLong,TestValueHighByte); //write test bytes to EEPROM
WriteEEPROM_Data(AddressUnsignedLong+1L,TestValueLowByte);
TestValueInt = TestValueInt + PortStepSizeInt*2;//so average of adjacent blocks for the same port is not truncated
Serial.print(F("."));//status to user - one block written
}//end of block so move to next block
StartOfNextMemoryBlockLong = (NumberOfTestBlocksLong*DataBlockSize)+StartOfDataMemoryLong; //update memory pointer so output subroutine knows when to stop
WriteEEPROM_Pointer(); //writes StartOfNextMemoryBlockLong
WriteEEPROM_ControlBlock(ProgramStateAddressByte, MemoryLockedByte); //done generating test data so lock memory
}
void Fdr::ReadEEPROM_Pointer(byte ID){ //ID used to figure out who called this subroutine since it is called by 4 subroutines
(void) (ID); //Kevin added this but I'm now not clear why.
StartOfNextMemoryBlockLong = (1L * long(ReadEEPROM_ControlBlock(StartOfNextMemoryBlockPointerBaseAddressByte))) + (256L * (long(ReadEEPROM_ControlBlock(StartOfNextMemoryBlockPointerBaseAddressByte + 1)))) + (65536L * (long(ReadEEPROM_ControlBlock(StartOfNextMemoryBlockPointerBaseAddressByte + 2)))) + (16777216L * long(ReadEEPROM_ControlBlock(StartOfNextMemoryBlockPointerBaseAddressByte + 3)));
} //a long is 4 bytes. Each read is a byte that I first convert to a long. Then I multiply each converted byte by an integer defined as a long and add them up. The sum is a long. In this way we don't mix data types.
void Fdr::WriteEEPROM_Pointer()
{//the variable StartOfNextMemoryBlockLong is written to the control block
WriteEEPROM_ControlBlock(StartOfNextMemoryBlockPointerBaseAddressByte,byte(StartOfNextMemoryBlockLong)); //this takes the lowest byte of the float.
WriteEEPROM_ControlBlock(StartOfNextMemoryBlockPointerBaseAddressByte+1,byte(StartOfNextMemoryBlockLong>>8)); //this takes the second byte
WriteEEPROM_ControlBlock(StartOfNextMemoryBlockPointerBaseAddressByte+2,byte(StartOfNextMemoryBlockLong>>16)); //this takes the third byte
WriteEEPROM_ControlBlock(StartOfNextMemoryBlockPointerBaseAddressByte+3,byte(StartOfNextMemoryBlockLong>>24)); //this takes the forth byte
}
void Fdr::RecordPortReadings()
{ //records one block of data
PortDataAddressLong = StartOfNextMemoryBlockLong;//begin new block of data
//if Softwire enabled, this subroutine must change to read digital sensors,store the results in EEPROM, and conditionally output data to display; put new code here.
if (ContinuousDisplayDataFlag == true)Serial.println(); //used on the ground to test system; force new line before outputting data
for (byte PortCountByte = 0; PortCountByte < 8; PortCountByte++){ //read, write and optionally display all 7 data ports
if((PortCountByte == 7) && (JustPoweredUpForTimeStampFlag == true) && (ReadEEPROM_ControlBlock(ProgramStateAddressByte) == InFlightWithPowerDisruptionByte))
{
//this is the battery data. If we just recovered from a power hit, set this value to 0 to indicate power hit CR3.0
PortReadingInInt = 0;
JustPoweredUpForTimeStampFlag = false; //is set at power up and this is the only use so can be cleared here
#ifdef DiagPrint8
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
}else{
//did not have a power hit so record ports 0 - 5 and port 7 the same
if(PortCountByte == 6)
{
PortReadingInInt = Port6PeakInt;//port6 was read as often as possible in order to find the peak over the last waiting interval. Rather than read it now, we use the peak value that was found RGS1.1
Port6PeakInt = 0;//prepare for the next peak detection interval
#ifdef DiagPrint7
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
}else{
PortReadingInInt = analogRead(LogicalAnalogPinByte[PortCountByte]); //10 bits of data. analogRead() is a built in Arduino function
#ifdef DiagPrint8
Serial.println();
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" PortCountByte = "));
Serial.println( PortCountByte );
Serial.print(F(" PortReadingInInt = "));
Serial.println( PortReadingInInt );
#endif
}
}
PortReadingInHighByte = highByte(PortReadingInInt);
PortReadingInLowByte = lowByte(PortReadingInInt);
#ifdef DiagPrint9
ErrorCodeByte = ReadEEPROM_ControlBlock(ErrorCodeAddressByte);
if (ErrorCodeByte != OldErrorCodeByte)
{
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ErrorCodeByte = "));
Serial.println( ErrorCodeByte );
OldErrorCodeByte = ErrorCodeByte;
}
#endif
#ifdef DiagPrint9
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" PortDataAddressLong = "));
Serial.println( PortDataAddressLong );
#endif
WriteEEPROM_Data(PortDataAddressLong,PortReadingInHighByte);//if address is pointing within control block, error is generated
WriteEEPROM_Data(PortDataAddressLong+1L,PortReadingInLowByte);
#ifdef DiagPrint9
ErrorCodeByte = ReadEEPROM_ControlBlock(ErrorCodeAddressByte);
if (ErrorCodeByte != OldErrorCodeByte)
{
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ErrorCodeByte = "));
Serial.println( ErrorCodeByte );
OldErrorCodeByte = ErrorCodeByte;
}
#endif
if (ContinuousDisplayDataFlag == true)
{
if (PortCountByte < 7){ //external sensor ports
VoltageFloat = float(PortReadingInInt) * 0.004888;
}else{ //battery monitor
VoltageFloat = float(PortReadingInInt) * 0.004888 * 2; //used for battery reading where we first divided by 2 so must now compensate for that. CR3.0
}
Serial.print(VoltageFloat,3);//used on the ground to test system; 3 places past decimal point
Serial.print(F(" ")); //put two spaces after each reading
if ((PortCountByte == 2) || PortCountByte == 5) Serial.print (F("|| ")); //every 3 ports, put "|| " between readings
}
PortDataAddressLong = PortDataAddressLong + 2L; //advance to the next port data address
}
}
void Fdr::IncrementEEPROM_Pointer(){
/*****************************************************************
EEPROM is 2^17 = 131,072 bytes going from address 0 to 131071. A block is DataBlockSize (16 bytes). Addresses 0 through 15 are for the control block.
This first data block goes from address 16 through [(16+16) -1] = 31. The next block starts at (16 + 16 =) 32 and goes through 47.
The max number of data blocks is {[2^17] - control block}/16 = (131072 - 16)/16 = 8191 block of data.
The last data block starts at 16 + (16*8190) = 131,056. If we went one more data block, it would start at 131072 which is beyond available memory.
DataBlockSize is in a #define so is a substitution within the compiler. Therefore it does not have Byte added to the end.
*******************************************************************/
ReadEEPROM_Pointer(CalledByIncrementEEPROM_PointerByte); //it outputs to StartOfNextMemoryBlockLong; this is the start of the block that was just written.
StartOfNextMemoryBlockLong = StartOfNextMemoryBlockLong + DataBlockSize;//increment to address of the next data block start. If =< 131071 - DataBlockSize, we return without locking EEPROM. If >131071 - DataBlockSize we lock EEPROM and return
if (StartOfNextMemoryBlockLong > (131072 - DataBlockSize)){//If true, there is not enough room for this next DataBlock. Lock the EEPROM and return. CR3.0
WriteEEPROM_ControlBlock(ProgramStateAddressByte,MemoryLockedByte);
}
}
/********************************************************************************
L E V E L 4 S U B R O U T I N E S
*********************************************************************************/
void Fdr::DisplayErrorStateQ()
{
ErrorCodeByte = ReadEEPROM_ControlBlock(ErrorCodeAddressByte);
if (ErrorCodeByte != 0)
{
PrintLine();//RGS1.2
}
switch (ErrorCodeByte){
case 1:
Serial.println(F("Out of range read detected"));
break;
case 2:
Serial.println(F("Out of range write detected"));
break;
case 3:
Serial.println(F("ControlTheFlightParameters(): Ready for Launch EEPROM read back failure"));
break;
case 4:
Serial.println(F("ControlTheFlightParameters(): In Flight EEPROM read back failure"));
break;
case 5:
Serial.println(F("JustWait(): EEPROM read back failure"));
break;
case 6:
Serial.println(F("PrepareForLaunch(): EEPROM read back failure"));
break;
case 7:
Serial.println(F("UnprotectedWriteEEPROM(): asked to write to locked memory"));
break;
case 8:
Serial.println(F("Invalid error code. "));//was WriteEEPROM() Attempt Made To Write To One SecondMillisecondsConstantByte
break;
case 9:
Serial.println(F("SerialAvailableResponseErrorCheck(): SerialAvailableReturnCodeOutOfRangeByte"));
break;
case 10:
Serial.println(F("Read back from EEPROM found Mismatch"));
break;
case 11:
Serial.println(F("WriteEEPROM_Data(): Illegal data write attempt to control block"));
break;
case 12:
Serial.println(F("WriteEEPROM_ControlBlock(): WriteEEPROM_Data(): Attempt made to write to data block"));
break;
}
if (ErrorCodeByte != 0)
{
Serial.println();
}
}
void Fdr::PrepareForLaunch()
{
//StartOfNextMemoryBlockPointerBaseAddressByte
WriteEEPROM_ControlBlock(StartOfNextMemoryBlockPointerBaseAddressByte,byte(StartOfDataMemoryLong)); //initialize StartOfNextMemoryBlockLong
WriteEEPROM_ControlBlock(StartOfNextMemoryBlockPointerBaseAddressByte+1,byte(StartOfDataMemoryLong>>8));
WriteEEPROM_ControlBlock(StartOfNextMemoryBlockPointerBaseAddressByte+2,byte(StartOfDataMemoryLong>>16));
WriteEEPROM_ControlBlock(StartOfNextMemoryBlockPointerBaseAddressByte+3,byte(StartOfDataMemoryLong>>24));
WriteEEPROM_ControlBlock(ProgramStateAddressByte,PreFlightByte); //set program state to pre-flight
WriteEEPROM_ControlBlock(ErrorCodeAddressByte,SystemNormalFlagByte); //set error state to system normal
//verify all writes worked
if ((ReadEEPROM_ControlBlock(StartOfNextMemoryBlockPointerBaseAddressByte) != byte(StartOfDataMemoryLong))||(ReadEEPROM_ControlBlock(StartOfNextMemoryBlockPointerBaseAddressByte+1) != byte(StartOfDataMemoryLong>>8))||(ReadEEPROM_ControlBlock(StartOfNextMemoryBlockPointerBaseAddressByte+2) != byte(StartOfDataMemoryLong>>16))||(ReadEEPROM_ControlBlock(StartOfNextMemoryBlockPointerBaseAddressByte+3) != byte(StartOfDataMemoryLong>>24))||(ReadEEPROM_ControlBlock(ProgramStateAddressByte)!= PreFlightByte)||(ReadEEPROM(ErrorCodeAddressByte)!=SystemNormalFlagByte))
{
WriteEEPROM_ControlBlock(ErrorCodeAddressByte, PrepareForLaunchReadBackFailureFlagByte);
}
}
float Fdr::LivePortVoltageReadingFloat(byte PortNumberByte)
{
//byte LogicalAnalogPinByte[8]={8,7,10,0,1,2,3,9}; //maps port number to analog logical pin number
float PortReadingFloat = 0; //CR2.0
PortReadingFloat = float(analogRead(LogicalAnalogPinByte[PortNumberByte]));//convert from int to float
PortReadingFloat = PortReadingFloat*0.004888; //convert from pure number to voltage at 4.888 mV per step
#ifdef DiagPrint8
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" PortReadingInInt = "));
Serial.println( PortReadingInInt );
#endif
return PortReadingFloat;
}
/********************************************************************************
L E V E L 5 S U B R O U T I N E S
**********************************************************************/
void Fdr::NonFaultStopProgram()
{ //effectively stop program and also flickers LED
while(true){ //CR1.9
digitalWrite(ExternalLED, ExternalLED_OnByte);
delay(100);
digitalWrite(ExternalLED, ExternalLED_OffByte);
delay(100);
}
}
void Fdr::FaultStopProgram()
{ //effectively stop program and turns off LED
while(true){ //CR1.9
digitalWrite(ExternalLED, ExternalLED_OffByte);
}
}
void Fdr::WriteEEPROM_ControlBlock(byte eeAddressByte, byte data){ //subroutine accepts a single byte for the address and converts it to a long because this is the control block which will always contain less than 255 bytes RGS1.2
if(eeAddressByte > 15)
{
#ifdef DiagPrint12
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
#endif
WriteEEPROM_ControlBlock(ErrorCodeAddressByte, AttemptMadeToWriteToDataBlockByte);
return;
}
UnprotectedWriteEEPROM(long(eeAddressByte), data);
}
byte Fdr::ReadEEPROM_ControlBlock(byte eeAddressByte){ //subroutine accepts a single byte for the address and converts it to a long because this is the control block which will always contain less than 255 bytes
//return ReadEEPROM(long(eeAddressByte));
byte data = ReadEEPROM(long(eeAddressByte));
return data;
}
/********************************************************************************
E E P R O M S U B R O U T I N E S
*********************************************************************************/
//Based on eeprom example from SparkFun Electronics June 11th, 2017
void Fdr::WriteEEPROM_Data(long eeAddress, byte data)
{//RGS1.2
if (eeAddress < 16L)
{//data write attempted into control block
WriteEEPROM_ControlBlock(ErrorCodeAddressByte,AttemptMadeToWriteToControlBlockByte);
return; //set error value but keep collecting data
}else{//all other writes pass through
UnprotectedWriteEEPROM(eeAddress,data);
}
}
void Fdr::UnprotectedWriteEEPROM(long eeAddress, byte data){ //subroutine accepts a 4 byte address and a single byte of data to write RGS1.2
//The EEPROM's spec sheet shows a Block Select Bit (B0) and two bytes of address. When eeAddress is < 65536, the lower block is accessed so B0 = 0. All of my addresses are this case as verified with a scope.
if (eeAddress > 131071L){ //max address is 2^17 - 1 = 131071
WriteEEPROM_ControlBlock(ErrorCodeAddressByte,OutOfRangeWriteFlagByte);
return;
}
if ((ReadEEPROM(ProgramStateAddressByte) == MemoryLockedByte) && (eeAddress >15)){ //this permits writes to the control parameters even when EEPROM is locked. CR3.1
WriteEEPROM_ControlBlock(ErrorCodeAddressByte,WriteEEPROM_AttemptMadeToWriteToLockedMemoryFlagByte);
return; //if Lock EEPROM flag is true, do not write to EEPROM. Is set false by "P".
}
if (eeAddress < 65536L)
{
Wire.beginTransmission(EEPROM_ADR_LOW_BLOCK);
eeAddress &= 0xFFFF; //Erase the upper 16 bits of the long variable
}
else
{
Wire.beginTransmission(EEPROM_ADR_HIGH_BLOCK);
}
Wire.write((int)(eeAddress >> 8)); // queue MSB
Wire.write((int)(eeAddress & 0xFF)); // queue LSB
Wire.write(data); // queue single byte
Wire.endTransmission();//transmit address and data
delay(10); //Write cycle time is max of 5 ms so wait 10.
//verify write worked
if (ReadEEPROM(eeAddress) != data){
//Serial.println(F("EEPROM write failed. Error 10."));
WriteEEPROM_ControlBlock(ErrorCodeAddressByte,ReadBackFromEEPROM_MismatchByte); //CR2.0
}
}
byte Fdr::ReadEEPROM(long eeaddress){ //subroutine accepts a 4 byte address and returns a single byte of data
if (eeaddress > 131071L){
WriteEEPROM_ControlBlock(ErrorCodeAddressByte, OutOfRangeReadFlagByte);
return 0;
}
if (eeaddress < 65536) { //send block address based on eeaddress
Wire.beginTransmission(EEPROM_ADR_LOW_BLOCK);
}else{
Wire.beginTransmission(EEPROM_ADR_HIGH_BLOCK);
}
//then send data address within the block
Wire.write((int)(eeaddress >> 8)); //queue MSB
Wire.write((int)(eeaddress & 0xFF)); //queue LSB
Wire.endTransmission();//transmit address
delay(10); //Write cycle time is max of 5 ms so wait 10.
if (eeaddress < 65536){
Wire.requestFrom(EEPROM_ADR_LOW_BLOCK, 1);
}else{
Wire.requestFrom(EEPROM_ADR_HIGH_BLOCK, 1);
}
byte rdata = 0x33; //if Wire.available() returns false, 0x33 is returned from subrotine; THIS IS STRANGE AND MAYBE A BUG. THE DATA COULD BE ANY VALUE BUT IF 33, THAT FLAGS AN ERROR?
if (Wire.available()) rdata = Wire.read(); //if EEPROM and its I2C is available, return the single byte.
return rdata;
}
int Fdr::WriteSEEPROM(long eeAddress, byte data) { //4.4 Student's EEPROM. subroutine accepts a 4 byte address and a returns a single integer. If no error, 0 is returned. If there is an error, 0x01ee is returned where e is the error code. ee = 00 means address is out of range, ee=02 means readback failed to match what was written.
//The EEPROM's spec sheet shows a Block Select Bit (B0) and two bytes of address. When eeAddress is < 65536, the lower block is accessed so B0 = 0. All of my addresses are this case as verified with a scope.
if (eeAddress > 131071L){ //max address is 2^17 - 1 = 131071
return 0x0100; //address is out of range
}
if (eeAddress < 65536L)
{
Wire.beginTransmission(SEEPROM_ADR_LOW_BLOCK);
eeAddress &= 0xFFFF; //Erase the upper 16 bits of the long variable
}
else
{
Wire.beginTransmission(SEEPROM_ADR_HIGH_BLOCK);
}
Wire.write((int)(eeAddress >> 8)); //queue MSB
Wire.write((int)(eeAddress & 0xFF)); //queue LSB
Wire.write(data); //queue single byte
Wire.endTransmission();//transmit address and data
delay(10); //Write cycle time is max of 5 ms so wait 10.
//verify write worked
int ReadData = ReadSEEPROM(eeAddress);
if (ReadData != int(data)){
return 0x0102;
}
return 0x0000;
}
int Fdr::ReadSEEPROM(long eeaddress){ //4.4 Student's EEPROM. subroutine accepts a 4 byte address and returns a single integer. If no error, the upper byte will be 0 and the lower byte will be data. If there is an error, 0x01ee is returned where ee is the error code. ee = 00 means address is out of range. ee = 01 means I2C bus not available.
if (eeaddress > 131071L){ //address is out of range
return 0x0100;
}
if (eeaddress < 65536) { //send block address based on eeaddress
Wire.beginTransmission(SEEPROM_ADR_LOW_BLOCK); //we will be talking to EEPROM 1
}else{
Wire.beginTransmission(SEEPROM_ADR_HIGH_BLOCK);//we will be talking to EEPROM 1
}
//then send data address within the block
Wire.write((int)(eeaddress >> 8)); //queue MSB
Wire.write((int)(eeaddress & 0xFF)); //queue LSB
Wire.endTransmission();//transmit address
delay(10); //Write cycle time is max of 5 ms so wait 10.
if (eeaddress < 65536){
Wire.requestFrom(SEEPROM_ADR_LOW_BLOCK, 1);
}else{
Wire.requestFrom(SEEPROM_ADR_HIGH_BLOCK, 1);
}
int rdata = 0; //if Wire.available() returns false, 0xFF01 is returned from subrotine
if (Wire.available() == true)
{//if a byte is available to read, return the integer 0x00DD where DD is the single byte of date.
rdata = int(Wire.read()); //the single byte of data becomes the LSB.
return rdata;
} else {//there is no data to read so we have a failure. Return the integer 0xFF01
return 0x0101;
}
}
unsigned int Fdr::Iridium(unsigned int ModemCommand)
{//this was marked as unsigned long but it should have been unsigned int because that is the format of the return codes RGS1.5
/********************************************************
M O D E M C O M M A N D S
********************************************************
PingUInt
SetUpModemUInt
PerformTransmitUInt
getReceivedDataUInt
dataLoopAroundEnabledUInt
dataLoopAroundDisabledUInt
statusUInt
********************************************************
D A T A T O M O D E M
********************************************************
IridiumTransmitDataByte[] of 45 bytes
********************************************************
R E T U R N C 0 D E S
********************************************************
ModemReadyForUseUInt
BusySettingUpModemUIntSetUpModem();
ModemFailedAtSetupUInt
ModemFailedAtSetupTimeOutUInt
SentPerformTransmitUInt
SentgetReceivedDataUInt
SentPingUInt
PingThroughMPM_AndModemSuccessUInt
PingToMPM_SuccessButToModemFailedUInt
PingToMPM_TimedOutUInt
MPM_Busy_TransmitCommandRejectedUInt
InvalidCommandUInt
MPM_DidNotRespondToRequestForDataUInt
dataLoopAroundEnabledUInt
dataLoopAroundDisabledUInt
********************************************************
R E S P O N S E F R O M M O D E M A N D D A T A
********************************************************
MPM_ResponseUInt
IridiumReceiveDataByte[45]
MPM_RejectedDataByte[45] - accessable with TeraTerm connected to MPM. Uncomment the #define rejectedData line to turn on the print statements
********************************************************
FDR can send Modem Pro Micro (MPM) only PerformTransmitUInt plus 45 bytes of data to transmit, Ping, and Abort. Any other command is flagged as an error.
To get status and, optionally, the received array, this subroutine requests up to 47 bytes. The first two bytes hold the status and the remaining 45 bytes are the received data.
FDR can request that MPM give it status and, optionally, received data. Possible responses to FDR are:
• ReturnCodeUInt
• InvalidCommandUInt
• MPM_Busy_TransmitCommandRejectedUInt
If ReturnCodeUInt is TransmitAndReceiveSuccessfulUInt, student can see what was sent from the ground in IridiumReceiveDataByte[45].
*******************************************************/
#ifdef runOnceTest
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F("ModemCommand = "));
Serial.println(ModemCommand);
#endif
if (ModemCommand == statusUInt)
{
ReturnCodeUInt = modemStatus();//it returns the current return code so the user can decide when to send the next command
#ifdef statusTest
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.println();
Serial.println(F("current ReturnCodeUInt = "));
Serial.println(ReturnCodeUInt);
#endif
if(ReturnCodeUInt == dataLoopAroundDisabledUInt)
{//I reported it when I did the clearing of loop around so don't want to return it again
//ReturnCodeUInt = idleUInt;
}
return ReturnCodeUInt;
}
if (ModemCommand == PingUInt)
{
ReturnCodeUInt = PingModem();//an AT command is sent to modem which must respond "OK" for a pass.
#ifdef pingTest
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.println();
Serial.println(F("ping ReturnCodeUInt = "));
Serial.println(ReturnCodeUInt);
#endif
return ReturnCodeUInt; //this is a special case because ReturnCodeUInt set by MPM
}
if(IridiumModemPresentBool == false)return NoFunctioningModemPresentUInt;
if (ModemCommand == SetUpModemUInt)
{
ReturnCodeUInt = SetUpModem();//function does also populate ReturnCodeUInt so this assignment isn't necessary
#ifdef modemNGtest
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.println();
Serial.println(F("ReturnCodeUInt = "));
Serial.println(ReturnCodeUInt);
#endif
return ReturnCodeUInt;
}
if (ModemCommand == PerformTransmitUInt)
{
ReturnCodeUInt = PerformTransmit();
return ReturnCodeUInt;
}
if(ModemCommand == getReceivedDataUInt)
{
ReturnCodeUInt = getReceivedData();
return ReturnCodeUInt;
}
if(ModemCommand == setLoopAroundUInt)
{
enableLoopAroundQbool = true;
ReturnCodeUInt = loopback();//it returns ACK that request was sent
return ReturnCodeUInt;
}
if(ModemCommand == clearLoopAroundUint)
{
enableLoopAroundQbool = false;
ReturnCodeUInt = loopback();//it returns ACK that request was sent
return ReturnCodeUInt;
}
return InvalidCommandUInt;//bug found by Kevin. added 1/30/24
}// end of Iridium()
void Fdr::FlushWireBuffer()
{//ensure nothing left in buffer
byte drainedDataByte;
while (Wire.available() > 0)
{
drainedDataByte = Wire.read();
#ifdef modemNGtest
Serial.print("flushed data: ");
Serial.println(drainedDataByte, HEX);
#endif
}
}
byte Fdr::GoToVideoStandby()//RGS1.5
{
if(BatteryOnQ() == false)//this can only happen if the Pro Micro is getting power from the USB and the battery is turned off
{
EstimatedCameraStateByte = CameraHasNoPowerStateByte;
return CameraHasNoPowerErrorByte;
}
if(EstimatedCameraStateByte > MaximumCameraStateValueByte)return IllegalCameraStateValueErrorByte;
if(EstimatedCameraStateByte == VideoStandbyStateByte)return NoCameraErrorByte;
if(EstimatedCameraStateByte == StillStandbyStateByte)ChangeCameraMode();//since camera is in still-standby mode, we need to change modes in order to get to video-standby
if(EstimatedCameraStateByte == VideoRecordStateByte)PushShutter();//since camera was in video-record state, pushing the shutter switches it to video-standby
EstimatedCameraStateByte = VideoStandbyStateByte;//update estimated camera state to video-standby
return NoCameraErrorByte;
}
byte Fdr::StartVideoRecording()//RGS1.5
{
if(BatteryOnQ() == false)//this can only happen if the Pro Micro is getting power from the USB and the battery is turned off
{
EstimatedCameraStateByte = CameraHasNoPowerStateByte;
return CameraHasNoPowerErrorByte;
}
if(EstimatedCameraStateByte > MaximumCameraStateValueByte)return IllegalCameraStateValueErrorByte;
if(EstimatedCameraStateByte == VideoRecordStateByte)return NoCameraErrorByte;//however, user has asked to start recording video while already there
if(EstimatedCameraStateByte == StillStandbyStateByte)ChangeCameraMode();//we must go to video-standby first via mode change subroutine
//camera is now in video-standby state
PushShutter();//this moves the camera from video-standby to video-record
EstimatedCameraStateByte = VideoRecordStateByte;//update estimated camera state to reflect our best guess
return NoCameraErrorByte;
}
byte Fdr::GoToStillPictureStandby()//RGS1.5
{
if(BatteryOnQ() == false)//this can only happen if the Pro Micro is getting power from the USB and the battery is turned off
{
EstimatedCameraStateByte = CameraHasNoPowerStateByte;
return CameraHasNoPowerErrorByte;
}
if(EstimatedCameraStateByte > MaximumCameraStateValueByte)return IllegalCameraStateValueErrorByte;
if(EstimatedCameraStateByte == StillStandbyStateByte)return NoCameraErrorByte;//however, user has asked to go to still picture standby while already there
if(EstimatedCameraStateByte == VideoRecordStateByte)PushShutter();//moves the camera from video-record to video-standby
//camera is now in video-standby state
ChangeCameraMode();//moves camera from video-standby to still-standby state
EstimatedCameraStateByte = StillStandbyStateByte ;//update estimated camera state to reflect our best guess
return NoCameraErrorByte;
}
byte Fdr::TakeStillPicture()//RGS1.5
{
if(BatteryOnQ() == false)//this can only happen if the Pro Micro is getting power from the USB and the battery is turned off
{
EstimatedCameraStateByte = CameraHasNoPowerStateByte;
return CameraHasNoPowerErrorByte;
}
if(EstimatedCameraStateByte > MaximumCameraStateValueByte)return IllegalCameraStateValueErrorByte;
if(EstimatedCameraStateByte == VideoRecordStateByte)
{
PushShutter();//moves the camera from video-record to video-standby
EstimatedCameraStateByte = VideoStandbyStateByte;
}
if(EstimatedCameraStateByte == VideoStandbyStateByte)ChangeCameraMode();//moves the camera from video-standby to still-standby
//camera is now at still-standby state
EstimatedCameraStateByte = StillStandbyStateByte;
PushShutter();//take a still picture
return NoCameraErrorByte;
}
void Fdr::PushShutter()
{//two pulses pushes the shutter to either take one picture or start/stop video recording 4.3
OnePulse();
OnePulse();
delay (2000); //give time for camera to process command
#ifdef DiagPrintRC
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" PushShutter() just run."));
#endif
}
void Fdr::ChangeCameraMode()
{//single pulse toggles between still and video 4.3
OnePulse();
delay (2000); //give time for camera to process command
#ifdef DiagPrintRC
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ChangeCameraMode just run."));
#endif
}
void Fdr::OnePulse(){ // |--|__ assumes output starts out low 4.3
digitalWrite(RunCamControl, LOW); //insure positive edge is generated
digitalWrite(RunCamControl, HIGH); //80 ms wide pulse
delay (80);
digitalWrite(RunCamControl, LOW);
delay (80);//80 ms of low
}
void Fdr::PrintOutInterpretationOfSingleResponseFromTransmissionCommand()
{//not being used
Serial.println(__LINE__);
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
if(ReturnCodeUInt == MPM_Busy_TransmitCommandRejectedUInt)
{
Serial.println(__LINE__);
Serial.println(F(" ERROR: you asked to transmit but the modem is not done with your last request."));
return;
}
if((ReturnCodeUInt >= StatusRangeMinUInt) && (ReturnCodeUInt <= StatusRangeMaxUInt))
{
Serial.println(__LINE__);
Serial.print(F(" Status is return code "));
Serial.println( ReturnCodeUInt );
return;
}
if((ReturnCodeUInt >= FailureRangeMinUInt) && (ReturnCodeUInt <= FailureRangeMaxUInt))
{
Serial.println(__LINE__);
Serial.print(F(" Fault is "));
Serial.println( ReturnCodeUInt );
return;
}
//should not be any other responses from MPM_
Serial.println(__LINE__);
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
Serial.println(F("ERROR: unexpected response from modem software."));
return;
}
void Fdr::RequestDataTransmissionSession()
{
#ifdef print1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
/* the following code was moved to Iridium.ino
byte data = 0; // used in diag print
//prevent the same data being send twice in a row
boolean DuplicateBool = true;
for (byte i=0; i<45;i++){
data = (*IridiumTransmitDataByte)[i];
//Serial.print( data );
//Serial.println(",");
if((*IridiumTransmitDataByte)[i] != PreviousIridiumTransmitDataByte[i])
{
DuplicateBool = false;
break;//no need to search further. The arrays are not identical.
}
}
if(DuplicateBool)
{
ReturnCodeUInt = DuplicateTransmitOfDataAttemptedUInt;
DataTransmissionSessionActiveBool = false;
return;
}
*/
DataTransmissionSessionActiveBool = true;
ReturnCodeUInt = Iridium(PerformTransmitUInt);//pass command plus array. Return code will say SentPerformTransmitUInt or NoFunctioningModemPresentUInt. Student must decide what to do with the rainy day responses.
#ifdef print1
Serial.print(F("line number "));//diag code
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
#endif
}//end of RequestDataTransmissionSession()
void Fdr::CheckTransmissionProgress()
{
/**************************************************
My failure returns are now 1 to 299 (this includes MO status failures)
FailureRangeMinUInt = 1
FailureRangeMaxUInt = 299
My status returns are 300 to 399
StatusRangeMinUInt = 300
StatusRangeMaxUInt = 399
My transmit success returns are 402 to 499
TransmitSuccessRangeMinUInt = 402
SuccessRangeMaxUInt = 499
****************************************************/
ReturnCodeUInt = SentgetReceivedDataUInt;
if ((millis() - LastTransmissionCheckTimeStampUInt) < 5000)return;//silently rejects request to check status if less than 5 seconds since last request in order to give time for MPM to work
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
ReturnCodeUInt = Iridium(getReceivedDataUInt);
if(LastReturnCodeUInt == ReturnCodeUInt)
{
Serial.print(F("."));
}else{
#ifdef DiagPrint1
LastReturnCodeUInt = ReturnCodeUInt;
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
#endif
}
//if we got a success or a failure, some cleanup is needed before returning
if ((ReturnCodeUInt == TransmitAndReceiveSuccessfulUInt) || (ReturnCodeUInt == TransmitSuccessfulAndNoReceiveUInt))
{
Serial.print(F("22 line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" Transmission was successful. "));
Serial.println( ReturnCodeUInt );
/* the following code was moved to Iridium.ino
for(byte i=0; i<45;i++)//since array was successfully transmitted, save it as previous array
{
PreviousIridiumTransmitDataByte[i] = (*IridiumTransmitDataByte)[i];//save a copy of what was just transmitted. We use it to prevent sending the same data twice in a row
}
*/
CycleIdleBool = true; //Iridium cycle is now over.
LastTransmissionCheckTimeStampUInt = millis();
}
if ((ReturnCodeUInt >= FailureRangeMinUInt)&& (ReturnCodeUInt <= FailureRangeMaxUInt))
{
CycleIdleBool = true; //Iridium cycle is now over.
}
//all other return codes are status and do not need any additional logic
}
bool Fdr::I_Two_C_BusAvailableQ()
{ //If bus is occupied, it returns false. I attempt to read address 0x00
Wire.beginTransmission(EEPROM_ADR_LOW_BLOCK);
//then send data address within the block
Wire.write((int)(0x00 >> 8)); //queue MSB
Wire.write((int)(0x00 & 0xFF)); //queue LSB
Wire.endTransmission();//transmit address
delay(10); //Write cycle time is max of 5 ms so wait 10.
Wire.requestFrom(EEPROM_ADR_LOW_BLOCK, 1);
if (Wire.available())return true;
return false;
}
unsigned int Fdr::OneTimeRun()
//The first time it is called, it prints the Fdr.cpp version number to the terminal emulator. Then it tests the I2C bus and prints OK or the program stops executing. If the GPS is provisioned, it verifies the hardware is functioning but not that it has found any satellites. If the modem is provisioned, it verifies the hardware is functional and sets it. It returns the newest return code.
//Subsequent calls to this function return the last known return code
{
unsigned int modemStatusUInt;
if (OneTimeRunBool)
{
for(byte i=0; i < 5;i++)//this is a countdown to let user time to set up serial monitors but is also giving time for MPM to be stable and idling before I talk to it
{
Serial.println(5 - i);
delay(300);
}
Serial.print(F("FDR.cpp ver "));
Serial.println(versionFloat);
Serial.println();
OneTimeRunBool = false;
StartForTimeStampULong = millis();//used for diag prints
digitalWrite(ExternalLED, ExternalLED_OnByte);//wink LED to show software is running after power up CR3.0
delay(500);
digitalWrite(ExternalLED, ExternalLED_OffByte);
digitalWrite(RunCamControl, LOW); //insure positive edge is generated 4.3
//test if I2C works
Serial.print(F("I2C TST: "));
ReadEEPROM_ControlBlock(ErrorCodeAddressByte);
Serial.println(F("OK"));
if (*GNSS_Bool)
{
#ifdef ebugLoopAround
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
Serial1.begin(GPSbaudRateUInt); //define what rate the UART will expect from GPS
Serial.print(F("GPS "));
#ifdef GNSSprint
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
GlobalNavigationSatelliteSystem();//test GPS
if((*NavigationDataByte)[0] == 201)
{//we have a hardware problem
Serial.println(F("NG"));
}else
{
Serial.println(F("OK"));
}
//if we have equipped a GPS
//set up communications path between Arduino and GPS; Default of 8 data bits, 1 stop bit, no parity or flow control is what the GPS needs. 2/10/2024 we found that the baud rate defaults to 9600 but can be set to anything from 9600 to 384000. Set
}
//Serial.print(F("Modem: "));
if (*Modem_Bool)//if we have equipped a modem
{
#ifdef ebugLoopAround
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
for(i=0;i<45;i++)
{
PreviousIridiumTransmitDataByte[i]=0x00;
}
#ifdef DiagPrint9
Serial.print(F("setup(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" PortDataAddressLong = "));
Serial.println( PortDataAddressLong );
#endif
ReturnCodeUInt = Iridium(PingUInt);//verify hardware present
#ifdef ebugLoopAround
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ReturnCodeUInt = "));
This is the Serial.println( ReturnCodeUInt );
#endif
Serial.print(F("Modem "));//form modem diag output
if(ReturnCodeUInt == PingThroughMPM_AndModemSuccessUInt)
{
IridiumModemPresentBool = true;//auto detect a modem but it may not be the correct serial number or be fully functional. If all OK, IridiumModemOperationalBool will next be set during modem setup.
}else
{//failed ping so return early
Serial.println(F("failed ping"));
IridiumModemPresentBool = false;//defensive
return ReturnCodeUInt;//failure returned and left as return code but it is easier for user to just use return value since timing is not known to them.
}
//to get here, modem must have passed ping
ReturnCodeUInt = Iridium(SetUpModemUInt);//one time setup of modem which includes seeing if connected modem is the right one.
if(ReturnCodeUInt == ModemReadyForUseUInt)
{
IridiumModemOperationalBool = true;
Serial.println(F("OK"));
}else
{
IridiumModemOperationalBool = false;
Serial.println(F("failed setup"));
}
return ReturnCodeUInt;//either way, send back return code
}
}//execute this tasks if this is the first time we ran through loop
return ReturnCodeUInt;//after first time executed, just return last known return code
}//end of oneTimeRun()
unsigned int Fdr::modemStatus()
{
ModemCommandArrayByte[0] = lowByte(statusUInt);
ModemCommandArrayByte[1] = highByte(statusUInt);
#ifdef ModemStatusTesting
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ModemCommand = "));
Serial.println( statusUInt );
#endif
Wire.beginTransmission(0xA); // take ownership of I2C bus and begin session with the MPM which is the MPM_
Wire.write(ModemCommandArrayByte,2);// queues up ping command to MPM. It is an unsigned int so 2 bytes
Wire.endTransmission();// sends the 2 bytes to the MPM
//no delay need since it will echo existing value
//next, read back current return code. If MPM_ does not respond with anything for more than 10 seconds, call it ModemStatus_TimedOutUInt.
unsigned int statusStartTimeUInt = millis();
Wire.requestFrom(0xA,2);// request 2 bytes from MPM_ device #0xA for return code.
while (Wire.available() < 1) if((millis()-statusStartTimeUInt)> 10)
{
return ModemStatus_TimedOutUInt;//MPM didn't respond in time
}
while (Wire.available() > 0)
{
ResponseLowByte = Wire.read(); //this assumes low byte arrives first
ResponseHighByte = Wire.read();
MPM_ResponseUInt = word(ResponseHighByte,ResponseLowByte);//assemble retun code from MPM
}
if(MPM_ResponseUInt == 0xFF00)return MPM_DidNotRespondToRequestForDataUInt;
return MPM_ResponseUInt;
// the MPM_ResponseUInt is just the current return code.
} //end of modemStatus()
unsigned int Fdr::PingModem()
{
ModemCommandArrayByte[0] = lowByte(PingUInt);
ModemCommandArrayByte[1] = highByte(PingUInt);
StartForTimeStampULong = millis();//start diag timer when ping command received.
#ifdef modemNGtest
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ModemCommand = "));
Serial.println( PingUInt );
#endif
Wire.beginTransmission(0xA); // take ownership of I2C bus and begin session with the MPM which is the MPM_
Wire.write(ModemCommandArrayByte,2);// queues up ping command to MPM. It is an unsigned int so 2 bytes
Wire.endTransmission();// sends the 2 bytes to the MPM
delay(2000); //give MPM_ time to process command, run ping, and have results waiting. I measured 0.6 seconds.
//next, read back ping result. If MPM_ does not respond with anything for more than 10 seconds, call it PingToMPM_TimedOutUInt.
unsigned int PingStartTimeUInt = millis();
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
Wire.requestFrom(0xA,2);// request 2 bytes from MPM_ device #0xA for return code.
while (Wire.available() < 1) if((millis()-PingStartTimeUInt)> 10000)
{
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
return PingToMPM_TimedOutUInt;//this was not necessarily a response related to ping but if nothing comes back from MPM_, we have detected ping fault
}
while (Wire.available() > 0)
{
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
ResponseLowByte = Wire.read(); //this assumes low byte arrives first
ResponseHighByte = Wire.read();
MPM_ResponseUInt = word(ResponseHighByte,ResponseLowByte);//assemble response from MPM
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" MPM_ResponseUInt is "));
Serial.println( MPM_ResponseUInt );
#endif
}
if(MPM_ResponseUInt == 0xFF00)return MPM_DidNotRespondToRequestForDataUInt;
return MPM_ResponseUInt;
// the MPM_ResponseUInt is just the return code. Given that ping is the active command, only ping results should be in return code.
} //end of PingUInt
unsigned int Fdr::loopback()
{
if(enableLoopAroundQbool)
{
ModemCommandArrayByte[0] = lowByte(setLoopAroundUInt);
ModemCommandArrayByte[1] = highByte(setLoopAroundUInt);
}else
{
ModemCommandArrayByte[0] = lowByte(clearLoopAroundUint);
ModemCommandArrayByte[1] = highByte(clearLoopAroundUint);
#ifdef modemNGtest
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ModemCommand byte 1 = "));
Serial.println( ModemCommandArrayByte[1] );
Serial.print(F(" ModemCommand byte 0 = "));
Serial.println( ModemCommandArrayByte[0] );
#endif
}
Wire.beginTransmission(0xA); // take ownership of I2C bus and begin session with the MPM which is the MPM_
Wire.write(ModemCommandArrayByte,2);// queues up ping command to MPM. It is an unsigned int so 2 bytes
Wire.endTransmission();// sends the 2 bytes to the MPM
//delay(2000); //give MPM_ time to process command, run ping, and have results waiting. I measured 0.6 seconds. This is left over from making loopback() out of ping()
//there is no direct response from modem, it should just start or stop doing loopback
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
if(enableLoopAroundQbool)
{
return dataLoopAroundEnabledUInt;
}else
{
return dataLoopAroundDisabledUInt;
}
} //end of loopback()
unsigned int Fdr::SetUpModem()
{
IridiumModemOperationalBool = false;
ModemCommandArrayByte[0] = lowByte(SetUpModemUInt);
ModemCommandArrayByte[1] = *nearFarModemSNbyte;
//orginal design sent highByte(SetUpModemUInt). I needed to change the design so I could send the near and far modem User's sn to Iridium.ino so chose to hide it here. This minimizes the disturbance of the code.
Wire.beginTransmission(0xA); // take ownership of I2C bus and begin session with the MPM which is the MPM_
Wire.write(ModemCommandArrayByte,2);// queues up SET UP MODEM command to MPM. It is an unsigned int so 2 bytes
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
Serial.print(F("ModemCommandArrayByte[1] (nearFarByte) = "));
Serial.println(ModemCommandArrayByte[1]);
#endif
Wire.endTransmission();// sends the 2 bytes to the MPM
//Serial.println(__LINE__);
//modem setup command sent to MPM. Now keep reading back until MPM says it is done with modem setup
delay(5000);//give MPM time to set up the modem
//ask MPM for result of setting up the modem
Wire.requestFrom(0xA,2);// request 2 byte return code from MPM_ device #0xA.
ReadbackStartTime = millis();
while(Wire.available() < 1)
{
//no response yet
if ((millis() - ReadbackStartTime) > 10000)
{
MPM_ResponseUInt = ModemFailedAtSetupUInt;//the lack of a response is the response.
return ModemFailedAtSetupTimeOutUInt;
}
}
while (Wire.available() > 0)
{
/*
Serial.print(F("07 line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
*/
ResponseLowByte = Wire.read(); // this assumes low byte arrives first
ResponseHighByte = Wire.read();//
MPM_ResponseUInt = word(ResponseHighByte,ResponseLowByte);//assemble response from MPM
}
#ifdef modemNGtest
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" MPM_ResponseUInt = "));
Serial.println( MPM_ResponseUInt );
#endif
if(MPM_ResponseUInt == 0xFF00)
{
ReturnCodeUInt = MPM_DidNotRespondToRequestForDataUInt;
return MPM_DidNotRespondToRequestForDataUInt;
}
if(MPM_ResponseUInt == ModemReadyForUseUInt)
{
IridiumModemOperationalBool = true;
/* modem ready for use so select far modem's RB serial number.
The array modemSNbyte[]'s pointer was passed in the pointer array and in Fdr.cpp I use this pointer to define the location of modemSNbyte[] which will point to the same information. Element 1 is the far RB serial number, farRBsnUInt which will be used during the transmition of data */
}
//return ModemReadyForUseUInt;
//}
/*
if(MPM_ResponseUInt == ModemFailedAtSetupUInt)return ModemFailedAtSetupUInt;
if(MPM_ResponseUInt == WrongModemConnectedCheckSerialNumberUInt)return WrongModemConnectedCheckSerialNumberUInt;
//any other MPM_ResponseUInt is status so for now just keep asking until setup of modem completes
//Wait 1 seconds and ask MPM_ again for setup status.
delay(1000);
*/
ReturnCodeUInt = MPM_ResponseUInt;
return ReturnCodeUInt;
}//end of SetUpModem()
unsigned int Fdr::PerformTransmit()
{
//The command PerformTransmitUInt is sent to the MPM along with a 32 and 13 byte array. Return code is set to SentPerformTransmitUInt
ModemCommandArrayByte[0] = lowByte(PerformTransmitUInt);
ModemCommandArrayByte[1] = highByte(PerformTransmitUInt);
Wire.beginTransmission(0xA); // take ownership of I2C bus and begin session with the MPM which is the MPM_
Wire.write(ModemCommandArrayByte,2);// queues up student selected command to MPM. It is an unsigned int so 2 bytes
Wire.endTransmission();//send command and release the bus
#ifdef ebugLoopAround
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ModemCommand is "));
Serial.println( PerformTransmitUInt );
#endif
Wire.beginTransmission(0xA);
for (byte i = 0; i<32;i++)//USART buffer capacity is 32 characters. Send bytes 0-31 of the array to be transmitted
{
Wire.write((*IridiumTransmitDataByte)[i]);// queues up array to be transmitted by the modem
}
Wire.endTransmission();// sends 32 bytes to the MPM and releases the bus
//delay(100);
Wire.beginTransmission(0xA);
for (byte i = 32; i<45;i++)//USART buffer capacity is 32 characters. Send bytes 32-44.
{
Wire.write((*IridiumTransmitDataByte)[i]);// queues up array to be transmitted by the modem
}
Wire.endTransmission();// sends 13 bytes to the MPM and releases the bus
//delay(100);
return SentPerformTransmitUInt;
}//end of PerformTransmit()
unsigned int Fdr::getReceivedData()
{
//ask MPM to send back first sub array which is 32 bytes
ModemCommandArrayByte[0] = lowByte(SendBackFirstBlockOfReceivedArrayUInt );
ModemCommandArrayByte[1] = highByte(SendBackFirstBlockOfReceivedArrayUInt );
#ifdef ebugLoopAround
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ModemCommandArrayByte[0] = "));
Serial.println( ModemCommandArrayByte[0] );
Serial.print(F(" ModemCommandArrayByte[1] = "));
Serial.println( ModemCommandArrayByte[1]);
#endif
Wire.beginTransmission(0xA); // take ownership of I2C bus and begin session with the MPM
Wire.write(ModemCommandArrayByte,2);// queues up SendBackFirstBlockOfReceivedArrayUInt command to MPM. It is an unsigned int so 2 bytes
#ifdef ebugLoopAround
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
Wire.endTransmission();// sends the 2 bytes to the MPM. The MPM will then know what to return when asked for data
//Next, ask slave for 13 bytes of received data
delay(20);//give time for MPM to receive command, set related flag, and fill first array of data with 32 bytes
FlushWireBuffer();//be sure receive buffer is empty
Wire.requestFrom(0xA,32);//request received array from MPM. USART can handle a maximum of 32 bytes. 0xA is 0b1010. The EEPROM has an address of 00 and the SEEPROM has an address of 01 so there is no address conflict here.
#ifdef ebugLoopAround
Serial.println(F("from MPM. First 32 bytes"));
#endif
unsigned int startOfWaitForMPMresponseUInt = millis();
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.println(F(" About to wait for 32 bytes of MPM data."));
Serial.print(F("Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
while (Wire.available() < 32) //wait until all 32 bytes arrives
{
if(millis() - startOfWaitForMPMresponseUInt > 100)
{
MPM_ResponseUInt = TimeOutWaitingForgetReceivedDataUInt;
return MPM_ResponseUInt;
}
//delay(500);
#ifdef ebugLoopAround
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.println(F(" waiting for 32 bytes of MPM data."));
#endif
}
//buffer has data so collect it
#ifdef ebugLoopAround
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F("Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.println(F(" 32 bytes of MPM data just read in."));
#endif
for ( i = 0; i < 32; i++)
{
(*IridiumReceiveDataByte)[i] = Wire.read();
#ifdef ebugLoopAround
Serial.print((*IridiumReceiveDataByte)[i]);
Serial.print(" ");
#endif
}
#ifdef ebugLoopAround
Serial.println();
#endif
delay(100);//let slave run a little before asking for second block of data. I'm not sure this is needed
//ask slave for second block of array
ModemCommandArrayByte[0] = lowByte(SendBackSecondBlockOfReceivedArrayUInt );
ModemCommandArrayByte[1] = highByte(SendBackSecondBlockOfReceivedArrayUInt );
#ifdef ebugLoopAround
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ModemCommandArrayByte[0] = "));
Serial.println( ModemCommandArrayByte[0] );
Serial.print(F(" ModemCommandArrayByte[1] = "));
Serial.println( ModemCommandArrayByte[1]);
#endif
Wire.beginTransmission(0xA); // take ownership of I2C bus and begin session with the MPM
Wire.write(ModemCommandArrayByte,2);// queues up SendBackSecondBlockOfReceivedArrayUInt command to MPM. It is an unsigned int so 2 bytes
#ifdef ebugLoopAround
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
Wire.endTransmission();// sends the 2 bytes to the MPM. The MPM will then know what to return when asked for data
//Next, ask slave for 13 bytes of received data
delay(20);//give time for MPM to receive command, set related flag, fill second array with received data
FlushWireBuffer();//be sure receive buffer is empty
Wire.requestFrom(0xA,13);//request second block of received array from MPM_ device #0xA. USART can handle a maximum of 32 bytes. Array is 45 bytes so got 32 on the first round and the remaining 13 on the second round
#ifdef ebugLoopAround
startOfWaitForMPMresponseUInt = millis();
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.println(F(" About to wait for 13 bytes of MPM data."));
Serial.print(F("Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
while (Wire.available() < 13) //wait until all 13 bytes arrives
{
if(millis() - startOfWaitForMPMresponseUInt > 100)
{
MPM_ResponseUInt = TimeOutWaitingForgetReceivedDataUInt;
return MPM_ResponseUInt;
}
//delay(500);
#ifdef ebugLoopAround
Serial.println(F("waiting for 13 bytes of MPM data."));
#endif
}
#ifdef ebugLoopAround
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.println(F(" 13 bytes of MPM data ready to read."));
Serial.print(F("Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
//buffer has data so collect it
for ( i = 32; i < 45; i++)//read in 13 bytes
{
(*IridiumReceiveDataByte)[i] = Wire.read();
#ifdef ebugLoopAround
Serial.print((*IridiumReceiveDataByte)[i]);
Serial.print(" ");
#endif
}
MPM_ResponseUInt = receiveDataPlacedInReceiveArrayUint; //all data received
return MPM_ResponseUInt;
}//end of getReceivedData()
void Fdr::DirectPath(bool HexQ)//isn't used
{
//directly connects terminal emulator to UART so used with GNSS. When run in modem's Pro Micro, it talks to the modem. Options are ASCII_Bool which equals false and HEX_Bool which equals true. They set the print format.
char character;
unsigned int DelayAfterDoingPrintToModemUInt = 20; //optionally used in print to serial1 below
while(1)
{
while(Serial1.available() > 0)
{
//delay(DelayAfterDoingPrintToModemUInt);
character = Serial1.read();
if (HexQ)
{
Serial.println(character, HEX);
}else{
Serial.print(character);
}
}
while(Serial.available() > 0)
{
character = Serial.read();
Serial1.print(character);
//delay(DelayAfterDoingPrintToModemUInt);
}
}
}
void Fdr::setupGPS()
{
/*******************************************
Input: GPSbaudRateUInt defined at top of program
Output: UART set to this rate
*******************************************/
#ifdef newGPScodeDiag
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(" T"));
Serial.println (millis() - StartForTimeStampULong);
#endif
Serial1.begin(GPSbaudRateUInt); //define what rate the UART will expect from GPS
delay(10);//time for hardware to configure
}//end of setupGPS()
void Fdr::GlobalNavigationSatelliteSystem()
{
/*******************************************
This subroutine will parce its GPGGA sentence into bytes:
(*NavigationDataByte)[16] which consists of:
BYTE DESCRIPTION
0 hours (UTC)
1 minutes (UTC)
2 seconds (UTC)
3 degrees (latitude)
4 minutes (latitude)
5 seconds (latitude)
6 0 for north, 1 for south
7 degrees (longitude)
8 minutes (longitude)
9 seconds (longitude)
10 0 for east, 1 for west
11 altitude MSB (byte 3) See also Alititude()
12 altitude (byte 2)
13 altitude (byte 1)
14 altitude LSB(byte 0)
15 units: 0 for meters, 1 for feet
The user must look at UTC in order to know if this is new data.
Array is returned with 200 for any field that is null. If $GPGGA sentence not found, all fields set to 200.
Hours = 201 means we timed out waiting for data from GNSS.
north/south and east/west fields set to 202 means unexpected value read.
Hours = 203 means correct header could not be found
These values were chosen because they are out of range for all bytes and is also not a symptom of a hardware fault which can occur with the value 255.
**********************************************/
#ifdef gpsRunTimeStudy
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
InitializeNavigationDataByteArray();//set all elements to 205
BufferText = "";
#ifdef gpsRunTimeStudy
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
/*
if(GNSS_TimedOutWaitingForDataBool)//means we previously timed out waiting for GNSS sentence's start symbol or any characters
{
#ifdef GNSSprint
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
if(GNSS_TimeToRetryReadingDataQBool)
{
InitializeNavigationDataByteArray();//all elements set to 200
GNSS_TimedOutWaitingForDataBool = false;//this will cause us to look for $ again.
GNSS_TimeToRetryReadingDataQBool = false;//used up flag so clear it.
GNSS_StartOfWaitingForDataMsULong = millis();//init timer
#ifdef GNSSprint
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
return;//with GNSS_TimedOutWaitingForDataBool cleared, we will look for a sentence the next time around
}
(*NavigationDataByte)[0] = 201;//tells user not to trust the array because we did not see the sentence
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
return;//we previously timed out looking for start of sentence delimiter("$") so just return.
}
*/
#ifdef GNSSprint
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
FindNeededSentence(); //scans for start of a sentance and then for the proper header. If it can't find it, element 0 is set to 201
#ifdef gpsRunTimeStudy
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
if((*NavigationDataByte)[0] == 201)
{//we have a hardware problem
return;
}
if (GNSS_TimedOutWaitingForDataBool)
{
#ifdef GNSSprint9
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
//return; //timed out Removed so user can try gps again even if it failed last time. Phylosophy issue.
}
//to get here, we must have correct sentence's characters
#ifdef gpsRunTimeStudy
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
//InitializeNavigationDataByteArray();//initialize array to 200. Parcing code should be setting each element to either received data, 200, or 201.
/************************************************************
D A T A S T R U C T U R E O F S E N T E N C E
*************************************************************
Sample Characters Quantity Count value or variable
, delimiter 0
15 hours 1 & 2 or "," if UTC is null
01 minutes 3 & 4
16 seconds 5 & 6
. decimal 7
00 fractional seconds 8 thorugh A-1
, delimiter A
33 degrees lat A+1 & A+2
17 minutes lat A+3 & A+4
. decimal A+5
56410 fractional minutes lat A+6 through B-1
, delimiter B
N north/south B+1
, delimiter C
112 degrees lon C+1 thorugh C+3
05 minutes lon C+4 & C+5
. decimal C+6
05327 fractional minutes lon C+7 through D-1
, delimiter D
W east/west D+1
, delimiter E
2,09,1 ignored fields
. decimal
02 ignored fields
, delimiter F
362 altitude F+1 through G-1 or ","
. decimal G can be "." or ","
2 fractional (ignore) H-1
, delimiter H
M altitude units H+1
,-28.1,M,,0000*62 ignored fields
all empty fields will cause 200 to be put in corresponding output array element.
*******************************************************/
//parce BufferText string
IncompleteSentenceBool = false;
ParceTime();
if (IncompleteSentenceBool)return;
ParceLatitude();
if (IncompleteSentenceBool)return;
ParceLongitude();
if (IncompleteSentenceBool)return;
SkipFourFields();
if (IncompleteSentenceBool)return;
ParceAltitude();
if (IncompleteSentenceBool)return;
#ifdef gpsRunTimeStudy
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
#ifdef DiagPrint47
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
for(i = 0;i<16;i++)
{
Serial.print( (*NavigationDataByte)[i] );
Serial.print(F(","));
}
Serial.println();
#endif
}//At end of GlobalNavigationSatelliteSystem()
void Fdr::PrepareTransmitArrayForModem() //diag
{
byte count;
//put GNSS data in transmit array
for(count = 0; count < 16; count++)
{
(*IridiumTransmitDataByte)[count] = (*NavigationDataByte)[count];
}
int BatteryReadingInt = analogRead(LogicalAnalogPinByte[7]); //battery voltage 10 bits of data
BatteryReadingInt = BatteryReadingInt*2;//compensate for voltage divider
(*IridiumTransmitDataByte)[16] = highByte(BatteryReadingInt);
(*IridiumTransmitDataByte)[17] = lowByte(BatteryReadingInt);
for (count = 18;count <45;count++)
{
(*IridiumTransmitDataByte)[count] = count;//diag
}
for(count = 0;count<45;count++)
{
Serial.print((*IridiumTransmitDataByte)[count]);
Serial.print(",");
}
Serial.println();
}
void Fdr::ProcessReturnCodes()//this fcn not used
{
//runs after RequestDataTransmissionSession() and deals with ReturnCodes generated by it
/************************************************
R E T U R N C O D E S
************************************************
SentPerformTransmitUInt
DuplicateTransmitOfDataAttemptedUInt
NoFunctioningModemPresentUInt
*************************************************/
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
if ((ReturnCodeUInt >= FailureRangeMinUInt) && (ReturnCodeUInt <= FailureRangeMaxUInt))
{
//request that transmit be initiated again
RequestTransmitRestartBool = true;
return;
}
if ((ReturnCodeUInt >= TransmitSuccessRangeMinUInt)&& (ReturnCodeUInt <= SuccessRangeMaxUInt))TransmitSuccessfulBool = true;//used to control timer that determines the soonest user can transmit again. See ModemTransmitTiming()
}
/* function no longer needed because if user requests transmits too often, the modem will send back error codes
void Fdr::ModemTransmitTiming()
{
unsigned int ModemTimeNowSecondsUInt = millis()/1000;
if(RequestTransmitRestartBool)
{
LastSuccessfulTransmitTimeSecondsUInt = ModemTimeNowSecondsUInt - MinimumTransmitCycleTimeSecondsUInt - 1;//this ensures we next set the TimeToTransmitBool.
#ifdef DiagPrintModem
Serial.print(F("line number ")); Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
}
if ((ModemTimeNowSecondsUInt - LastSuccessfulTransmitTimeSecondsUInt) > MinimumTransmitCycleTimeSecondsUInt)
{
TimeToTransmitBool = true;
#ifdef DiagPrintModem
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
return;
}
if (TransmitSuccessfulBool)
{
TransmitSuccessfulBool = false;
LastSuccessfulTransmitTimeSecondsUInt = ModemTimeNowSecondsUInt;
#ifdef DiagPrintModem
Serial.print(F("line number "))
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
}
}//end of ModemTransmitTiming()
*/
void Fdr::GNSS_Timing()
{
/*code not used
/************************************************************
There are two timers here. When the user requests GPS data, we l If the GPS doesn't send us data
Set GNSS_TimeToRetryReadingDataQBool true after GNSS_DelayBeforeRetryOfReadingDataMsUInt last lapsed since the last time it was set false. It is used to test for presence of GNSS receiver after it has timed out. This is necessary if connection to device is intermittent and we don't want to just give up right away. After 4 retries, we do give up.
#ifdef DiagPrint3
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
if (GNSS_TimeToRetryReadingDataQBool == false)
{//flag says it is not time to retry accessing GNSS but will check timer to see if it is now time to do it
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
if((millis() - LastTimeWeDidA_RetryOfReadingDataMsUInt) > GNSS_DelayBeforeRetryOfReadingDataMsUInt)
{//we have waited long enough, try accessing the GNSS again
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
GNSS_TimeToRetryReadingDataQBool = true; //set flag to retry GNSS
LastTimeWeDidA_RetryOfReadingDataMsUInt = millis();//mark the time of this last retry
GNSS_RetryCounterByte = GNSS_RetryCounterByte +1;//increment counter; When GlobalNavigationSatelliteSystem() called, it will test count and if > GNSS_RetryCountLimitByte, give up trying to retry GNSS access. RGS1.5
}
}
*/
}
void Fdr::ReadHeader()
/**************************************************************
If no data waiting in UART buffer, it just returns.
If data is waiting, it puts the first 5 characters into BufferText and returns.
If there is less than 5 characters in the buffer, it returns with what it found in BufferText.
Output
BufferText - will be "" if no data was in the UART buffer; will be up to 5 characters if available.
*************************************************************/
{
count = 0;
BufferText = "";
#ifdef DiagPrint3
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" BufferText = "));
Serial.print( BufferText );
Serial.println("*");
#endif
while(count < 5)
{//record header which is 5 characters
while(Serial1.available() < 1)//buffer now empty
{
if((millis()-GNSS_StartOfWaitingForDataMsULong) > GNSS_WaitingForDataTimeLimitMsULong)
{
GNSS_TimedOutWaitingForDataBool = true;//timed out waiting for characters
(*NavigationDataByte)[0] = 201;//flags that this array should not be trusted because header not found
#ifdef DiagPrint3
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
return;//nothing in buffer so give up and don't wait for data
}
}
//buffer has data
character = Serial1.read();
#ifdef DiagPrint3
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" character = "));
Serial.println( character );
#endif
BufferText.concat(character);
count++;
}
//we now have 5 characters from GPS stored in BufferText and it should be the header
#ifdef DiagPrint3
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" BufferText = "));
Serial.println( BufferText );
#endif
}
void Fdr::ReadSentence()
{
/************************************************************
Input: none
Output: BufferText filled with read in characters except the end of sentence delimeter is dropped
************************************************************/
BufferText = "";//prepare to record all characters starting after header
count = 0;
while(AtEndOfNeededSentenceBool == false)
{//record all characters after header until carriage return
while(Serial1.available() < 1)
{
if((millis()-GNSS_StartOfWaitingForDataMsULong) > GNSS_WaitingForDataTimeLimitMsULong)
{
GNSS_TimedOutWaitingForDataBool = true;//timed out waiting for characters
(*NavigationDataByte)[0] = 201;//flags that this array should not be trusted because header not found
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
return;
}
}
//only get here when buffer has data
character = Serial1.read();
if (character == '\r')
{
AtEndOfNeededSentenceBool = true;//found carriage return so at end of the sentence. Setting this flag releases us from the while()
}else{
BufferText.concat(character);//if not at end of sentence, keep recording
}
}//now have all characters in needed sentence in BufferText
}//end of ReadSentence()
void Fdr::ParceTime()
{
//we are walking through the string BufferText
#ifdef debugParceTime1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
//parce hour UTC
count = 0;//delimiter position before hours UTC which is the first character in the string BufferText
#ifdef debugParceTime1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
if(BufferText[1] == ',')//UTC field is null
{
#ifdef debugParceTime1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
(*NavigationDataByte)[0] = 200;//null UTC so corresponding output array element for hours is set to 200
(*NavigationDataByte)[1] = 200;//null UTC so corresponding output array element for minutes is set to 200
(*NavigationDataByte)[2] = 200;//null UTC so corresponding output array element for minutes is set to 200
IncompleteSentenceBool = true;
return;
}else{
//UTC is not null so process it
#ifdef debugParceTime1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
(*NavigationDataByte)[0] = (((BufferText[1] - 0x30)*10) + (BufferText[2] - 0x30));//convert each ASCII character to a number and then use it to build the hour value
#ifdef debugParceTime1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
//do range check to protect against unexpected field values
if((*NavigationDataByte)[0] > 23)(*NavigationDataByte)[0] = 200;
#ifdef debugParceTime
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
//parce minutes UTC
(*NavigationDataByte)[1] = ((BufferText[3] - 0x30)*10) + (BufferText[4] - 0x30);
//do range check to protect against unexpected field values
if((*NavigationDataByte)[1] > 59)(*NavigationDataByte)[1] = 200;
//parce seconds UTC
(*NavigationDataByte)[2] = ((BufferText[5] - 0x30)*10) + (BufferText[6] - 0x30);
//do range check to protect against unexpected field values
#ifdef debugParceTime
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" BufferText[5] = "));
Serial.println( BufferText[5] );
Serial.print(F(" BufferText[6] = "));
Serial.println( BufferText[6] );
Serial.print(F(" (*NavigationDataByte)[2] = "));
Serial.println( (*NavigationDataByte)[2] );
#endif
if((*NavigationDataByte)[2] > 59)(*NavigationDataByte)[2] = 200;
count = 7;//puts us at either "." if just before fractional seconds or "," for field delimiter
#ifdef debugParceTime
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" seconds = "));
Serial.println( (*NavigationDataByte)[2] );
#endif
//parce fractions of a second UTC. I don't use it but need to advance count to next delimiter
if(BufferText[count] == ',')
{//if there is no fractional part, we see "," right away so "A" is 7.
#ifdef debugParceTime
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
}else{
//there is a fractional part
while(BufferText[count] != ',')
{
if(count>30)break;//protect against garbage in bufferText hanging program
#ifdef debugParceTime1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F("count = "));
Serial.println( count );
Serial.print(F(" BufferText[count] = "));
Serial.println( BufferText[count] );
#endif
count++;//search for delimiter and advance count along field. Exit just before ',' with count at A-1
}
//we normally exit while() at delimter which is count of "A" but are forced out when count is more than 30.
}
}
}//end of ParceTime()
void Fdr::ParceLatitude()
{
//parce degrees latitude
CountPlusOne = count+1; //at A+1 if field not null and B if it is null
if(BufferText[CountPlusOne] == ',')
{//latitude field is null so fill each byte with 200
(*NavigationDataByte)[3] = 200;
(*NavigationDataByte)[4] = 200;
(*NavigationDataByte)[5] = 200;
IncompleteSentenceBool = true;
return;
}else{
//process degrees lattitude field
CountPlusOne = count+1;//most significant digit "A+1"
CountPlusTwo = count+2;//least significant digit "A+2"
(*NavigationDataByte)[3] = ((BufferText[CountPlusOne] - 0x30)*10) + (BufferText[CountPlusTwo] - 0x30);
count = count + 3;//now at start of minutes latitude sub-field.
//do range check to protect against unexpected field values
if((*NavigationDataByte)[3] > 90)(*NavigationDataByte)[3] = 200;
#ifdef DiagPrintGNSS
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" (*NavigationDataByte)[3] = "));
Serial.println( (*NavigationDataByte)[3] );
#endif
//parce minutes latitude
//count is most significant digit which is "A+3"
CountPlusOne = count+1;//least significant digit "A+4"
(*NavigationDataByte)[4] = ((BufferText[count] - 0x30)*10) + (BufferText[CountPlusOne] - 0x30);
count = count+2;//now at "A+5" if this is the decimal before fractional minutes latitude or at "B" if no fractional part, the delimiter before North/South field.
//do range check to protect against unexpected field values
if((*NavigationDataByte)[4] > 59)(*NavigationDataByte)[4] = 200;
float FractionalMinutesFloat = 0;
if(BufferText[count] == '.')//this means we have a fractional minute. If none, we just use FractionalMinutesFloat = 0
{
//parce fractional minutes latitude and convert to seconds
count = count+1;//now at A+6, the most significant digit of fracctional minutes
EndOfField = count;//prepare to search for end of fractional minutes sub-field which will be "B-1"
//Serial.println(__LINE__);
while(BufferText[EndOfField] != ',')
{
if(EndOfField > 200)
{
Serial.println("stuck1");
break;//prevents program from hanging up
}
EndOfField++;
}
//search for delimiter and advance EndOfField along sub-field. Exit at "," with count at "B". The number we need to process starts at count and ends at EndOfField-1
//Serial.println(__LINE__);
float DividerFloat = 10;//scales each digit
for(pointer = count; pointer < (EndOfField); pointer++)
{//pointer goes from A+6 to B-1
FractionalMinutesFloat = FractionalMinutesFloat + float((BufferText[pointer]- 0x30)/DividerFloat);
DividerFloat = DividerFloat*10;//prepare to scale next digit
}
(*NavigationDataByte)[5] = byte(FractionalMinutesFloat*60);//convert from fractional minutes to integer seconds
//do range check to protect against unexpected field values
if((*NavigationDataByte)[5] > 59)(*NavigationDataByte)[5] = 200;
count = EndOfField;//update character count to delimiter just before north/south field. Now at B.
}else{
//there is no fractional minutes so seconds latitude is simply 0
// character count is at delimiter just before north/south field. Now at B.
(*NavigationDataByte)[5] = 0;
}
}
count = count+1;//advance count to N/S field "B+1"
#ifdef DiagPrintGNSS
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" count = "));
Serial.println( count );
#endif
//parce north/south
switch(BufferText[count]){
case ',':
(*NavigationDataByte)[6] = 200;//means null field
count = count + 2;//character count set to delimiter just before degrees longitude B+2
break;
case 'N':
(*NavigationDataByte)[6] = 0;//0 means north
count = count +1;//advance to delimiter just before degrees longitude "C"
break;
case 'S':
(*NavigationDataByte)[6] = 1;//1 means south
count = count +1;//advance to delimiter just before degrees longitude "C"
break;
default:
(*NavigationDataByte)[6] = 202;//means unexpected value
count = count +1;//advance to delimiter just before degrees longitude "C"
break;
}
/*if(BufferText[count] == ',')//at B+1
{//null field
(*NavigationDataByte)[6] = 200;//means null field
count = count + 2;//character count set to delimiter just before degrees longitude B+2
}else{
if(BufferText[count] == 'N')//at B+1
{
(*NavigationDataByte)[6] = 0;//0 means north
}
if(BufferText[count] == 'S')//at B+1
{
(*NavigationDataByte)[6] = 1;//1 means south
}
count = count +1;//advance to delimiter just before degrees longitude "C"
}*/
}//end of ParceLatitude()
void Fdr::ParceLongitude()
{
//parce degrees longitude
count = count+1;//advance to most significant digit C+1
CountPlusOne = count+1;//C+2
CountPlusTwo = count+2;//C+3
#ifdef DiagPrint1
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" count = "));
Serial.println( count );
Serial.print(F(" BufferText[count] = "));
Serial.println( BufferText[count] );
Serial.print(F(" BufferText[CountPlusOne] = "));
Serial.println( BufferText[CountPlusOne] );
Serial.print(F(" BufferText[CountPlusTwo] = "));
Serial.println( BufferText[CountPlusTwo] );
#endif
if((BufferText[count] == ',')|| (BufferText[CountPlusOne]==','))
{
IncompleteSentenceBool = true;
return;
}
(*NavigationDataByte)[7] = ((BufferText[count]- 0x30)*100) + ((BufferText[CountPlusOne]- 0x30)*10) + (BufferText[CountPlusTwo]- 0x30);
//do range check to protect against unexpected field values
if((*NavigationDataByte)[7] > 180)(*NavigationDataByte)[7] = 200;
#ifdef DiagPrintGNSS
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" (*NavigationDataByte)[7] = "));
Serial.println( (*NavigationDataByte)[7] );
#endif
count = count + 3;//advance to most significant digit of minutes longitude. C+4
//parce minutes longitude
CountPlusOne = count+1;//C+5
(*NavigationDataByte)[8] = ((BufferText[count]- 0x30)*10) + (BufferText[CountPlusOne]- 0x30);
//do range check to protect against unexpected field values
if((*NavigationDataByte)[8] > 59)(*NavigationDataByte)[8] = 200;
//parce seconds longitude
count = count + 2;//advance to start of fractional minutes longitude which starts with "." if there is any fraction ("C+6") or with "," to signal the end of the field ("D").
EndOfField = count;
if(BufferText[EndOfField] == '.')
{//we have a fractional part of minutes longitude
while(BufferText[EndOfField] != ',')
{
if(EndOfField > 200)
{
Serial.println("stuck2");
break;//prevents program from hanging up
}
EndOfField++;
}
//search for delimiter and advance EndOfField along field. Exit at "," with EndOfField at "D". The number we need to process starts at count+1 ("C+7") and ends at EndOfField-1 ("D-1")
//Serial.println(__LINE__);
count = count+1;//at start of fractional part
float DividerFloat = 10;//scales each number
FractionalMinutesFloat = 0;
for(pointer = count; pointer < EndOfField; pointer++)
{
#ifdef DiagPrintGNSS
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" pointer = "));
Serial.println( pointer );
Serial.print(F(" BufferText[pointer] = "));
Serial.println( BufferText[pointer] );
Serial.print(F(" DividerFloat = "));
Serial.println( DividerFloat );
#endif
FractionalMinutesFloat = FractionalMinutesFloat + float((BufferText[pointer]- 0x30)/DividerFloat);
DividerFloat = DividerFloat*10;//prepare to scale next digit
#ifdef DiagPrintGNSS
Serial.print(F(" FractionalMinutesFloat = "));
Serial.println( FractionalMinutesFloat );
#endif
}
(*NavigationDataByte)[9] = byte(FractionalMinutesFloat*60);//convert from fractional minutes to integer seconds
//do range check to protect against unexpected field values
if((*NavigationDataByte)[9] > 59)(*NavigationDataByte)[9] = 200;
count = EndOfField;//advance to delimiter just before East/West field "D"
}else{
//we do not have a fractional part of minutes longitude
(*NavigationDataByte)[9] = 0;//there is no fractional part of minutes longitude so seconds equals 0. Note that this is not null (200)
//count is C+6 but since fractional minutes sub-field is empty, we are at the delimiter so this count is also "D".
}
//parce east/west
CountPlusOne = count+1;//D+1
CountPlusTwo = count+2;
#ifdef DiagPrintGNSS
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" count = "));
Serial.println( count );
Serial.print(F(" BufferText[CountPlusOne] = "));
Serial.println( BufferText[CountPlusOne] );
#endif
switch(BufferText[CountPlusOne]){
case ',':
//East/West field is empty
IncompleteSentenceBool = true;
return;
case 'E':
(*NavigationDataByte)[10] = 0;//0 means east
count = count+2;//at delimeter after normal East/West ("E")
break;
case 'W':
(*NavigationDataByte)[10] = 1;//1 means west
count = count+2;//at delimeter after normal East/West ("E")
break;
default:
(*NavigationDataByte)[10] = 202;//means unexpected value
IncompleteSentenceBool = true;
return;
}
if(BufferText[CountPlusTwo] != ',')
{//unexpected character after E/W field. This check was added because I saw this anomoly once.
IncompleteSentenceBool = true;
return;
}
}//end of ParceLongitude()
void Fdr::SkipFourFields()
{
//next, advance past 4 delimiter. Count is now at delimiter just after east/west field which is "E". We will increment count until we get to the 4th delimiter which is just before the altitude field.
FieldCountByte = 0;
while(FieldCountByte < 4)
{
if(BufferText[count] == ',')FieldCountByte = FieldCountByte +1;//if we find a ',', increment the count until we have traversed 4 fields which we are ignoring
count=count+1;//advance to next character
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" count = "));
Serial.println( count );
Serial.print(F(" BufferText[count] = "));
Serial.println( BufferText[count] );
#endif
}
}//end of SkipFourFields()
void Fdr::ParceAltitude()
{
StartOfAltitudeByte = count;//count is now at the first digit of the altitude field which is "F+1"
//start including digits until we reach either "." or ","
EndOfFieldByte = StartOfAltitudeByte;// "F+1"
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" StartOfAltitudeByte = "));
Serial.println( StartOfAltitudeByte );
Serial.print(F(" BufferText[StartOfAltitudeByte] = "));
Serial.println( BufferText[StartOfAltitudeByte] );
#endif
if(BufferText[StartOfAltitudeByte] == ',')//if true, we are at H and altitude field is null
{
IncompleteSentenceBool = true;
return;
}else{
//altitude field is not null but may not have a fractinal part
while((BufferText[EndOfFieldByte] != '.') && (BufferText[EndOfFieldByte] != ','))
{
EndOfFieldByte++;//search for decimal and comma and if neither seen, advance EndOfField along field. Exit at "G" if decimal found or "H" if "," found. The numbers we need to process starts at count (F+1) and ends at EndOfFieldByte-1.
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" EndOfFieldByte = "));
Serial.println( EndOfFieldByte );
Serial.print(F(" BufferText[EndOfFieldByte] = "));
Serial.println( BufferText[EndOfFieldByte] );
#endif
}
EndOfFieldByte = EndOfFieldByte -1;//must back up one count because the above logic takes us to the decimal or delimiter
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" EndOfFieldByte = "));
Serial.println( EndOfFieldByte );
Serial.print(F(" BufferText[EndOfFieldByte] = "));
Serial.println( BufferText[EndOfFieldByte] );
#endif
AltitudeULong = 0;
long MultiplierLong = 1;//scales each number
for(pointer = EndOfFieldByte; pointer >= StartOfAltitudeByte; pointer--)
{//move from right (G-1) to left (F+1)
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" pointer = "));
Serial.println( pointer );
Serial.print(F(" BufferText[pointer] = "));
Serial.println( BufferText[pointer] );
Serial.print(F(" MultiplierLong = "));
Serial.println( MultiplierLong );
#endif
AltitudeULong = AltitudeULong + long((BufferText[pointer] - 0x30) * MultiplierLong);
MultiplierLong = MultiplierLong * 10;//advance to next highest number
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
//Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" AltitudeULong = "));
Serial.println( AltitudeULong );
#endif
}
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" AltitudeULong = "));
Serial.println( AltitudeULong );
#endif
/*******************************
transfer AltitudeULong into array as 4 bytes
byte description
11 altitude MSB (byte 3)
12 altitude (byte 2)
13 altitude (byte 1)
14 altitude LSB(byte 0)
*********************************/
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
(*NavigationDataByte)[11] = byte(AltitudeULong >> 24);//MSB: byte 3
(*NavigationDataByte)[12] = byte(AltitudeULong >> 16);//byte 2
(*NavigationDataByte)[13] = byte(AltitudeULong >> 8);// byte 1
(*NavigationDataByte)[14] = byte(AltitudeULong);//LSB: byte 0
}
count = EndOfFieldByte + 1;//advance to character after integer alititude "G" or "H"
//ignore fractional altitude and advance count to next field
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" count = "));
Serial.println( count );
#endif
GNSS_StartOfWaitingForDataMsULong = millis();
while(BufferText[count] != ',')
{
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" count = "));
Serial.println( count );
Serial.print(F(" BufferText[count] = "));
Serial.println( BufferText[count] );
#endif
count = count +1;
if((millis()-GNSS_StartOfWaitingForDataMsULong) > GNSS_WaitingForDataTimeLimitMsULong)
{
IncompleteSentenceBool = true;
return;
}
}
//now we are at the delimeter preceeding units field: "H"
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
CountPlusOne = count+1;//H+1
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" CountPlusOne = "));
Serial.println( CountPlusOne );
Serial.print(F(" BufferText[CountPlusOne] = "));
Serial.println( BufferText[CountPlusOne] );
#endif
switch(BufferText[CountPlusOne]){//units field
case ',':
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
IncompleteSentenceBool = true;
return;
case 'M':
(*NavigationDataByte)[15] = 0;//0 means metric
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
break;
case 'f':
(*NavigationDataByte)[15] = 1;//1 means feet but we this is not in the standards for the GPGGA sentence
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
break;
default:
(*NavigationDataByte)[15] = 202;//means unexpected value
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
IncompleteSentenceBool = true;
return;
}
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" (*NavigationDataByte)[15] = "));
Serial.println( (*NavigationDataByte)[15] );
#endif
}//end of ParceAltitude()
void Fdr::FindNeededSentence()
/************************************************
Scan Serial1, the UART, looking for the needed sentence which starts with $ and is followed by header. If buffer still empty within 20 ms, set first element of array to 201 saying GPS seems to be dead. If correct header found, ReadSentence() fills BufferText with the data from the sentence, dropping the end of sentence delimiter.
202 means unexpected value in a data field
If correct header cannot be found in 100 ms, first element is set to 203 and implies a hardware problem.
*************************************************/
{
unsigned long GNSS_StartOfCollectDataMsULong = 0;//set to millis() when used
GNSS_StartOfWaitingForDataMsULong = millis();
#ifdef gpsRunTimeStudy
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
AtEndOfNeededSentenceBool = false;//when true, we exit the following while
while(AtEndOfNeededSentenceBool == false)
{//keep reading characters from GNSS until $GPGGA is detected or we time out
if((millis() - GNSS_StartOfWaitingForDataMsULong) > (GNSS_WaitingForDataTimeLimitMsULong))
{
(*NavigationDataByte)[0] = 203;//flags that this array should not be trusted because correct header could not be found.
#ifdef gpsRunTimeStudy
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
return;
}
unsigned long emptyBufferWaitStartTimeULong = millis();
while(Serial1.available() < 1)
{//Buffer is empty
if((millis()-emptyBufferWaitStartTimeULong) > GNSS_WaitingForDataTimeLimitMsULong)
{ //we will wait up to 4 seconds for something to be put in buffer before we give up
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
GNSS_TimedOutWaitingForDataBool = true;//timed out waiting for characters
(*NavigationDataByte)[0] = 201;//flags that this array should not be trusted because $ plus header not found after waiting 20 ms. Looks like GPS is not working
#ifdef GNSSprint
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
return;
}
}//when UART buffer has data, we continue.
GNSS_StartOfCollectDataMsULong = millis();//initialize timer to protect against hanging due to no useful data from GPS
#ifdef gpsRunTimeStudy
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
character = Serial1.read();//only get here when buffer has at least one character
#ifdef DiagPrint3
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F("+"));
Serial.println( character );
#endif
if (character == '$')
{//at start of a sentence. Now see if it is the one we need
#ifdef gpsRunTimeStudy
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
//GNSS_TimedOutWaitingForDataBool = false;//because we found $ but it will change to true if we can't find right header
ReadHeader();//if it reads the wrong header or a partial header, it still returns. If it doesn't see anything, it returns "".
#ifdef gpsRunTimeStudy
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
if (BufferText.indexOf("GPGGA") == 0)
{//found needed header
#ifdef gpsRunTimeStudy
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
ReadSentence();//it controls AtEndOfNeededSentenceBool flag so controls the while() above. BufferText is filled with read in characters except the end of sentence delimeter is dropped
#ifdef gpsRunTimeStudy
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
}//we didn't see wanted header after $ so continue
#ifdef gpsRunTimeStudy
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
}//we didn't see $ as read in character so continue
#ifdef gpsRunTimeStudy
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
}//bottom of while(AtEndOfNeededSentenceBool) so if we processed entire sentence and put the data in BufferText, we are done. Otherwise we will read next character looking for the $ followed by the header which signals the start of our data.
#ifdef gpsRunTimeStudy
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
}//end of FindNeededSentence()
#ifdef GNSStest
void Fdr::PrintOutGNSS_Array()
{//diag
#ifdef DiagPrint5
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
for(i = 0;i < 16;i++)
{
Serial.print( (*NavigationDataByte)[i] );
Serial.print(F(","));
}
Serial.println();
Initialize(*NavigationDataByte)Array();//done with array so initialize it.
}
#endif
void Fdr::InitializeNavigationDataByteArray()
{
for(i = 0;i < 16;i++)(*NavigationDataByte)[i] = 205;
}
void Fdr::Port6Peak()
{//RGS1.1
PortReadingInInt = analogRead(LogicalAnalogPinByte[6]); //10 bits of data
if(PortReadingInInt < Port6PeakInt)return;//this will be most of the time so want it to run fast
Port6PeakInt = PortReadingInInt;//when we do read a higher value, save it as the peak
#ifdef DiagPrint7
Serial.print(F("Port6Peak(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F("Port6PeakInt = "));
Serial.println( Port6PeakInt );
#endif
}
void Fdr::ZeroControlBlock()
{//diag tool to corrupt control block before it is checked
for(byte ControlBlockAddressByte = 0; ControlBlockAddressByte < 16; ControlBlockAddressByte++)
{
WriteEEPROM_ControlBlock(ControlBlockAddressByte, 0);
}
}
void Fdr::ZeroJustTimeCalibration()
{//diag tool to corrupt just time calibration value in control block
WriteEEPROM_ControlBlock(OneSecondMillisecondsBaseAddressByte, 0);
WriteEEPROM_ControlBlock(OneSecondMillisecondsBaseAddressByte + 1, 0);
}
void Fdr::PrintLine()
{
Serial.println();
#ifndef reduceMenu
Serial.println(F("=================================================")); //RGS1.2
#endif
#ifdef reduceMenu
Serial.println(F("==="));
#endif
}
bool Fdr::BatteryOnQ()
{
if (LivePortVoltageReadingFloat(7) < 2.5){ //when battery is off, port 7 reads less than 2.5V
return false;
}else{
return true;
}
}
void Fdr::StudentDefinedMenuQ() //RGS1.5
{
if((Command == 'R')||(Command == 'r'))
{
Command = 'N'; //clear Command
Serial.println();
Serial.println(F("This function is currently unused."));
}
}
void Fdr::VideoRecordAtPowerUp()
{
if (BatteryOnQ() == false)NoBatteryWasConnectedBool = true;//this check covers the case of the USB being connected first and then the battery is turned on.
#ifdef DiagPrintRC
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" NoBatteryWasConnectedBool is "));
Serial.println( NoBatteryWasConnectedBool );
#endif
if (((JustPoweredUpToBeUsedByRunCamFlag == true)||(NoBatteryWasConnectedBool == true)) && (BatteryOnQ() == true)) //
/*Camera gets its own power up flag RGS1.4. Only send start record if the battery is connected. This prevents the camera from missing the record signal because camera momentarily without power.
This test covers two cases:
* if no USB connected and then the battery is turned on, JustPoweredUpToBeUsedByRunCamFlag is set true within setup(). As long as the camera has power, the commands are sent.
* if the USB is connected and then the battery is turned on, setup() will not run. By checking for the battery and setting the NoBatteryWasConnectedBool flag, we can test of see if the battery just came up. RGS1.5.
*/
{
#ifdef LimitedDiagPrintRC
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" JustPoweredUpToBeUsedByRunCamFlag is "));
Serial.println( JustPoweredUpToBeUsedByRunCamFlag );
Serial.print(F(" NoBatteryWasConnectedBool is "));
Serial.println( NoBatteryWasConnectedBool );
#endif
NoBatteryWasConnectedBool = false;
#ifdef LimitedDiagPrintRC
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
#endif
#ifdef DiagPrint1
ErrorCodeByte = ReadEEPROM_ControlBlock(ErrorCodeAddressByte);
if (ErrorCodeByte != OldErrorCodeByte)
{
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ErrorCodeByte = "));
Serial.println( ErrorCodeByte );
OldErrorCodeByte = ErrorCodeByte;
}
#endif
if(ReadEEPROM_ControlBlock(ProgramStateAddressByte) < 2) //if we just powered up and are in either of the flight states, then start the camera. RGS1.4
{
delay(5000);//insure RunCam2 has time to power up and be ready to receive commands. Note that this stops loop() from running for 5 seconds right after power-up.
LocalErrorByte = StartVideoRecording(); //start video recording after power is applied to system. The clamp must be on the camera. The camera must be configured with Remote mode, Auto-record off, and Auto shutdown off.
#ifdef DiagPrintRC
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" Camera error code is "));
Serial.println( LocalErrorByte );
#endif
JustPoweredUpToBeUsedByRunCamFlag = false;//since this is the only user of this flag, I can retire it here. RGS1.4
}
#ifdef LimitedDiagPrintRC
Serial.print(F("Estimated Camera State is "));//diag code
Serial.println(EstimatedCameraStateByte);
Serial.print(F("Camera Error Code is "));//diag code
Serial.println(LocalErrorByte);
#endif
#ifdef DiagPrintRC
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS: "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" just generated pushbutton "));
#endif
}
}//end of VideoRecordAtPowerUp()
void Fdr::SetNominalTimerValue(){
OneSecondMillisecondsUnsignedLong = 1000L;//nominal value
WriteEEPROM_ControlBlock(OneSecondMillisecondsBaseAddressByte, lowByte(OneSecondMillisecondsUnsignedLong)); //save to EEPROM RGS1.2
WriteEEPROM_ControlBlock(OneSecondMillisecondsBaseAddressByte+1, highByte(OneSecondMillisecondsUnsignedLong)); //save to EEPROM with write subroutine that allows write to timer constant. all other write subrotines do not. RGS1.2
}
void Fdr::Time()
{
LoopCycleTimeMonitor();//generates one message if it takes more than LoopCycleTimeLimitMS_UInt for one cycle. No monitoring for first 30 seconds to allow for hardware start up delays RGS1.5
LapseTimerSeconds(); //only advance time if ControlTheFlightParameters() has run due to power up. Otherwise, time will advance before variables that depend on being initialized have been set. CR2.0
if(*Modem_Bool){
ModemTransmitTiming();//RGS1.0
}
if(*GNSS_Bool){
GNSS_Timing();//not currently used
}
RefreshTimeCalibrationConstantQ();
LED_Cadences();
}
/****************************************
This header file is consistent with version 7 of Fdr.cpp
******************************************/
/*
Fdr runs on the Flight Data Recorder hardware and is able to collect up to 7 voltages
for up to 4.55 hours plus it records the battery voltage. It also can run a RunCam2 camera,
GPS, and an Iridium Modem.
*/
#ifndef Fdr_h
#define Fdr_h
#include "Arduino.h"
//I added */ after "REMOVED SLASH.
class Fdr
{
public:
/*************************************************
SET UP LIBRARY ENVIRONMENT
**************************************************/
Fdr(); //this is the Constructor and sets up the environment for other memembers of its class.
unsigned int OneTimeRun();
/*************************************************
VARIABLES AND CONSTANTS
**************************************************/
void Pointers(intptr_t *PointerArray);//changed int to intptr_t to be consistent with Fdr.cpp
/*************************************************
PUT ALL FDR FUNTIONALITY INTO THE USER'S LOOP
**************************************************REMOVED SLASH*/
void PutInLoop();
void Port6Peak();
/*************************************************
SEEPROM
**************************************************REMOVED SLASH*/
int WriteSEEPROM(long eeAddress, byte data);
int ReadSEEPROM(long eeaddress);
/*************************************************
CAMERA
**************************************************REMOVED SLASH*/
void VideoRecordAtPowerUp();
byte StartVideoRecording();
byte GoToVideoStandby();
byte GoToStillPictureStandby();
byte TakeStillPicture();
/*************************************************
GNSS
**************************************************REMOVED SLASH*/
void GlobalNavigationSatelliteSystem();
/*************************************************
MODEM
**************************************************REMOVED SLASH*/
unsigned int Iridium(unsigned int ModemCommand);
/*************************************************/
//private:
/**********************************************************
A R D U I N O O U T P U T C O N N E C T I O N S
*********************************************************
Arduino
Physical logical description
8 D5 external LED
10 D7 RunCam2 control
**********************************************************/
int ExternalLED = 5;
int RunCamControl = 7;
/**********************************************************
E E P R O M T O A R D U I N O H A R D W A R E C O N N E C T I O N S
**************************************************************************
24LC1025 Arduino
Physical Logical description Physical
1 A0 ground 3,4,23
2 A1 ground 3,4,23
3 A2 5V 21
4 VSS ground 3,4,23
5 SDA SDA* 5
6 SCL SCL* 6
7 WP ground 3,4,23
8 VCC 5v 21
*these are hard wire I2C and not Softwire
**********************************************************
The 24LC1025 is 1024kbit or 128k bytes.
If the eeaddress is less than the 64k byte threshold we use I2C address 0x50
If the address is above 65535 then we use 0x54 address
Based on: https://playground.arduino.cc/Code/I2CEEPROM
******************************************************
A R D U I N O T O P O R T C O N N E C T I O N S
*********************************************************
Arduino
Physical logical port description
11 A8 0 analog input
9 A7 1 analog input
13 A10 2 analog input
17 A0 3 analog input
18 A1 4 analog input
19 A2 5 analog input
20 A3 6 analog input
12 A9 7 battery monitor
**********************************************************
A R D U I N O U S A R T C O N N E C T I O N S
*********************************************************
Arduino
Physical logical description
1 D1 TXO not used by GNSS
2 D0 RXI connects to TX on the GNSS in order to pass NEMA 0183 sentences
**********************************************************/
int TXOpin = 1;
int RXIpin = 0;
/******************************************************
C O N T R O L B L O C K IN E E P R O M
*********************************************************
In the EEPROM the first 16 bytes are the control block.
byte description
==== ===========
0 Program State
1 spare
2 spare
3 StartOfNextMemoryBlockBaseAddressByte (base address so byte 0)
4 address byte 1
5 address byte 2
6 address byte 3
7 spare
8 OneSecondMillisecondsBaseAddressByte (base address so byte 0)
9 address byte 1
10 hardware and software error codes
11-15 spare
**********************************************************
Program States:
Value State
0 In-Flight
1 In-Flight with power disruption
2 MemoryLocked
3 Pre-flight
Given these choices in values, I only need to test if byte is <2 to know I'm in flight.
**********************************************************/
boolean *GNSS_Bool;//declare GNSS_Bool to be a pointer to a boolean data type
boolean *Modem_Bool;
boolean *AutomaticVideoRecordAtPowerUpBool ;
boolean IridiumModemPresentBool;
char *filePathChar;
byte ProgramStateAddressByte = 0;//was byte ProgramStateAddressByte = 0;
byte InFlightByte = 0;
byte InFlightWithPowerDisruptionByte = 1;
byte TestForInFlightByte = 2;
byte MemoryLockedByte = 2;
byte PreFlightByte = 3;
byte OneSecondMillisecondsBaseAddressByte = 8;
//byte ErrorCodeAddressByte = 10; diag
byte ErrorCodeAddressByte;
byte StartOfNextMemoryBlockPointerBaseAddressByte = 3;
unsigned int MPM_Busy_TransmitCommandRejectedUInt = 274;
unsigned int TransmissionProcessHasBegunUInt = 322;//matches MPM values
/************************************************
M O D E M S E R I A L N U M B E R S
*************************************************/
byte *nearFarModemSNbyte;//contains near and far modem User RB sn.
/************************************************ M O D E M S U C C E S S R E T U R N S
************************************************/
//unsigned int PingThroughMPM_AndModemSuccessUInt = 400;
//unsigned int ModemReadyForUseUInt = 401;
//unsigned int TransmitSuccessfulAndNoReceiveUInt = 402;
//unsigned int TransmitAndReceiveSuccessfulUInt = 403;
//unsigned int TransmitAndReceiveSuccessfulPlusReceivePendingUInt = 404;
/************************************************
M O D E M F A I L U R E R E T U R N S
************************************************/
unsigned int DuplicateTransmitOfDataAttemptedUInt = 255;
/***********************************************************
R E F E R E N C E I N F O R M A T I O N
**********************************************************
to interface with laptop: www.arduino.cc\en\Reference\Serial.html
https://learn.sparkfun.com/tutorials/reading-and-writing-serial-eeproms
Based on: https://playground.arduino.cc/Code/I2CEEPROM
**********************************************************/
/***********************************************************
C O N S T A N T S
*********************************************************/
String SourceFile = __FILE__;
#define ExternalLED_OnByte 1
#define ExternalLED_OffByte 0
#define StartOfDataMemoryLong 16L
byte SamplingRateSecondsByte = 2; //rate data is collected and output. I'm doing oversampling on output to generate data every second so do not change this ant.
byte LogicalAnalogPinByte[8]; //was byte LogicalAnalogPinByte[8]={8,7,10,0,1,2,3,9}; apparently, you can't do assignments of more than variables.
//unsigned long FailureRangeMinUInt = 1;//used to assess modem responses
//unsigned long FailureRangeMaxUInt = 299;
//unsigned long StatusRangeMinUInt = 300;
//unsigned long StatusRangeMaxUInt = 399;
//unsigned long SuccessRangeMinUInt = 400;//includes ping and modem set up
//unsigned long TransmitSuccessRangeMinUInt = 402;//only includes transmit related successes
//unsigned long SuccessRangeMaxUInt = 499;
//unsigned int MinimumTransmitCycleTimeSecondsUInt = 60;
#define MinimumTransmitCycleTimeSecondsUInt 60U //it is defined because they are used to define other variables
#define InitialReturnCodeValueUInt 328U //it is defined because they are used to define other variables
#define InitialMPM_ResponseValueUInt 329U //it is defined because they are used to define other variables
/***********************************************
M O D E M C O M M A N D S
***********************************************/
//unsigned int PingUInt = 0;
//unsigned int SetUpModemUInt = 1;
//unsigned long PerformTransmitUInt = 2;
//unsigned long getReceivedDataUInt = 3;
//unsigned int SendBackFirstBlockOfReceivedArrayUInt = 4;
//unsigned int SendBackSecondBlockOfReceivedArrayUInt = 5;
//unsigned int setLoopAroundUInt 6U
//unsigned int clearLoopAroundUint 7U
//***********************************************
//unsigned int SentPerformTransmitUInt = 323;
//unsigned int SentgetReceivedDataUInt = 324;
//unsigned int AboutToStartTransmitProcessUInt = 326;
//unsigned int InitialReturnCodeValueUInt = 328;
//unsigned int InitialMPM_ResponseValueUInt = 329;
//unsigned int BusySettingUpModemUInt = 330;
//unsigned int PerformingPingUInt = 331;
//unsigned int MPM_Busy_TransmitCommandPendingUInt = 334;
//unsigned int ModemSetupProceedingUInt = 335;
//unsigned int ModemDefaultsSetUInt = 336;
//unsigned int SentPingUInt = 337;
//unsigned int SBD_MessageSuccessfullyWrittenUInt = 338;
//unsigned int MT_MessagePendingUInt = 339;
//unsigned int MT_MessagesPendingUInt = 340;
//unsigned int InvalidCommandUInt = 120;
//unsigned int PingToMPM_SuccessButToModemFailedUInt = 276;
//unsigned int PingToMPM_TimedOutUInt = 275;
//unsigned int TimeOutWaitingForgetReceivedDataUInt = 279;
//unsigned int NoPingMPM_BusyUInt = 280;
//unsigned int ModemFailedAtSetupUInt = 281;
//unsigned int UnexpectedResponseFromModemDuringSetupUInt = 282;
//unsigned int ModemSetupFailedBecauseMPM_BusyUInt = 283;
//unsigned int ModemFailedAtSetupTimeOutUInt = 284;
//unsigned int MPM_BusyWhenFDR_AskedForSetupUInt = 285;
//unsigned int MPM_DidNotRespondToRequestForDataUInt = 286;
//unsigned int SoftwareError1UInt = 287;
//unsigned int MPM_BusyUInt = 288;
//unsigned int AskedForPingResultTooSoon_DoPingAgainUInt = 289;
//unsigned int PingToMPM_DidNotRespondUInt = 290;
//unsigned int WrongModemConnectedCheckSerialNumberUInt = 291;
//unsigned int RequestedTransmitTooSoonUInt = 292;
//unsigned int NoFunctioningModemPresentUInt = 293;
//unsigned int TimeOutAfterSendingMessageUInt = 294;
//unsigned int SBD_MessageTimeOutByModemUInt = 295;
//unsigned int SBD_MessageChecksumWrongUInt = 296;
//unsigned int SBD_MessageSizeWrongUInt = 297;
//unsigned int UnexpectedResponseAfterWritingDataToMobileOriginatedBufferUInt = 298;
//unsigned int SBD_MessageSizeTooBigOrTooSmallUInt = 299;
/********************************************************************************
H A R D W A R E A N D S O F T W A R E E R R O R C O D E S
*********************************************************************************/
byte SystemNormalFlagByte = 0;
byte OutOfRangeReadFlagByte = 1;
byte OutOfRangeWriteFlagByte = 2;
byte ControlTheFlightParametersReadyForLaunchReadBackFailureFlagByte = 3;
byte ControlTheFlightParametersInFlightFlagReadBackFailureFlagByte = 4;
byte JustWaitReadBackFailureFlagByte = 5;
byte PrepareForLaunchReadBackFailureFlagByte = 6;
byte WriteEEPROM_AttemptMadeToWriteToLockedMemoryFlagByte = 7;//RGS1.2
byte SerialAvailableReturnCodeOutOfRangeByte = 9;
byte ReadBackFromEEPROM_MismatchByte = 10;
byte AttemptMadeToWriteToControlBlockByte = 11;
byte AttemptMadeToWriteToDataBlockByte = 12;
byte NoCameraErrorByte = 0;
byte CameraHasNoPowerErrorByte = 1;
byte IllegalCameraStateValueErrorByte = 2;
byte VideoRecordStateByte = 0;
byte VideoStandbyStateByte = 1;
byte StillStandbyStateByte = 2;
byte CameraHasNoPowerStateByte = 3;
byte MaximumCameraStateValueByte = 3;
unsigned int LoopCycleTimeLimitMS_UInt = 950;//I measured 216 ms so this leaves some room plus is plenty in order to read the ports every 2000 milliseconds. However, GPS read takes 1000 ms so this will still be a problem each time we read the GPS.
/********************************************************************************
V A R I A B L E S
*********************************************************************************/
//int OneSecondMillisecondsInt = 0; //use to calibrate hardware clock to give 1 second; data comes from EEPROM bytes 8 and 9 was replaced with UL
unsigned long OneSecondMillisecondsUnsignedLong = 1000L; //CR3.0
long StartOfNextMemoryBlockLong = 16L; //this is the pointer to the next available memory location; it is initialized at power up RGS1.2
unsigned long StartTimeMS_UnsignedLong = 0; //will be set by millis() to the value of the real time clock shortly after power up. CR3.0
int TimeNowSecondsInt = 0; //used to set seconds flag
int Last_TimeNow_SecondsInt = 0; //used to set seconds flag
boolean TimeToTakeSampleBool = false; //is set true at the start of each second and cleared by last user CR3.2
boolean TimeToTransmitBool = false;
boolean TransmitSuccessfulBool = false;
char Command = 'N'; //stores single character sent from laptop; initilized to "N" = null.
boolean ReadyForLaunchFlag = false;
boolean InFlightFlag = false;
unsigned long StartOfPreFlightCadenceMS_UnsignedLong = millis();
unsigned long StartOfInFlightCadenceMS_ULong = millis();
unsigned long StartOfDisruptedPowerCadenceMS_UnsignedLong = millis();
unsigned long StartOfMemoryLockedLED_CadenceMS_UnsignedLong = millis();
unsigned long StartOfFaultLED_CadenceMS_UnsignedLong = millis();
boolean PreFlightLED_CadenceFlag = false;
boolean InFlightLED_CadenceFlag = false;
boolean DisruptedPowerLED_CadenceFlag = false;
boolean MemoryLockedLED_CadenceFlag = false;
boolean FaultLED_CadenceFlag = false;
boolean JustPoweredUpFlag = true; //flag used by ControlTheFlightParameters(). At every power up, this flag is set true.
boolean JustPoweredUpToBeUsedByRunCamFlag = true;//flage used only by the RunCam2 code so record command is sent after power up. RGS1.4
boolean JustPoweredUpForTimeStampFlag = true; //flag used by time stamp function. At every power up, this flag is set true. CR3.0
int InFlightCadenceIntervalMS_Int = 0;
int PreFlightCadenceIntervalMS_Int= 0;
int MemoryLockedCadenceIntervalMS_Int= 0;
int DisruptedPowerCadenceIntervalMS_Int= 0;
int FaultCadenceIntervalMS_Int= 0;
long eeAddressLong= 0;//local variable used for all addressing of EEPROM
boolean ContinuousDisplayDataFlag = false; //flag set by ContinuousDisplayDataQ() and cleared at power cycle CR1.7
long DurationMS_UnsignedLong= 0 ;
long IdealCalibrationTimeSeconds_UnsignedLong = 10800UL; //3 hours=180 minutes=10800 seconds (10800UL) so get number of internal ms counts per second
unsigned long StartOfCalibrationTimeMS_UnsignedLong = 0;
long CalibrationTimeMS_Long = 0;
float OneSecondMillisecondsFloat = 0; //CR2.0
float ErrorPercentageFloat = 0;
boolean FoundCommandQ = false;
boolean DeepReturnBool = false;//passes a return from a subroutine up to the calling subroutines
float LimitTestFloat = 0;
int SecondsInt = 0;
byte IncomingByte = 0; //used by RockBlock modem 4.4
unsigned int ReturnCodeUInt = InitialReturnCodeValueUInt;//used by RockBlock modem 4.4
unsigned int MPM_ResponseUInt = InitialMPM_ResponseValueUInt;
unsigned int LastMPM_ResponseUInt = 1000;//far outside of valid responses
byte (*IridiumTransmitDataByte)[45];//a pointer to an array of bytes
byte PreviousIridiumTransmitDataByte[45];//a locally defined array
byte (*IridiumReceiveDataByte)[45];//a pointer to an array of bytes
byte MPM_RejectedDataByte[45];
byte (*NavigationDataByte)[16];
unsigned long AltitudeULong=0;
byte i = 0;
unsigned long SystemTestStartTimeULong = millis();
bool CycleIdleBool = true;
//byte CommandLowByte = 0;
//byte CommandHighByte = 0;
byte ResponseLowByte = 0;
byte ResponseHighByte = 0;
unsigned long LastReturnCodeUInt = 1000;//beyond all valid return codes
unsigned long LastSuccessfulTransmitTimeSecondsUInt = (millis()/1000) - MinimumTransmitCycleTimeSecondsUInt - 10;//this ensures we run on first pass.
unsigned long ReadbackStartTime = 0;
unsigned long StartTimeULong = 0;
unsigned long StartForTimeStampULong = 0;
boolean IridiumModemOperationalBool = false;//set true if we can initialize it
unsigned long LastTransmissionCheckTimeStampUInt = millis() - 6000;//ensures first request is accepted
boolean PerformDataTransmissionBool = true;//this will let me transmit once
boolean DataTransmissionSessionActiveBool = false;
byte ModemCommandArrayByte[2];
unsigned long TimeSinceLastSuccessfulTransmissionSecondsULong = 0;
boolean RequestTransmitRestartBool = false;
char character;
String BufferText = "";
byte count = 0;
byte EndOfField = 0;
byte CountPlusOne = 0;
byte CountPlusTwo = 0;
boolean AtEndOfNeededSentenceBool = false;
byte pointer = 0;
byte FieldCountByte = 0;
unsigned long GNSS_WaitingForDataTimeLimitMsULong = 4000;
unsigned long GNSS_StartOfWaitingForDataMsULong = millis();
float FractionalMinutesFloat = 0;
byte StartOfAltitudeByte = 0;
byte EndOfFieldByte = 0;
boolean DiagPrintBool = true;
boolean GNSS_TimeToRetryReadingDataQBool = false;//init to don't retry access to GNSS
boolean GNSS_TimedOutWaitingForDataBool = false;//init is that GPS has been detected
unsigned int TimeNowMsUInt = millis();//time since power up.
unsigned int GNSS_DelayBeforeRetryOfReadingDataMsUInt = 60000;//Retry talking to GNSS after 60000 ms. RGS1.5
unsigned int LastTimeWeDidA_RetryOfReadingDataMsUInt = TimeNowMsUInt - GNSS_DelayBeforeRetryOfReadingDataMsUInt - 10;//initialized so GNSS_Timing() will think it has been more than GNSS_DelayBeforeRetryOfReadingDataMsUInt since last retry
boolean IncompleteSentenceBool = false;
int Port6PeakInt = 0;//Port6 is read with peak detection logic RGS1.1
int PortReadingInInt = 0;//make these variables global so I can use them for peak detector RGS1.1
byte PortReadingInHighByte = 0;
byte PortReadingInLowByte = 0;
byte PortReadingOutByte = 0;
long PortDataAddressLong = StartOfNextMemoryBlockLong; //port 0
float VoltageFloat = 0; //used when sending port voltages to display
boolean ASCII_Bool = false;
boolean HEX_Bool = true;
byte OldErrorCodeByte = 20;
byte ErrorCodeByte = 21;
boolean NoBatteryWasConnectedBool = true;
byte EstimatedCameraStateByte = VideoStandbyStateByte;//at power up, camera should be in video-standby
byte GNSS_RetryCounterByte = 1;//counter advanced each time we try to access the GNSS and it fails. RGS1.5
byte GNSS_RetryCountLimitByte = 2;//number of times we will retry the GNSS when it has not sent any data
byte LocalErrorByte = 99;
unsigned int LastLoopTimeStampMS_UInt = millis()+29999;//offset compensates for not monitoring for the first 30 seconds
boolean LoopCycleTimeWarningRaisedBool = false;//part of alarm for excessive time it takes to run through loop()
unsigned int LoopCycleTimeMS_UInt = 0;
bool OneTimeRunBool = true;
bool resetLoopTimerBool = false;//I use this flag to prevent loop time warning after user enters a function abort.
bool enableLoopAroundQbool;
unsigned long baudRatesULong[4] = {9600,19200,28800,38400};
//rate is initialized to 9600 since this is the default for the GPS.
byte currentBaudRateByte = 0;//used in communications with GPS
bool GPSavailableQbool = true;
/********************************************************************************
U S E R I D - USE WITH ReadEEPROM_Pointer()
*********************************************************************************/
byte CalledBySetupByte = 1;
byte CalledByIncrementEEPROM_PointerByte = 2;
byte CalledByDiagnoticOutputControlBlockByte = 4;
byte CalledByRecordDataByte = 6;
byte CalledByOutputDataToFileByte = 7;
/********************************************************************************
S O F T W I R E S E T U P (O P T I O N A L)
*********************************************************************************/
byte sdaPin = 8; //Replace "8" with the logical pin you want to use for the Serial Data line
byte sclPin = 7; //Replace "7" with the logical pin you want to use for the Serial Clock line
byte cmdAmbient = 6;
byte cmdObject1 = 7;
byte cmdObject2 = 8;
byte cmdFlags = 0xf0;
byte cmdSleep = 0xff;
/*
if(SoftwireActive){//eventually, I want to pass a flag that enables SoftWire
SoftWire i2c(sdaPin, sclPin);
}
*/
/* these are the old definitions that were in Fdr.h but don't contain values
boolean *GNSS_Bool;
boolean *Modem_Bool;
boolean *AutomaticVideoRecordAtPowerUpBool ;
boolean IridiumModemPresentBool;
int ExternalLED;
int RunCamControl;
int TXOpin;
int RXIpin;
byte ProgramStateAddressByte = 0;//it was without = 0
byte InFlightByte;
byte InFlightWithPowerDisruptionByte;
byte TestForInFlightByte;
byte MemoryLockedByte;
byte PreFlightByte;
byte OneSecondMillisecondsBaseAddressByte;
byte ErrorCodeAddressByte;
byte StartOfNextMemoryBlockPointerBaseAddressByte;
unsigned int MPM_Busy_TransmitCommandRejectedUInt;
unsigned int TransmissionProcessHasBegunUInt;//matches MPM values
unsigned int PingThroughMPM_AndModemSuccessUInt;
unsigned int ModemReadyForUseUInt;
unsigned int TransmitSuccessfulAndNoReceiveUInt;
unsigned int TransmitAndReceiveSuccessfulUInt;
unsigned int TransmitAndReceiveSuccessfulPlusReceivePendingUInt;
unsigned int DuplicateTransmitOfDataAttemptedUInt;
byte IridiumTransmitDataByte[45]; //I moved this line ahead of the following
byte LogicalAnalogPinByte[8];//maps port number to analog logical pin number it was byte LogicalAnalogPinByte[8];
byte ExternalLED_OnByte;
byte ExternalLED_OffByte;
long StartOfDataMemoryLong;
byte SamplingRateSecondsByte; //rate data is collected and output. I'm doing oversampling on output to generate data every second so do not change this constant.
unsigned long FailureRangeMinUInt;//used to assess modem responses
unsigned long FailureRangeMaxUInt;
unsigned long StatusRangeMinUInt;
unsigned long StatusRangeMaxUInt;
unsigned long SuccessRangeMinUInt;//includes ping and modem set up
unsigned long TransmitSuccessRangeMinUInt;//only includes transmit related successes
unsigned long SuccessRangeMaxUInt;
unsigned int MinimumTransmitCycleTimeSecondsUInt;
//byte IridiumTransmitDataByte[45];
byte IridiumReceiveDataByte[45];
byte NavigationDataByte[16];
String SourceFile;
***********************************************
M O D E M C O M M A N D S
************************************************
unsigned int PingUInt;
unsigned int SetUpModemUInt;
unsigned long PerformTransmitUInt;
unsigned long getReceivedDataUInt;
unsigned int SendBackFirstBlockOfReceivedArrayUInt;
unsigned int SendBackSecondBlockOfReceivedArrayUInt;
***********************************************
unsigned int SentPerformTransmitUInt;
unsigned int SentgetReceivedDataUInt;
unsigned int AboutToStartTransmitProcessUInt;
unsigned int InitialReturnCodeValueUInt;
unsigned int InitialMPM_ResponseValueUInt;
unsigned int BusySettingUpModemUInt;
unsigned int PerformingPingUInt;
unsigned int MPM_Busy_TransmitCommandPendingUInt;
unsigned int ModemSetupProceedingUInt;
unsigned int ModemDefaultsSetUInt;
unsigned int SentPingUInt;
unsigned int SBD_MessageSuccessfullyWrittenUInt;
unsigned int MT_MessagePendingUInt;
unsigned int MT_MessagesPendingUInt;
unsigned int InvalidCommandUInt;
unsigned int PingToMPM_SuccessButToModemFailedUInt;
unsigned int PingToMPM_TimedOutUInt;
unsigned int TimeOutWaitingForgetReceivedDataUInt;
unsigned int NoPingMPM_BusyUInt;
unsigned int ModemFailedAtSetupUInt;
unsigned int UnexpectedResponseFromModemDuringSetupUInt;
unsigned int ModemSetupFailedBecauseMPM_BusyUInt;
unsigned int ModemFailedAtSetupTimeOutUInt;
unsigned int MPM_BusyWhenFDR_AskedForSetupUInt;
unsigned int MPM_DidNotRespondToRequestForDataUInt;
unsigned int SoftwareError1UInt;
unsigned int MPM_BusyUInt;
unsigned int AskedForPingResultTooSoon_DoPingAgainUInt;
unsigned int PingToMPM_DidNotRespondUInt;
unsigned int WrongModemConnectedCheckSerialNumberUInt;
unsigned int RequestedTransmitTooSoonUInt;
unsigned int NoFunctioningModemPresentUInt;
unsigned int TimeOutAfterSendingMessageUInt;
unsigned int SBD_MessageTimeOutByModemUInt;
unsigned int SBD_MessageChecksumWrongUInt;
unsigned int SBD_MessageSizeWrongUInt;
unsigned int UnexpectedResponseAfterWritingDataToMobileOriginatedBufferUInt;
unsigned int SBD_MessageSizeTooBigOrTooSmallUInt;
byte SystemNormalFlagByte;
byte OutOfRangeReadFlagByte;
byte OutOfRangeWriteFlagByte;
byte ControlTheFlightParametersReadyForLaunchReadBackFailureFlagByte;
byte ControlTheFlightParametersInFlightFlagReadBackFailureFlagByte;
byte JustWaitReadBackFailureFlagByte;
byte PrepareForLaunchReadBackFailureFlagByte;
byte WriteEEPROM_AttemptMadeToWriteToLockedMemoryFlagByte;//RGS1.2
byte SerialAvailableReturnCodeOutOfRangeByte;
byte ReadBackFromEEPROM_MismatchByte;
byte AttemptMadeToWriteToControlBlockByte;
byte AttemptMadeToWriteToDataBlockByte;
byte NoCameraErrorByte;
byte CameraHasNoPowerErrorByte;
byte IllegalCameraStateValueErrorByte;
byte VideoRecordStateByte;
byte VideoStandbyStateByte;
byte StillStandbyStateByte;
byte CameraHasNoPowerStateByte;
byte MaximumCameraStateValueByte;
unsigned int LoopCycleTimeLimitMS_UInt;//I measured 216 ms so this leaves some room plus is plenty in order to read the ports every 2000 milliseconds.
byte CalledByIncrementEEPROM_PointerByte;
byte CalledByDiagnoticOutputControlBlockByte;
byte CalledByRecordDataByte;
byte CalledByOutputDataToFileByte;
byte cmdAmbient;
byte cmdObject1;
byte cmdObject2;
byte cmdFlags;
byte cmdSleep;
bool OneTimeRunBool;
bool JustPoweredUpFlag;
unsigned long OneSecondMillisecondsUnsignedLong;
unsigned long StartTimeMS_UnsignedLong;
unsigned long StartForTimeStampULong;
bool LoopCycleTimeWarningRaisedBool;
unsigned int LoopCycleTimeMS_UInt;
unsigned int LastLoopTimeStampMS_UInt;
int TimeNowSecondsInt;
int Last_TimeNow_SecondsInt;
bool TimeToTakeSampleBool;
char Command;
bool DeepReturnBool;
unsigned long StartOfCalibrationTimeMS_UnsignedLong;
bool FoundCommandQ;
unsigned long DurationMS_UnsignedLong;
float LimitTestFloat;
bool ContinuousDisplayDataFlag;
bool FaultLED_CadenceFlag;
bool MemoryLockedLED_CadenceFlag;
bool InFlightLED_CadenceFlag;
bool DisruptedPowerLED_CadenceFlag;
int ReturnCodeUInt;
int PreFlightCadenceIntervalMS_Int;
unsigned long StartOfPreFlightCadenceMS_UnsignedLong;
int InFlightCadenceIntervalMS_Int;
unsigned long StartOfInFlightCadenceMS_ULong;
unsigned long IdealCalibrationTimeSeconds_UnsignedLong;
float OneSecondMillisecondsFloat;
bool PreFlightLED_CadenceFlag;
unsigned long StartOfDisruptedPowerCadenceMS_UnsignedLong;
float ErrorPercentageFloat;
int DisruptedPowerCadenceIntervalMS_Int;
int MemoryLockedCadenceIntervalMS_Int;
unsigned long StartOfMemoryLockedLED_CadenceMS_UnsignedLong;
int FaultCadenceIntervalMS_Int;
long StartOfNextMemoryBlockLong;
long PortDataAddressLong;
bool JustPoweredUpForTimeStampFlag;
int PortReadingInInt;
byte PortReadingInHighByte;
byte PortReadingInLowByte;
float VoltageFloat;
unsigned long StartOfFaultLED_CadenceMS_UnsignedLong;
int Port6PeakInt;
byte ErrorCodeByte;
*/
void Time();//
void OnTheGround();//
void InTheAir();//
void RefreshTimeCalibrationConstantQ();//
void ReadEEPROM_Pointer(byte CalledBySetupByte);//
byte ReadEEPROM_ControlBlock(byte eeAddressByte);//
void WriteEEPROM_ControlBlock(byte eeAddressByte, byte data);//
void RecordPortReadings();//
void IncrementEEPROM_Pointer();//
void SetNominalTimerValue();//
void LoopCycleTimeMonitor();//
void LapseTimerSeconds();//
void ModemTransmitTiming();//
void GNSS_Timing();//
void LED_Cadences();//
void ControlTheFlightParameters();//
void ScanForCommand();//
void OutputMenuQ();//
void SingleDisplayDataQ(); //D command
void PrepareForLaunchQ(); //P command
void ContinuousDisplayDataQ(); //A command CR1.7
void OutputDataFileQ(); //O command
void StopCollectingDataQ(); //S command
void CalibrateClockQ();//
void DiagnoticOutputControlBlockQ(); //H command
void GenerateTestPatternQ(); //T command
void StudentDefinedMenuQ();//R command
void PingModemQ();//I command
unsigned int PingModem();
unsigned int modemStatus();
unsigned int loopback();
void FlashLED();//
void DataIn();//
void PrintOutGNSS_Array();//
void IridiumModemSatelliteSystem();//
void InFlightCadence();//
void DisruptedPowerCadence();//
void MemoryLockedCadence();//
void PreFlightCadence();//
void FaultCadence();//
void PrintLine();//
void C_CommandQ();//
void FlickerLEDforCalibration();//
void SerialAvailableResponseErrorCheck();//
void FirstAbortCommandQ();//
void G_CommandQ();//
void PrintCalibrationStartedText();//
void EndOfCalibration();//
void PrintCalibrationIntroText();//
void A_CommandText();//
void CalibrationUserStatus();
void CalibrationLEDFlicker();
void I_CommandText();
void InvalidCommandText();
void A_CommandQ();
void Y_CommandQ();
void EndOfCalibrationCommandValidityCheck();
void IandY_CommandQ();
void E_Command();
void SetIllegalTimerValue();
void SaveTimerCalibrationValueToEEPROM();
void TellUserAboutTimerCalibrationValue();
void CalculateAndFormatCalibrationValue();
void OutputDataToFile();
void DiagnoticOutputControlBlock();
void OutputSingleLiveScan();
void OutputContinuousDisplayDataStateChange();
void OutputMenuOfCommandsAndLED_Cadences();
void SendPrepareForLaunchWarning();
void ActOnResponseToPrepareForLaunch();
void GenerateTestPatternWarning();
void ActOnResponseToGenerateTestPattern();
void RecordData();
bool BatteryOnQ();
void WriteEEPROM_Pointer();
float LivePortVoltageReadingFloat(byte PortNumberByte);//
void DisplayErrorStateQ();
void JustWait();
byte ReadEEPROM(long eeaddress);
void PrepareForLaunch();
void NonFaultStopProgram();
void FaultStopProgram();
void GenerateTestPattern();
void WriteEEPROM_Data(long eeAddress, byte data);
void UnprotectedWriteEEPROM(long eeAddress, byte data);
unsigned int SetUpModem();
unsigned int PerformTransmit();
unsigned int getReceivedData();
void FlushWireBuffer();
void ChangeCameraMode();
void PushShutter();
void OnePulse();
void PrintOutInterpretationOfSingleResponseFromTransmissionCommand();
void RequestDataTransmissionSession();
void CheckTransmissionProgress();
bool I_Two_C_BusAvailableQ();
void DirectPath(bool HexQ);
void InitializeNavigationDataByteArray();
void FindNeededSentence();
void ParceTime();
void ParceLatitude();
void ParceLongitude();
void SkipFourFields();
void ParceAltitude();
void PrepareTransmitArrayForModem();
void ProcessReturnCodes();
void ReadHeader();
void ReadSentence();
void ZeroControlBlock();
void ZeroJustTimeCalibration();
bool dollarSignInBufferQ();
void setupGPS();
};
#endif
#include <Fdr.h>//was Fdr.h
Fdr fdr; //I am defining fdr as type Fdr.
#include <Wire.h>
/**************************************
Modems
***************************************
The near modem is the one in the payload. The far modem is who we are talking with via satellite.
***************************************
We have nearURBsn and farURBsn where URBsn means User RockBlock serial number. The two higher bits select the near modem and the two lower bits select the far modem. For example, if nearFarmodemSNbyte equaled 11, this is 1011 in binary. The nearURBsn is UserRBsn 10 and the farURBsn is UserRBsn 11.
/***********************************************
Available Modems
UserRBsn RBsn IMEI
00 13301 300234066438070
01 218642 300534065390120
10 13298 300234066436090
11 218641 300534065396130
there is also a spare modem that has not been provisioned into the Iridium.ino code.
*************************************************
Available functions contained in the library.
*************************************************
SEEPROM
***************************************************
fdr.WriteSEEPROM(long eeAddress, byte data);
fdr.ReadSEEPROM(long eeaddress);
*************************************************
CAMERA
***************************************************
fdr.VideoRecordAtPowerUp();
fdr.StartVideoRecording();
fdr.GoToVideoStandby();
fdr.GoToStillPictureStandby();
fdr.TakeStillPicture();
*************************************************
GNSS
***************************************************
fdr.GlobalNavigationSatelliteSystem();
*************************************************
MODEM
***************************************************
fdr.Iridium(unsigned int ModemCommand);
*************************************************/
/***********************************************
MODEM COMMANDS
***********************************************/
#define PingUInt 0U
#define SetUpModemUInt 1U
#define PerformTransmitUInt 2U
#define getReceivedDataUInt 3U
#define SendBackFirstBlockOfReceivedArrayUInt 4U
#define SendBackSecondBlockOfReceivedArrayUInt 5U
#define setLoopAroundUInt 6U//added 2/2/2024
#define clearLoopAroundUint 7U//added 2/2/2024
#define statusUInt 8U //returns the current returnCode
#define MPM_DidNotRespondToRequestForDataUInt 286U
/************************************************
S U C C E S S R E T U R N S
************************************************/
#define PingThroughMPM_AndModemSuccessUInt 400
#define ModemReadyForUseUInt 401
#define TransmitSuccessfulAndNoReceiveUInt 402
#define TransmitAndReceiveSuccessfulUInt 403
#define TransmitAndReceiveSuccessfulPlusReceivePendingUInt 404
#define dataLoopAroundEnabledUInt 405
#define dataLooopAroundDisabledUInt 406
/******************************************
FEATURE CONTROL
*******************************************
Set any to true to enable its functionality and to false to disable it.
*******************************************/
bool GNSS = true; //if a GPS is connected, set to true
bool Modem = true; //If an Iridium modem is connected, set to true
bool AutomaticVideoRecordAtPowerUp = false; //RunCam2 via RC2 port on board
/*******************************************
Modem Provisioning
********************************************
nearURBsn and farURBsn where URBsn means User RockBlock serial number.
The two higher bits in nearFarModemSNbyte select the near modem and the two lower bits select the far modem. For example, if nearFarmodemSNbyte equaled 11, this is 1011 in binary. The nearURBsn is UserRBsn 10 and the farURBsn is UserRBsn 11.
/***********************************************
Available Modems
UserRBsn RBsn IMEI
00 13301 300234066438070
01 218642 300534065390120
10 13298 300234066436090
11 218641 300534065396130
there is also a spare modem that has not been provisioned.
These numbers are stored in the Modem's Pro Micro code, Iridium.ino.
************************************************/
//byte nearFarModemSNbyte = 0b1100; /// 15 - Modem "11" aka 218641
byte nearFarModemSNbyte = 0b0011; /// 12 - Modem "11" aka 218641
/*******************************************/
char filePathChar[] = __FILE__;//define a string called filePathChar[] that contains the entire path to this .ino program
//unsigned long StartForTimeStampULong = millis();//for diag prints
/******************************************
USER LIBRARY VARIABLES
*******************************************/
byte IridiumTransmitData[45] = {8};//initialized to a unique nonzero value
byte IridiumReceiveData[45] = {1};//initialized to a unique nonzero value
byte NavigationData[16];
/*******************************************/
intptr_t PointerArray[8];
char const *comma_str = ", ";
/******************************************
SET UP LIBRARY ENVIRONMENT
******************************************/
Fdr Fdr; //the second Fdr is the Constructor for the class Fdr
/// Creates variable called 'wildcards' and takes an SDA and SCL pin.
enum {
SCLPIN = 14,
SDAPIN = 15,
};
/// the SDA Pin has to be given to 'wildcards' before the SCL pin.
//SoftWire wildcards(SDAPIN, SCLPIN);
enum /** i2c Device Addresses */ {
I2C_AGT = 0x68,
I2C_TP = 0x77,
};
static uint32_t big_to_little_endian32(uint32_t const num) {
return ((num>>24) & 0xff) | ((num<<8) & 0xff0000) | ((num>>8) & 0xff00) | ((num<<24) & 0xff000000);
}
static uint16_t big_to_little_endian16(uint16_t const num) {
return (num>>8) | (num<<8);
}
enum { /// Addresses within the SEEPROM.
ADDR_SAVED_CURR_TIME = 0, /// [0-3] unsigned long.
ADDR_NEXT_GPS_BLK = 4, /// [4-5] unsigned int.
ADDR_START_GPS_BLK = 1, /// use this with a multiple of the size of the navigation data array.
MAX_SEEPROM_MEM = (1L << 17L) - 1L,
};
static byte ReadSEEPROMByte(long addr) {
return fdr.ReadSEEPROM(addr);
}
static void WriteSEEPROMByte(long addr, byte b) {
fdr.WriteSEEPROM(addr, b);
}
static int ReadSEEPROMInt(long addr) {
int low = ReadSEEPROMByte(addr+0);
int hi = ReadSEEPROMByte(addr+1);
return hi << 8 | low;
}
static void WriteSEEPROMInt(long addr, int i) {
WriteSEEPROMByte(addr+0, (byte)(i & 0xFF));
WriteSEEPROMByte(addr+1, (byte)(i >> 8));
}
static long ReadSEEPROMLong(long addr) {
byte a[sizeof(long)];
for( size_t i=0; i < sizeof(long); i++ ) {
a[i] = ReadSEEPROMByte(addr+i);
}
return *reinterpret_cast< long* >(&a[0]);
}
static void WriteSEEPROMLong(long addr, long i) {
byte *a = reinterpret_cast< byte* >(&i);
for( size_t n=0; n < sizeof(long); n++ ) {
WriteSEEPROMByte(addr+n, a[n]);
}
}
static void ReadSEEPROMByteArray(long addr, byte *arr, size_t len) {
for( size_t i=0; i < len; i++ ) {
arr[i] = ReadSEEPROMByte(addr+i);
}
}
static void WriteSEEPROMByteArray(long addr, byte *arr, size_t len) {
for( size_t i=0; i < len; i++ ) {
WriteSEEPROMByte(addr+i, arr[i]);
}
}
static void InitBlkIdx() {
WriteSEEPROMInt(ADDR_NEXT_GPS_BLK, ADDR_START_GPS_BLK);
}
static void ResetSEEPROM() {
InitBlkIdx();
WriteSEEPROMLong(ADDR_SAVED_CURR_TIME, 0);
//Serial.println("SEEPROM has been cleared.");
}
static bool StoreGPSAndIncrBlkSEEPROM() {
auto const i = ReadSEEPROMInt(ADDR_NEXT_GPS_BLK);
long const addr = i * sizeof NavigationData;
if( addr >= MAX_SEEPROM_MEM ) {
return false;
}
WriteSEEPROMByteArray(addr, &NavigationData[0], sizeof NavigationData);
WriteSEEPROMInt(ADDR_NEXT_GPS_BLK, i + 1);
return true;
}
/**
BYTE DESCRIPTION
0 hours (UTC)
1 minutes (UTC)
2 seconds (UTC)
3 degrees (latitude)
4 minutes (latitude)
5 seconds (latitude)
6 0 for north, 1 for south
7 degrees (longitude)
8 minutes (longitude)
9 seconds (longitude)
10 0 for east, 1 for west
11 altitude MSB (byte 3) See also Alititude()
12 altitude (byte 2)
13 altitude (byte 1)
14 altitude LSB (byte 0)
15 units: 0 for meters, 1 for feet
*/
enum {
HOURS, MINS_UTC, SECS_UTC,
DEGREES_LAT, MINS_LAT, SECS_LAT,
IS_SOUTH,
DEGREES_LONG, MINS_LONG, SECS_LONG,
IS_WEST,
ALT3, ALT2, ALT1, ALT0,
UNITS,
};
static void PrintSEEPROMData() {
auto const max = ReadSEEPROMInt(ADDR_NEXT_GPS_BLK);
Serial.println(F("hours, mins (utc), secs (utc), degs (lat), mins (lat), secs (lat), is south, degs (long), mins (long), secs (long), is west, modem res, alt, flags"));
for( auto i=1; i < max; i++ ) {
byte gps_data[sizeof NavigationData] = {0};
ReadSEEPROMByteArray(i * sizeof gps_data, &gps_data[0], sizeof gps_data);
for( size_t n=0; n <= IS_WEST; n++ ) {
Serial.print(gps_data[n]); Serial.print(comma_str);
}
Serial.print(*reinterpret_cast< int16_t* >(&gps_data[ALT3]), HEX); Serial.print(comma_str);
Serial.print(big_to_little_endian16(*reinterpret_cast< int16_t* >(&gps_data[ALT1]))); Serial.print(comma_str);
Serial.print(gps_data[UNITS], BIN);
Serial.println();
}
}
enum { TASK_LED_PIN = 4 };
enum {
SEC_IN_MS = 1000UL,
HALF_MIN_IN_MS = 30000UL,
MIN_IN_MS = SEC_IN_MS * 60UL,
FIVE_MIN_IN_MS = MIN_IN_MS * 5UL,
MODEM_DELAY = SEC_IN_MS,
};
unsigned long
transmit_time_old = millis(),
modem_chk_time_old = millis()
;
#define max_time_ms FIVE_MIN_IN_MS
byte power_loss = 0;
char msg[sizeof IridiumTransmitData - sizeof NavigationData];
bool
out_of_mem = false,
waiting_for_tx = false,
seeprom_write = false,
one_time_run_res = false,
one_time_run_delay = false
;
/// 18 bytes
static void prep_msg(int extern_temp, int intern_temp, int uv_sensor) {
memset(msg, 0, sizeof msg);
msg[0] = ' ';
if( power_loss > 0 ) {
power_loss = 0;
strcat(msg, "pwr|");
}
if( out_of_mem ) {
strcat(msg, "OoM|");
}
snprintf(msg, sizeof msg - 1, "%s%i|%i|%i", msg, extern_temp, intern_temp, uv_sensor);
//Serial.print(F("bytes:")); Serial.println(bytes);
/*
switch( payload_status ) {
case 0: strcat(msg, "-led"); break;
case 1: strcat(msg, "+led"); break;
}
*/
}
enum {
PORT_INTERNAL_TEMP_A7 = 7,
PORT_EXTERNAL_TEMP_A8 = 8,
PORT_UV_SENSOR_A10 = 10,
};
//FILE f_out;
//int sput(char c, __attribute__((unused)) FILE* f) { return !Serial.write(c); }
/// Pin on the modem closest to cable --> voltage regulator; should be battery voltage.
/// Middle one (pin) --> ground;
/// last pin, closest to modem, should be 5V.
void setup() {
pinMode(TASK_LED_PIN, OUTPUT);
FillPointerArray();//collect address of all variables needed by the Fdr library
fdr.Pointers(PointerArray);//send pointer array to the library
Serial.begin(19200);
//while( !Serial );
Wire.begin(); //set up communications as FDR
Wire.setClock(400000); //set clock rate for EEPROMs and Modem Pro Micro (MPM) if attached
//fdev_setup_stream(&f_out, sput, nullalt3_ptr, _FDEV_SETUP_WRITE); // cf https://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html#gaf41f158c022cbb6203ccd87d27301226
//stdout = &f_out;
//ResetSEEPROM();
}
void loop() {
/// have an initial 10 second delay to wait for bleed discharge to dissipate
/// from the 1 Farad capacitor on the RockBLOCK Modem.
if( !one_time_run_delay ) {
delay(SEC_IN_MS * 10);
one_time_run_delay = true;
}
/// read the result from the Modem Pro-Micro [MPM]
/// and relay the result to the
auto const MPM_result = fdr.OneTimeRun();
if( !one_time_run_res ) {
switch( MPM_result ) {
case PingThroughMPM_AndModemSuccessUInt: {
Serial.println(F("OneTimeRun :: ping ok!")); break;
}
case ModemReadyForUseUInt: {
Serial.println(F("OneTimeRun :: modem ready")); break;
}
default: {
Serial.print(F("OneTimeRun :: ")); Serial.println(MPM_result);
}
}
one_time_run_res = true;
PrintSEEPROMData();
}
fdr.PutInLoop();//contains all Flight Data Recorder functionality
///put some of your code here
///Your code can use up to 210 milliseconds of realtime. Any more than this and you will get a loop timing warning and the data collection code won't run correctly.
fdr.Port6Peak();//call this function as often as possible to maximize time resolutuion for port 6
/// We want this because we don't want to delay for 5 minutes.
/// so we setup a delta time system to check when something should run.
/// while also allowing other systems to function as normal.
auto time_curr = millis();
auto const time_saved = static_cast< unsigned long >(ReadSEEPROMLong(ADDR_SAVED_CURR_TIME));
//Serial.print("saved time: "); Serial.println(time_saved);
if( time_curr < time_saved ) {
//Serial.println("got power loss");
/// got a power loss.
power_loss = 1; /// This will stay as 1 until it was logged to the GPS
/// IDEA: count power losses & compare to accelerometer data.
}
if( !waiting_for_tx && ((time_curr - transmit_time_old) < max_time_ms) ) {
return;
}
transmit_time_old = millis();
//Serial.print(max_time_ms / MIN_IN_MS); Serial.println(" mins passed.");
WriteSEEPROMLong(ADDR_SAVED_CURR_TIME, transmit_time_old);
int16_t *const alt3_ptr = reinterpret_cast< int16_t* >(&NavigationData[ALT3]);
enum {
RESULT_TYPE_RX = 0x8000,
RESULT_TYPE_ST = 0x4000,
};
if( !waiting_for_tx ) {
fdr.GlobalNavigationSatelliteSystem();
//Serial.println("Got GPS data");
NavigationData[UNITS] |= power_loss << 1;
//Serial.println("Attempt Transmit");
/// 611/125000
#define VOLTAGE_ADJUST 0.004888
auto extern_temp_volts = analogRead(PORT_EXTERNAL_TEMP_A8);
auto intern_temp_volts = analogRead(PORT_INTERNAL_TEMP_A7);
auto uv_sensor_volts = analogRead(PORT_UV_SENSOR_A10);
prep_msg(extern_temp_volts, intern_temp_volts, uv_sensor_volts);
Serial.print("Msg == "); Serial.println(msg);
for(auto i=0; i < sizeof msg; i++) {
Serial.print(msg[i]); Serial.print(comma_str);
}
Serial.println();
/// [ first 16 bytes: GPS data | 29 bytes - message ] .
memcpy(&IridiumTransmitData[0], &NavigationData[0], sizeof NavigationData);
memcpy(&IridiumTransmitData[sizeof NavigationData], msg, sizeof msg);
/*
Serial.println(extern_temp_volts);
Serial.println(intern_temp_volts);
for( size_t i=0; i < sizeof IridiumTransmitData; i++ ) {
Serial.print(IridiumTransmitData[i]); Serial.print(F("-"));
}
*/
///*
auto const tx_result = fdr.Iridium(PerformTransmitUInt);
waiting_for_tx = true;
switch( tx_result ) {
case 272: { /// TransmitSuccessfulButReceiveFailedUInt
/// don't run receiving data code.
break;
}
case 250: /// NoModemConnectedUInt
case 293: /// No functioning modem found
{
seeprom_write = true;
waiting_for_tx = false;
break;
}
}
Serial.println(tx_result);
//*/
modem_chk_time_old = millis();
}
/// now wait a certain amount of ms for the transmission.
time_curr = millis();
if( waiting_for_tx && (time_curr - modem_chk_time_old) < MODEM_DELAY ) {
return;
}
//Serial.print(time_curr - modem_chk_time_old);
modem_chk_time_old = millis();
auto const modem_status = fdr.Iridium(statusUInt);
Serial.print(modem_status); Serial.println(F("- status res"));
waiting_for_tx = waiting_for_tx && (300 <= modem_status && modem_status <= 340);
if( !waiting_for_tx ) {
*alt3_ptr = modem_status;
seeprom_write = true;
}
if( modem_status >= TransmitSuccessfulAndNoReceiveUInt ) {
switch( modem_status ) {
case TransmitSuccessfulAndNoReceiveUInt: {
/// nothing to do here.
break;
}
case TransmitAndReceiveSuccessfulUInt:
case TransmitAndReceiveSuccessfulPlusReceivePendingUInt:
{
/*
auto const rx_result = fdr.Iridium(getReceivedDataUInt);
if( rx_result==407 ) {
/// 407 is 'receiveDataPlacedInReceiveArrayUint'.
if( modem_status==TransmitAndReceiveSuccessfulPlusReceivePendingUInt ) {
IridiumReceiveData[sizeof IridiumReceiveData - 1] = 0;
}
char const *cmd_str = reinterpret_cast< char const* >(&IridiumReceiveData[0]);
payload_status = cmd_str[0]=='1';
digitalWrite(TASK_LED_PIN, (payload_status != 0)? HIGH : LOW);
}
*/
break;
}
}
}
if( seeprom_write ) {
out_of_mem = !StoreGPSAndIncrBlkSEEPROM();
seeprom_write = false;
Serial.println(F("GPS=>SEEPROM"));
/*
for( size_t i=0; i < sizeof NavigationData; i++ ) {
Serial.print(NavigationData[i]); Serial.print(comma_str);
}
Serial.println();
*/
}
} /// end of loop()
void FillPointerArray() {
/******************************************
LIBRARY VARIABLES
*******************************************/
PointerArray[0] = reinterpret_cast< intptr_t >(&IridiumTransmitData);
PointerArray[1] = reinterpret_cast< intptr_t >(&IridiumReceiveData);
PointerArray[2] = reinterpret_cast< intptr_t >(&NavigationData);
PointerArray[3] = reinterpret_cast< intptr_t >(&GNSS);//element 3 now contains the address of the GNSS variable
PointerArray[4] = reinterpret_cast< intptr_t >(&Modem);
PointerArray[5] = reinterpret_cast< intptr_t >(&AutomaticVideoRecordAtPowerUp);
PointerArray[6] = reinterpret_cast< intptr_t >(&filePathChar[0]);//put the starting address of the string filePath into the pointer array
PointerArray[7] = reinterpret_cast< intptr_t >(&nearFarModemSNbyte);
}
//#define mastercontrol
#ifdef mastercontrol
#define nearFarTest
#define interruptDelim
#define looparoundTest
#define nearfasrtest //this controls a lot more than near far logic. I used it to debug interrupts interfering with various functions.
#define modemNGtest //this controls prints showing modem interactions at setup
#define DiagPrint1
#endif
/*
Change Record
1.0 This file will run on a Pro Micro dedicated to the Iridium Modem. Communications is through the I2C interface. This Pro Micro is called
the Modem Pro Micro (MPM).
1.3 permits any of four modems to be used at the near and far ends. MPM loop around of modem data.
1.4 adds modem status function
Address is 0x0A;
*/
/******************************************
Available Modems
UserRBsn RBsn IMEI
00 13301 300234066438070
01 218642 300534065390120
10 13298 300234066436090
11 218641 300534065396130
there is also a spare modem that has not been provisioned into the Iridium.ino code.
*******************************************/
#include <WString.h> // for FlashString
#include <Stream.h> // for Stream
#include "Arduino.h"
#include <Wire.h> //for I2C
#define MPM__ADDRESS 0xA //MPM_ address,any number from 0x01 to 0x7F
/*******************************************************
F L A G S
*******************************************************/
bool MPM_BusyBool = false;
bool ModemSetupResultWasPickedUpByFDR_Bool = false;
bool NewFDR_CommandRejectedBool = false;
bool SendFirstBlockOfReceivedArrayBool = false;
bool SendSecondBlockOfReceivedArrayBool = false;
bool OneTimeDelayBool = true;//diag
bool PingActiveBool = false;
bool enable = false; //gates diagnostic print statements
//bool NewIridiumCycleBool = false;
//bool CycleActiveBool = false;
bool FoundOhBool = false;
bool modemLooparoundEnabledQbool = false;
bool MPMonlyLooparoundEnabledQbool = false;
bool secondBlockNotFilledBool = false;
bool firstBlockNotFilledBool= false;
//************************************************************
/*************************************************************
C O N S T A N T S
*************************************************************
All return codes have unique numbers.
************************************************************/
const unsigned int DelayBeforeDoingReadFromModemUInt = 20;//I get a solid failure when reading the serial number when delay is 1 ms and works at 2 ms. So cliff is between 1 and 2 ms. be 10x away from it.
const unsigned int DelayAfterDoingPrintToModemUInt = 20;
/*******************************************************/
const long MoreThantheMaxNumberCharactersByteWasProcessedExtractFieldLong = -1;
const long NonNumericCharacterWasFoundBeforeTheCommaExtractFieldLong = -2;
const long NoNumbersFoundBeforeTheCommaExtractFieldLong = -3;
/*******************************************************
Modem Serial Numbers
*******************************************************/
const unsigned long RBsnULong[4]={13301,218642,13298,218641};//these are the four RockBLOCK serial numbers in decimal. Each one can take up to 3 bytes
const String nearIMEIstring[4]={"300234066438070","300534065390120","300234066436090","300534065396130"};//these are the IMEI printed on the modems
//in both arrays, the User serial number, written on the modem in binary, identifies the modem
/***********************************************
M 0 S T A T U S R E T U R N C O D E S
************************************************
0 MO message, if any, transferred successfully.
1 MO message, if any, transferred successfully, but the MT message in the queue was too big to be transferred.
2 MO message, if any, transferred successfully, but the requested Location Update was not accepted.
3..4 Reserved, but indicate MO session success if used.
5..8 Reserved, but indicate MO session failure if used.
10 GSS reported that the call did not complete in the allowed time.
11 MO message queue at the GSS is full.
12 MO message has too many segments.
13 GSS reported that the session did not complete.
14 Invalid segment size.
15 Access is denied. ISU-reported values:
16 ISU has been locked and may not make SBD calls (see +CULK command).
17 Gateway not responding (local session timeout).
18 Connection lost (RF drop).
19 Link failure (A protocol error caused termination of the call).
20..31 Reserved, but indicate failure if used.
32 No network service, unable to initiate call.
33 Antenna fault, unable to initiate call.
34 Radio is disabled, unable to initiate call (see *Rn command).
35 ISU is busy, unable to initiate call.
36 Try later, must wait 3 minutes since last registration.
37 SBD service is temporarily disabled.
38 Try later, traffic management period (see +SBDLOE command)
39..63 Reserved, but indicate failure if used.
64 Band violation (attempt to transmit outside permitted frequency band).
65 PLL lock failure hardware error during attempted transmit.
*****************************************************
S U B R O U T I N E R E T U R N C O D E S
************************************************
My failure returns are 100 to 299
My status returns are 300 to 399
My success returns are 400 to 499
************************************************/
const unsigned long FailureRangeMinUInt = 1;
const unsigned long FailureRangeMaxUInt = 299;
const unsigned long StatusRangeMinUInt = 300;
const unsigned long StatusRangeMaxUInt = 399;
const unsigned long SuccessRangeMinUInt = 400;
const unsigned long SuccessRangeMaxUInt = 499;
/************************************************
F A I L U R E R E T U R N S
************************************************/
const unsigned int FailureAfterSBDIX_UInt = 100;
const unsigned int ModemStatus_TimedOutUInt = 101;
const unsigned int UnexpectedMO_StatusValueUInt = 104;
const unsigned int UnexpectedMOMSN_ValueUInt = 105;
const unsigned int UnexpectedMT_StatusValueUInt = 106;
const unsigned int UnexpectedMTMSN_StatusValueUInt = 107;
const unsigned int UnexpectedMT_SBD_MessageLengthUInt = 108;
const unsigned int UnexpectedMT_SBD_MessageQueuedValueUInt = 109;
const unsigned int ModemFailureAfterSBDIX_UInt = 112;
const unsigned int UnexpectedResponseToSBDIXUInt = 113;
const unsigned int TimeOutAfterSBDIX_UInt = 114;
const unsigned int TimeOutAfterSendingMessageSizeUInt = 116;
const unsigned int MO_BufferClearedErrorResponseUInt = 118;
const unsigned int MO_BufferClearedTimeOutUInt = 119;
const unsigned int InvalidCommand = 120;
const unsigned int MO_BufferClearedUnexpectedResponseUInt = 200;
const unsigned int MT_BufferClearedErrorResponseUInt = 202;
const unsigned int MT_BufferClearedTimeOutUInt = 203;
const unsigned int MT_BufferClearedUnexpectedResponseUInt = 204;
const unsigned int DisableFlowControlRequestTimedOutUInt = 206;
const unsigned int DisableFlowControlRequestUnexpectedResponseUInt = 207;
const unsigned int DisableSBD_RingSetupFailedDueToTimeOutUInt = 208;
const unsigned int DisableSBD_RingSetupFailedDueToUnexpectedResponseUInt = 209;
const unsigned int RingIndicationErrononiouslyEnabledUInt = 231;
const unsigned int TimeOutAfterGetResponseFromVerifyDisableMT_AlertUInt = 232; //no response within TimeLimitULong ms
const unsigned int UnexpectedResponseToSBDMTAUInt = 233;
const unsigned int UnexpectedResponseAfterSendingMessageSizeUInt = 234;
const unsigned int SetupFailedDueToTimeOutUInt = 236;
const unsigned int SetupFailedDueToUnexpectedResponseUInt = 237;
const unsigned int NetworkStatusUnexpectedResponseUInt = 238;
const unsigned int TimeOutNetworkStatusUInt = 239;//no valid response within TimeLimitULong ms
const unsigned int NetworkNotAvailableUInt = 240;
const unsigned int MT_MessageUnexpectedResponseUInt = 242;
const unsigned int MT_MessageTimeOutUInt = 243; //no valid response within TimeLimitULong ms
const unsigned int MT_MessageFailedCheckSumUInt = 244;
const unsigned int MT_MessageTooLongUInt = 245;
const unsigned int ModemSetupFailedDueToTimeOutUInt = 249;
const unsigned int NoModemConnectedUInt = 250;
const unsigned int UnexpectedModemConnectedUInt = 251;
const unsigned int DuplicateTransmitOfDataAttemptedUInt = 255;
const unsigned int YouAreAskingToTransmitTooSoonSoTryLaterUInt = 256;
const unsigned int FlowControlSetupFailedDueToTimeOutUInt = 257;
const unsigned int FlowControlSetupFailedDueToUnexpectedResponseUInt = 258;
const unsigned int StoreConfigurationFailedDueToTimeOutUInt = 259;
const unsigned int StoreConfigurationFailedDueToUnexpectedResponseUInt = 260;
const unsigned int SelectProfileFailedDueToTimeOutUInt = 261;
const unsigned int SelectProfileFailedDueToUnexpectedResponseUInt = 262;
const unsigned int RingIndicationUnexpectedResponseUInt = 263;
const unsigned int OK_SearchTimedOutUInt = 264;
const unsigned int OK_UnexpectedResponseUInt = 265;
const unsigned int SignalStrengthTooLowUInt = 269;
const unsigned int TransmitSuccessfulButReceiveFailedUInt = 272;
const unsigned int UnexpectedFDR_CommandUInt = 273;
const unsigned int MPM_Busy_TransmitCommandRejectedUInt = 274;
const unsigned int PingToMPM_TimedOutUInt = 275;
const unsigned int PingToMPM_SuccessButToModemFailedUInt = 276;
const unsigned int InitialSetupModemStatusValueUInt = 278;
const unsigned int TimeOutWaitingForgetReceivedDataUInt = 279;
const unsigned int NoPingMPM_BusyUInt = 280;
const unsigned int ModemFailedAtSetupUInt = 281;
const unsigned int UnexpectedResponseFromModemDuringSetupUInt = 282;
const unsigned int ModemSetupFailedBecauseMPM_BusyUInt = 283;
const unsigned int ModemFailedAtSetupTimeOutUInt = 284;//added for completeness. This error only detected in FDR
const unsigned int MPM_BusyWhenFDR_AskedForSetupUInt = 285;
const unsigned int MPM_DidNotRespondToRequestForDataUInt = 286;
const unsigned int SoftwareError1UInt = 287;
const unsigned int MPM_BusyUInt = 288;
const unsigned int AskedForPingResultTooSoon_DoPingAgainUInt = 289;
const unsigned int PingToMPM_DidNotRespondUInt = 290;
const unsigned int WrongModemConnectedCheckSerialNumberUInt = 291;
const unsigned int RequestedTransmitTooSoonUInt = 292;
const unsigned int NoFunctioningModemPresentUInt = 293;
const unsigned int TimeOutAfterSendingMessageUInt = 294;
const unsigned int SBD_MessageTimeOutByModemUInt = 295;
const unsigned int SBD_MessageChecksumWrongUInt = 296;
const unsigned int SBD_MessageSizeWrongUInt = 297;
const unsigned int UnexpectedResponseAfterWritingDataToMobileOriginatedBufferUInt = 298;
const unsigned int SBD_MessageSizeTooBigOrTooSmallUInt = 299;
/************************************************
S T A T U S R E T U R N S
************************************************/
const unsigned int SuccessByteAfterSBDIX_UInt = 300;
const unsigned int MessageSizeAcceptedUInt = 301;
const unsigned int MO_BufferClearedSuccessfullyUInt = 302;
const unsigned int MT_BufferClearedSuccessfullyUInt = 303;
const unsigned int OK_FoundUInt = 304;
const unsigned int RingIndicationDisabledUInt = 305;
const unsigned int SetupSuccessfulUInt = 306;
const unsigned int MT_MessageRetrievedCorrectlyUInt = 307;
const unsigned int MT_MessageIsNullUInt = 308;
const unsigned int ModemSetupSuccessfulUInt = 309;
const unsigned int CorrectModemConnectedUInt = 310;
const unsigned int idleUInt = 311;
const unsigned int NetworkAvailableWithAcceptableSignalStrengthUInt = 313;
const unsigned int VerifyDisableMT_AlertUInt = 314;
const unsigned int FlushedUART_BufferUInt = 315;
const unsigned int ArraySentToModemUInt = 316;
const unsigned int InitiateTransmitAndReceiveUInt = 317;
const unsigned int TellModemToClearMO_BufferUInt = 318;
const unsigned int TellModemToClearMT_BufferUInt = 319;
const unsigned int ToldModemToGiveUsTheReceivedMessageUInt = 320;
const unsigned int TransmissionProcessHasBegunUInt = 322;
const unsigned int SentPerformTransmitUInt = 323;
const unsigned int SentgetReceivedDataUInt = 324;
const unsigned int AboutToStartTransmitProcessUInt = 326;
const unsigned int WaitingForOK_FromModemUInt = 327;
const unsigned int InitialReturnCodeValueUInt = 328;
const unsigned int InitialMPM_ResponseValueUInt = 329;
const unsigned int BusySettingUpModemUInt = 330;
const unsigned int PerformingPingUInt = 331;
const unsigned long PingNotRunningUInt = 333;
const unsigned int MPM_Busy_TransmitCommandPendingUInt = 334;
const unsigned int ModemSetupProceedingUInt = 335;
const unsigned int ModemDefaultsSetUInt = 336;
const unsigned int SentPingUInt = 337;
const unsigned int SBD_MessageSuccessfullyWrittenUInt = 338;
const unsigned int MT_MessagePendingUInt = 339;
const unsigned int MT_MessagesPendingUInt = 340;
/************************************************
S U C C E S S R E T U R N S
************************************************/
const unsigned int PingThroughMPM_AndModemSuccessUInt = 400;
const unsigned int ModemReadyForUseUInt = 401;
const unsigned int TransmitSuccessfulAndNoReceiveUInt = 402;
const unsigned int TransmitAndReceiveSuccessfulUInt = 403;
const unsigned int TransmitAndReceiveSuccessfulPlusReceivePendingUInt = 404;
const unsigned int dataLoopAroundEnabledUInt = 405;
const unsigned int dataLooopAroundDisabledUInt = 406;
const unsigned int receiveDataPlacedInReceiveArrayUint = 407;
/************************************************
C O M M A N D S F R O M F D R
************************************************/
const unsigned int PingUInt = 0;
const unsigned int SetUpModemUInt = 1;//upper byte holds modem User RB serial numbers
const unsigned long PerformTransmitUInt = 2;
const unsigned long getReceivedDataUInt = 3;
const unsigned int SendBackFirstBlockOfReceivedArrayUInt = 4;
const unsigned int SendBackSecondBlockOfReceivedArrayUInt = 5;
const unsigned int setLoopAroundUInt = 6;
const unsigned int clearLoopAroundUint = 7;
const unsigned int statusUInt = 8;
/**********************************************************
V A R I A B L E S
***********************************************************/
byte IridiumTransmitDataByte[45] = {4,5};//unique values
byte PreviousIridiumTransmitDataByte[45] = {6,7};//unique values
byte IridiumReceiveDataByte[45] = {8,9};//unique values
byte IridiumReceiveDataFirstBlock[32] = {10,11};//unique values
byte IridiumReceiveDataSecondBlock[13] = {12,13};//unique values
byte i;
byte IndexByte;
byte MOstatusByte = 0;
unsigned int MOMSNUInt = 0;
byte MTstatusByte = 0;
unsigned int MTMSNUInt = 0;
byte MTlengthByte = 0;
byte MTqueuedByte = 0;
byte TextIndexByte = 0;
String BufferText = "";
unsigned int SetupModemStatusUInt = InitialSetupModemStatusValueUInt;
unsigned int ReturnCodeUInt = InitialReturnCodeValueUInt;//viewable by FDR
unsigned int LocalReturnCodeUInt = InitialReturnCodeValueUInt;//not viewable by FDR
unsigned int ModemSetUpReturnCodeUInt = InitialReturnCodeValueUInt;
const unsigned int NoActiveFDR_CommandUInt = 999;
unsigned int FDR_CommandUInt = NoActiveFDR_CommandUInt;
unsigned int StatusForFDR_UInt = 0;
unsigned long TransmissionRecycleTimeULong = 0;//FDR can transmit data via Iridium no faster than once per minute in order to limit the cost. was 60000 but for test code changed to 0
unsigned long StartForTimeStampULong = millis();//I reset this clock when ping arrives so MPM and FDR have their time stamps in sync
unsigned long StartTimeULong = 0;
char character;
unsigned long TimeLimitULong = 0;
unsigned long CycleStartTimeULong = 0;
unsigned long PingStateUInt = PingNotRunningUInt;
byte BusTestCount = 0;
unsigned int NewFDR_CommandUInt = 888;
byte nearFarmodemSNbyte;//value set in FDR.ino and passed here hidden in the high byte of SetUpModemUInt
String IMEIstring;//will hold the near modem's IMEI number which is checked against the one sent back from the modem as part of modem setup
unsigned long farRBsnULong;//will hold the far modem's RockBLOCK serial number printed on the modem. This number takes up to 3 bytes.
/***********************************************************/
unsigned long StartTime = millis(); //test code
void setup()
{
Serial.begin(19200); // USB
Serial1.begin(19200); // 19200 Baud rate set to match that of the RockBLOCK.
//set up I2C
Wire.begin(MPM__ADDRESS); //MPM is the MPM_ with address 0xA.
Wire.onReceive(ReceiveFromFDR_Event);
Wire.onRequest(FDR_RequestsData);
//DirectPath(false);//true means I want HEX output. Once entered, we stay in it so no need to comment out rest of code.
// diagnostic code
unsigned long TimeNow = (millis()-StartTime)/1000;
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" lapse time is "));
Serial.println( TimeNow );
#endif
StartTime = millis();
#ifdef DiagPrint1
Serial.println(__LINE__);// diag
Serial.println(F("******start******"));
#endif
}
unsigned int loopCountUInt = 0;//used to display heartbeat on TX LED built into Pro Micro
bool toggleBool = true;
void loop()
{
loopCountUInt++;//every other time this variable wraps around to 0, we print a "." and flash the yellow transmit LED on the MPM. It is an indication that the MPM is sane.
if(loopCountUInt == 0)
{
if(toggleBool)
{
TXLED1;//turn on TX LED on Pro Micro
toggleBool = false;
Serial.print(".");
}else
{
TXLED0;//turn off TXLED on Pro Micro
toggleBool = true;
}
}
if (OneTimeDelayBool)
{
for(i=0; i < 5;i++)
{
Serial.println(5 - i);
delay(100);
}
Serial.println(F("This is the MPM."));
Serial.println();
OneTimeDelayBool = false;
}
if (FDR_CommandUInt != NoActiveFDR_CommandUInt)
{//diag utility
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.println(millis() - StartForTimeStampULong);
#endif
CycleStartTimeULong = millis();//start diag timer when FDR sends a command
}
if(lowByte(FDR_CommandUInt) == lowByte(SetUpModemUInt))
//If FDR_CommandUInt holds SetUpModemUInt, the low byte is 1. No other command has a 1 as its low byte. The high byte of the command contains the near and far modem's User RockBLOCK serial numbers which are each 2 bits.
{
#ifdef modemNGtest1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
ReturnCodeUInt = ModemSetupProceedingUInt;
nearFarmodemSNbyte = highByte(FDR_CommandUInt);//since low byte matches SetUpMOdemUInt, we know that the high byte is the nearFarmodemSNbyte sent from FDR.
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". Time Stamp "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
Serial.print(F("nearFarmodemSNbyte = "));
Serial.println(nearFarmodemSNbyte);
#endif
getModemSerialNumbers();//translate nearFarmodemSNbyte into its IMEIstring and farRBsnULong
IridiumModemSetup();//it verifies correct modem connected to payload via the IMEIstring and fills ReturnCodeUInt
#ifdef modemNGtest
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
#endif
FDR_CommandUInt = NoActiveFDR_CommandUInt;
}//end of SetUpModemUInt
if (FDR_CommandUInt == statusUInt)
{
#ifdef ModemStatusTesting
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
//nothing to do; FDR just asks for current return code.
FDR_CommandUInt = NoActiveFDR_CommandUInt;
}//end of status function
if (FDR_CommandUInt == PingUInt)
{
StartForTimeStampULong = millis();//start diag timer when ping command received.
#ifdef nearfasrtest1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
PingModem();//ReturnCodeUInt now contains the result.
#ifdef nearfasrtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
#endif
FDR_CommandUInt = NoActiveFDR_CommandUInt;
}//end of ping function
if (FDR_CommandUInt == setLoopAroundUInt)
{
#ifdef nearfasrtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
MPMonlyLooparoundEnabledQbool = true;
//modemLooparoundEnabledQbool = true;//was modemLooparoundEnabledQbool . flag used to enable LoopAround()
ReturnCodeUInt = dataLoopAroundEnabledUInt;//ReturnCodeUInt now contains ack. A status request will return this value if nothing else has run.
#ifdef nearfasrtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
#endif
FDR_CommandUInt = NoActiveFDR_CommandUInt;
}//end of loop around enable
if (FDR_CommandUInt == clearLoopAroundUint)
{
#ifdef nearfasrtest1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
MPMonlyLooparoundEnabledQbool = false;//was modemLooparoundEnabledQbool. flag used to enable LoopAround()
ReturnCodeUInt = dataLooopAroundDisabledUInt;//ReturnCodeUInt now contains ack. A status request will return this value if nothing else has run.
#ifdef nearfasrtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
#endif
FDR_CommandUInt = NoActiveFDR_CommandUInt;
}//end of loop around disabled
if (FDR_CommandUInt == PerformTransmitUInt)
{
ReturnCodeUInt = AboutToStartTransmitProcessUInt;
PerformTransmitAndReceiveSequence();//ReturnCodeUInt constantly being updated and will be sent back to FDR when requested within an interrupt. We only return upon conclusion: success or failure. If modem loop around has been enabled, we echo data via modem regardless of satellite state. If MPM loop around is enabled, we echo back data regardless of modem with a return code of dataLoopAroundEnabledUInt. If not in looparound and transmitting data is the same as the last session, return code is DuplicateTransmitOfDataAttemptedUInt to prevent wasting satellite credits.
#ifdef DiagPrint1
if ((ReturnCodeUInt == TransmitAndReceiveSuccessfulUInt) ||(ReturnCodeUInt == TransmitSuccessfulAndNoReceiveUInt)||(ReturnCodeUInt == TransmitAndReceiveSuccessfulPlusReceivePendingUInt))
{
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
//delay(55000);//test code because we also have test of command to ensure we do not get too high a rate of transmit commands. diag
//while(1);//stop program after single success
}
#endif
FDR_CommandUInt = NoActiveFDR_CommandUInt;
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
#endif
}
if(SendFirstBlockOfReceivedArrayBool && firstBlockNotFilledBool)
{
#ifdef nearfasrtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
Serial.println(F("IridiumReceiveDataByte[]:"));
for(byte i=0;i<45;i++)
{
Serial.print(IridiumReceiveDataByte[i]);
Serial.print(" ");
}
Serial.println(F("end of data"));
#endif
for(byte index=0;index<32;index++)
{
IridiumReceiveDataFirstBlock[index] = IridiumReceiveDataByte[index];
}
#ifdef nearfasrtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
Serial.println(F(" first array:"));
for(byte i=0;i<32;i++)
{
Serial.print(IridiumReceiveDataFirstBlock[i]);
Serial.print(" ");
}
Serial.println(F("end of first block of data"));
#endif
firstBlockNotFilledBool = false;//this prevents us from executing this code a second time
return;
}
if(SendSecondBlockOfReceivedArrayBool && secondBlockNotFilledBool)
{
#ifdef nearfasrtest1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
Serial.println(F("IridiumReceiveDataByte[]:"));
for(byte i=0;i<45;i++)
{
Serial.print(IridiumReceiveDataByte[i]);
Serial.print(" ");
}
Serial.println(F("end of all data"));
#endif
for(byte index=0;index<13;index++)
{
IridiumReceiveDataSecondBlock[index] = IridiumReceiveDataByte[index+32];
}
#ifdef nearfasrtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
Serial.println(F("second array:"));
for(byte i=0;i<13;i++)
{
Serial.print(IridiumReceiveDataSecondBlock[i]);
Serial.print(F(" "));
}
Serial.println(F("end of second block of data"));
#endif
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("() MPM: "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
secondBlockNotFilledBool = false;
return;
}
}//end of loop()
void TellModemWeWillBeSending45BytesPlusOverhead()
/*tells modem to expect a total of 50 bytes of data to be sent from the Pro Micro. Follow this subroutine with GetResponseFromModemAfterSendingMessageSize()
reference: page 95
*/
{
FlushUART_Buffer(); //defensive action in case last subroutine did not empty buffer
Serial1.print("AT+SBDWB=50\r"); //tell modem we are sending 45 bytes of data plus 5 bytes of overhead. Overhead includes "RB" and a 3 byte serial number of the termination modem
delay(DelayAfterDoingPrintToModemUInt);
}
void WriteDataToMobileOriginatedBuffer()
{//IridiumTransmitDataByte[] was defined in setup and this is the array that is passed as a global array. After being used, it is saved as PreviousIridiumTransmitDataByte[45]. Before doing a transmit, we check for this pattern. If seen, we reject command in order to prevent sending the same data twice in a row. farRBsnULong is the
int checkSum = 0;
//start sending SBD message as bytes to modem starting with the letters "RB" for RockBLOCK...
Serial1.write('R');//ASCII for "R"
delay(DelayAfterDoingPrintToModemUInt);
Serial1.write('B');//ASCII for "B"
delay(DelayAfterDoingPrintToModemUInt);
// and followed with serial number in binary.
//Serial number for the ground based modem is 3 bytes. For example, far modem could be marked RockBLOCK edc (= 0x35611).
delay(DelayAfterDoingPrintToModemUInt);
//farRBsnULong contains the top, middle and low bytes of the RBsn.
byte topByteSNbyte = (farRBsnULong & 0x00FF0000)>>16;//farRBsnULong is 4 bytes but SN is only lower 3 bytes. I mask off all but byte 2 and then shift it down 16 bits which is 2 bytes. This puts the top byte into the MSB position.
byte middleByteSNbyte = (farRBsnULong & 0x0000FF00)>>8;//this time I want byte 1 so mask off all but byte 1 and shift result over 8 bits
byte lowByteSNbyte = (farRBsnULong & 0x000000FF);//this time I want byte 0 so no shift after masking off lowest byte
/// 0x35611 & 0x00FF0000 == 0x30000
/// 0x30000 >> 0x10 = 0x3
/// 0x56 == 86
/// 0x11 == 17
#ifdef nearFarTest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
Serial.print(F("farRBsnULong = "));
Serial.println(farRBsnULong,HEX);
Serial.print(F("topByteSNbyte = "));
Serial.println(topByteSNbyte,HEX);
Serial.print(F("middleByteSNbyte = "));
Serial.println(middleByteSNbyte,HEX);
Serial.print(F("lowByteSNbyte = "));
Serial.println(lowByteSNbyte,HEX);
#endif;
//populate MO buffer with 3 byte RB s/n
Serial1.write(topByteSNbyte);
delay(DelayAfterDoingPrintToModemUInt);
Serial1.write(middleByteSNbyte);
delay(DelayAfterDoingPrintToModemUInt);
Serial1.write(lowByteSNbyte);
delay(DelayAfterDoingPrintToModemUInt);
checkSum += 'R' + 'B' + topByteSNbyte + middleByteSNbyte + lowByteSNbyte;//RB + 3 byte RB s/n
#ifdef nearFarTest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
for(int i = 0; i < 45; i++)
{
#ifdef nearFarTest22
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
Serial1.write(IridiumTransmitDataByte[i]); //send next byte of data to modems Mobile Origination buffer
delay(DelayAfterDoingPrintToModemUInt);
checkSum += IridiumTransmitDataByte[i]; //add the data to the running sum. Note that a byte is being added to an integer
}
// Writing the check sum to the MO register
Serial1.write(highByte(checkSum));
delay(DelayAfterDoingPrintToModemUInt);
Serial1.write(lowByte(checkSum));
delay(DelayAfterDoingPrintToModemUInt);
//all 50 bytes should be in the MO buffer now
ReturnCodeUInt = ArraySentToModemUInt;
#ifdef nearFarTest
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" checkSum = "));
Serial.println( checkSum );
#endif
}//end of WriteDataToMobileOriginatedBuffer()
void SendMessageInMobileOriginatedBuffer()//initiate transmit and receive exchange with satellite
{ //use this when not responding to a ring
FlushUART_Buffer(); //defensive action in case last subroutine did not empty buffer
Serial1.print("AT+SBDIX\r");
Serial.println("AT+SBDIX\r");
delay(DelayAfterDoingPrintToModemUInt);
ReturnCodeUInt = InitiateTransmitAndReceiveUInt;
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
}
void GetResponseFromModemAfterSendingMessage()
{//it handles the response after AT+SBDIX has been sent to the modem
/***************************************************
R e t u r n c o d e d e s c
----------------------------------------------------
UnexpectedMT_SBD_MessageLengthUInt
MO and/or MT messages, if any, transferred successfully
TimeOutAfterSBDIX_UInt give up after waiting TimeLimitULong ms
ModemFailureAfterSBDIX_UInt something went wrong with the modem; see MOstatus and MTstatus for details
UnexpectedResponseToSBDIXUInt response from modem not related to SBDIX command
****************************************************/
//Command Response: +SBDIX:<MO status>,<MOMSN>,<MT status>,<MTMSN>,<MT length>,<MTqueued>
//See 5.144 of the ISU AT Command Reference, version 2
//if MO status has a value other than 0, we have a failure.
byte number; //used when reading numbers
// "+SBDIX:10,23456,7,45678,50,2" is test string
//max number of characters is 29 plus ends with NULL
BufferText="";//initialize string to zero length
long FieldValueLong = 0;
TimeLimitULong = 40000; //in milliseconds
StartTimeULong = millis();
while(1)
{//keep looking until start of response found or we run out of time
Serial.println(__LINE__);
while(Serial1.available() < 1)
//look for bytes in buffer until either we find some or run out of time
{
if((millis() - StartTimeULong) > TimeLimitULong)
{
ReturnCodeUInt = TimeOutAfterSBDIX_UInt;//no bytes Received within TimeLimitULong ms
return;
}
}
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
while(Serial1.available() > 0)//keep reading stream from modem until all characters collected
{
delay(DelayBeforeDoingReadFromModemUInt); //emperically found that waiting more than 0.5ms is needed for reliable response
character = Serial1.read(); //read back one character of the response from SBDIX command
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" character in hex is "));
Serial.println( character, HEX);
Serial.print(F(" BufferText string is "));
Serial.println( BufferText );
#endif
BufferText.concat(character);//build up BufferText string for testing
}//have recorded entire string but must add "," to the end
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
BufferText.concat(","); //ExtractField() needs a "," at the end of each field. I add "," at the end of the string because the last field didn't have one
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.println(F(" BufferText string is:"));
Serial.println( BufferText );
#endif
//see Iridium docs, page 120 for command response
if (BufferText.indexOf("+SBDIX:") >= 0)
{//we have found "+SBDIX:" which is the start of the command response for SDBIX. I assume entire response is now in buffer
//+SBDIX:<MO status>,<MOMSN>,<MT status>,<MTMSN>,<MT length>,<MTqueued>
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.println(F(" +SBDIX: found "));
#endif
TextIndexByte = BufferText.indexOf("+SBDIX:") + 7; //first character position after +SBDIX:
//note that +SBDIX: does not have to be at the start of the string but the fields must follow it although embedded blanks will be ignored. The indexOf is the start of the substring and "+SBDIX:" is 7 characters
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
//Mobile Origination Status
FieldValueLong = ExtractField(2); //max number of digits is 2
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" MO status raw field value = "));
Serial.println( FieldValueLong );
#endif
if ( FieldValueLong < 0)
{
ReturnCodeUInt = UnexpectedMO_StatusValueUInt;
return;
}
MOstatusByte = lowByte(FieldValueLong);
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" MOstatusByte is "));
Serial.println( MOstatusByte );
#endif
if(MOstatusByte > 0)
{//See 5.144 in IRDM docs. This is the newer version of the response
ReturnCodeUInt = MOstatusByte;
return;
}
//Mobile Originated Message Sequence Number (MOMSN)
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" TextIndexByte = "));
Serial.println( TextIndexByte );
#endif
FieldValueLong = ExtractField(5);//max number of digits is 5
if (FieldValueLong < 0)
{
ReturnCodeUInt = UnexpectedMOMSN_ValueUInt;
return;
}
MOMSNUInt = FieldValueLong; //implicit conversion from a long to an unsigned integer
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" MOMSNUInt is "));
Serial.println( MOMSNUInt );
#endif
//Mobile Terminated (MT) status
Serial.println(__LINE__);
FieldValueLong = ExtractField(1);//max number of digits is 1
if (FieldValueLong < 0)
{
ReturnCodeUInt = UnexpectedMT_StatusValueUInt;
return;
}
MTstatusByte = lowByte(FieldValueLong);
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" MTstatusByte is "));
Serial.println( MTstatusByte );
#endif
//Mobile Terminated Message Sequence Number (MTMSN)
FieldValueLong = ExtractField(5);//max number of digits is 5
if (FieldValueLong < 0)
{
ReturnCodeUInt = UnexpectedMTMSN_StatusValueUInt;
return;
}
MTMSNUInt = FieldValueLong; //implicit conversion from long to unsigned integer
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" MTMSNUInt is "));
Serial.println( MTMSNUInt );
#endif
//Mobile Terminated SBD message length (in bytes)
FieldValueLong = ExtractField(2);//max number of digits is 2
if (FieldValueLong < 0)
{
ReturnCodeUInt = UnexpectedMT_SBD_MessageLengthUInt;
return;
}
MTlengthByte = lowByte(FieldValueLong);
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" MTlengthByte is "));
Serial.println( MTlengthByte );
#endif
//Mobile Terminated SBD messages queued in server.
FieldValueLong = ExtractField(1);//max number of digits is 1
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" FieldValueLong = "));
Serial.println( FieldValueLong );
#endif
if (FieldValueLong < 0)
{
ReturnCodeUInt = UnexpectedMT_SBD_MessageQueuedValueUInt;
return;
}
MTqueuedByte = lowByte(FieldValueLong);
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" MTqueuedByte is "));
Serial.println( MTqueuedByte );
#endif
if(MTqueuedByte == 1)ReturnCodeUInt = MT_MessagePendingUInt;
if(MTqueuedByte > 1)ReturnCodeUInt = MT_MessagesPendingUInt;
}else{
//unexpected response
ReturnCodeUInt = UnexpectedResponseToSBDIXUInt;
return;
}
//Now have parced response so ready to determine overall status
//MOstatusByte = 0 means MO message, if any, transferred successfully.
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" MOstatusByte = "));
Serial.println( MOstatusByte );
Serial.print(F(" MTstatusByte = "));
Serial.println( MTstatusByte );
Serial.print(F(" MTqueuedByte = "));
Serial.println( MTqueuedByte );
#endif
if((MOstatusByte == 0) && (MTstatusByte == 0))
{
ReturnCodeUInt = TransmitSuccessfulAndNoReceiveUInt;
return;
}
if((MOstatusByte == 0) && (MTstatusByte == 1) && (MTqueuedByte == 0))
{//MOstatusByte of 0 means SBD message successfully received from the server. MTstatusByte of 1 means an SBD message was successfully received from the server. MTqueuedByte = 0 means no messages queued in server
ReturnCodeUInt = TransmitAndReceiveSuccessfulUInt;
return;
}
if((MOstatusByte == 0) && (MTstatusByte == 1) && (MTqueuedByte > 0))
{//MOstatusByte of 0 means SBD message successfully received from the server. MTstatusByte of 1 means an SBD message was successfully received from the server. MTqueueByte > 0 means one or more messages are waiting to be sent up to the Payload.
ReturnCodeUInt = TransmitAndReceiveSuccessfulPlusReceivePendingUInt;
return;
}
if(MOstatusByte > 0)
{
ReturnCodeUInt = MOstatusByte; //MTstatusByte > 0 is an error code and goes as high as 65.
return;
}
ReturnCodeUInt = FailureAfterSBDIX_UInt;//I think I cover all possible values for MOstatusByte and MTstatusByte but just in case I missed something, we will return with this.
return;
} //bottom of while(1) so return to top and check timer first
}//end of GetResponseFromModemAfterSendingMessage()
long ExtractField(byte MaxNumberCharactersByte)
{
/*The string "BufferText" contains the characters from the modem. Using TextIndexByte to find the first character and "," to mark the end of the field, this subroutine converts each numeric ASCII character into its decimal equivalent and then combines them into an integer which is returned. TextIndexByte is advanced to start of next field. Any blanks found within a field are ignored but the character index is advanced.
The subroutine returns a long because two of the fields return unsigned integers and that won't permit me to pass values <0 as error flags. A long can carry the largests value stored in an unsigned int yet also have negative numbers.
************************************************************
R E T U R N C O D E S
************************************************************
Description value
MoreThantheMaxNumberCharactersByteWasProcessedExtractFieldLong -1
NonNumericCharacterWasFoundBeforeTheCommaExtractFieldLong -2
NoNumbersFoundBeforeTheCommaExtractFieldLong -3
integer decimal value of the field (always >= 0)
************************************************************/
byte CharacterCountByte = 0;
long FieldContentLong = 0;
while(BufferText[TextIndexByte] != 0x2C)
{ //while we don't have a "," we collect ASCII characters
#ifdef DiagPrint1
if (TextIndexByte > 10){
Serial.println(__LINE__);
Serial.print(F(" TextIndexByte = "));
Serial.println( TextIndexByte );
Serial.print(F("BufferText[TextIndexByte] in hex = "));
Serial.println( BufferText[TextIndexByte], HEX );
}
#endif
if((BufferText[TextIndexByte] >= 0x30) && (BufferText[TextIndexByte] <= 0x39))
{//we have an ASCII numeric so convert it to a decimal and add to total.
#ifdef DiagPrint1
if( TextIndexByte > 21)
{
Serial.println(__LINE__);
Serial.print(F(" FieldContentLong = "));
Serial.println( FieldContentLong );
}
#endif
FieldContentLong = (FieldContentLong*10) + (long(BufferText[TextIndexByte] - 0x30)); //move last digit to left and add new digit; The ASCII charcter for zero is 0x30 so by subtracting 0x30 we get the decimal equiv. for the corresponding number.
#ifdef DiagPrint1
if( TextIndexByte >21){
Serial.println(__LINE__);
Serial.print(F(" FieldContentLong = "));
Serial.println( FieldContentLong );
}
#endif
CharacterCountByte = CharacterCountByte +1; //increment the count of characters being summed but if a blank is found, do not count it in the Character Count
}
TextIndexByte = TextIndexByte + 1; //prepare to access next character position
if(CharacterCountByte > MaxNumberCharactersByte) return MoreThantheMaxNumberCharactersByteWasProcessedExtractFieldLong;//max field size searched yet no "," found
//ready to process next character
}
if (CharacterCountByte <1)
{
return NoNumbersFoundBeforeTheCommaExtractFieldLong;
}else{
TextIndexByte = TextIndexByte + 1; //at end of field so move pointer to first digit in next field
return FieldContentLong;
}
}
void GetResponseFromModemAfterSendingMessageSize()
{//it handles the response after AT+SBDWB has been sent to the modem
/***************************************************
R e t u r n c o d e
----------------------------------------------------
MessageSizeAcceptedUInt
TimeOutAfterSendingMessageSizeUInt no response within TimeLimitULong ms
SBD_MessageSizeTooBigOrTooSmallUInt
UnexpectedResponseAfterSendingMessageSizeUInt
****************************************************/
TimeLimitULong = 20000; //in milliseconds
//See pg 95
BufferText="";//initialize string to zero length
StartTimeULong = millis(); //prevents subroutine hanging up if it doesn't get a response from the modem
while (Serial1.available() < 1)
{
if((millis() - StartTimeULong) > TimeLimitULong)
{
ReturnCodeUInt = TimeOutAfterSendingMessageSizeUInt;//no response Received within TimeLimitULong ms
return;
}
}
while(Serial1.available() > 0)//keep reading stream from modem until all characters collected
{
delay(DelayBeforeDoingReadFromModemUInt); //emperically found that waiting more than 0.5ms is needed for reliable response
character = Serial1.read(); //read back one character of the response from SBDWB command
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" character in hex is "));
Serial.println( character, HEX);
#endif
BufferText.concat(character); //build up BufferText string
}//have recorded entire string
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" entire BufferText string is "));
Serial.println( BufferText );
#endif
if (BufferText.indexOf("READY") >= 0)
{
ReturnCodeUInt = MessageSizeAcceptedUInt;
return;
}
if (BufferText.indexOf("3") >= 0)
{
ReturnCodeUInt = SBD_MessageSizeTooBigOrTooSmallUInt;
return;
}
//if we got here, response is unexpected so drop an error code
ReturnCodeUInt = UnexpectedResponseAfterWritingDataToMobileOriginatedBufferUInt;
return;
}//end of GetResponseFromModemAfterSendingMessageSize()
void ClearMO_MessageBuffer()
{
/*
After collecting string from Mobile Originated buffer, we clear buffer. pages 82 and 123
*/
FlushUART_Buffer(); //defensive action in case last subroutine did not empty buffer
SetToNumericResponses();//in order to get the complete response, I must be in Numeric Response mode. I'll return to Textural Response mode when done reading result.
Serial1.print("AT+SBDD0\r"); //tell modem to clear the MO buffer. Follow this subroutine with GetResponseFromClearMO_MessageBuffer()
delay(DelayAfterDoingPrintToModemUInt);
ReturnCodeUInt = TellModemToClearMO_BufferUInt;
}
void GetResponseFromClearMO_MessageBuffer()
{//it handles the response after AT+SBDD0 has been sent to the modem. On pg 82 it says the response should be a 0 for success or a 1 for failure. This is true only in numeric mode which is set by "AT V0\r". I then get <cr><lf>OK<cr><lf> 0 <cr> which must mean buffer cleared successfully. <cr><lf>OK<cr><lf> 1 <cr> must mean failure. When done, I change back to Textual Responses mode with AT V1\r.
/***************************************************
R e t u r n c o d e s
----------------------------------------------------
MO_BufferClearedSuccessfullyUInt
MO_BufferClearedErrorResponseUInt
MO_BufferClearedTimeOutUInt
MO_BufferClearedUnexpectedResponseUInt
****************************************************/
TimeLimitULong = 20000; //in milliseconds
byte number; //used when reading numbers
StartTimeULong = millis(); //prevents subroutine hanging up if it doesn't get a response from the modem
BufferText = "";
while (Serial1.available() < 1)
{
if((millis() - StartTimeULong) > TimeLimitULong)
{
ReturnCodeUInt = MO_BufferClearedTimeOutUInt;//no response Received within TimeLimitULong ms
return;
}
}
while(Serial1.available() > 0)//keep reading stream from modem until all characters collected
{
delay(DelayBeforeDoingReadFromModemUInt); //emperically found that waiting more than 0.5ms is needed for reliable response
character = Serial1.read(); //read back one character of the response from SBDWB command
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" character in hex is "));
Serial.println( character, HEX);
#endif
BufferText.concat(character); //build up BufferText string
}//have recorded entire string in BufferText.
SetToTextualResponses();//switch back to Textual Responses. Was set to numeric response in ClearMO_MessageBuffer().
if (BufferText.indexOf("1") >= 0)
{
ReturnCodeUInt = MO_BufferClearedErrorResponseUInt;//MO buffer could not be cleared. This would be a modem fault.
return;
}
if (BufferText.indexOf("0") >= 0)
{
ReturnCodeUInt = MO_BufferClearedSuccessfullyUInt;
return;
}
//zero means MO buffer was cleared successfully.
//to get here, I did not see 0 or 1
ReturnCodeUInt = MO_BufferClearedUnexpectedResponseUInt;
return;
}//end of GetResponseFromClearMO_MessageBuffer()
void ClearMT_MessageBuffer()
{
/*
After collecting string from Mobile Terminated buffer, we clear buffer. pages 82 and 123
*/
FlushUART_Buffer(); //defensive action in case last subroutine did not empty buffer
SetToNumericResponses();//in order to get the complete response, I must be in Numeric Response mode. I'll return to Textural Response mode when done reading result.
Serial1.print("AT+SBDD1\r"); //tell modem to clear the MT buffer. Follow this subroutine with GetResponseFromClearMT_MessageBuffer()
delay(DelayAfterDoingPrintToModemUInt);
ReturnCodeUInt = TellModemToClearMT_BufferUInt;
}
unsigned int GetResponseFromClearMT_MessageBuffer()
{//it only handles the response after AT+SBDD1 has been sent to the modem. On pg 82 it says the response should be a 0 for success or a 1 for failure. This is true only in numeric mode which is set by "AT V0\r". I then get <cr><lf>OK<cr><lf> 0 <cr> which must mean buffer cleared successfully. <cr><lf>OK<cr><lf> 1 <cr> must mean failure. When done, I change back to Textual Responses mode with AT V1\r.
/***************************************************
R e t u r n c o d e
----------------------------------------------------
MT_BufferClearedSuccessfullyUInt
MT_BufferClearedErrorResponseUInt
MT_BufferClearedTimeOutUInt
MT_BufferClearedUnexpectedResponseUInt
****************************************************/
TimeLimitULong = 20000; //in milliseconds
byte number; //used when reading numbers
StartTimeULong = millis(); //prevents subroutine hanging up if it doesn't get a response from the modem
BufferText = "";
while (Serial1.available() < 1)
{
if((millis() - StartTimeULong) > TimeLimitULong) return MT_BufferClearedTimeOutUInt;//no response Received within TimeLimitULong ms
}
while(Serial1.available() > 0)//keep reading stream from modem until all characters collected
{
delay(DelayBeforeDoingReadFromModemUInt); //emperically found that waiting more than 0.5ms is needed for reliable response
character = Serial1.read(); //read back one character of the response from SBDWB command
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" character in hex is "));
Serial.println( character, HEX);
#endif
BufferText.concat(character); //build up BufferText string
}//have recorded entire string in BufferText.
SetToTextualResponses();//switch back to Textual Responses.
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" BufferText is "));
Serial.println( BufferText );
Serial.print(F(" BufferText.indexOf(0) = "));
Serial.println( BufferText.indexOf("0") );
#endif
if (BufferText.indexOf("1") >= 0) return MT_BufferClearedErrorResponseUInt;//MT buffer could not be cleared. This would be a modem fault. Test is of getting both OK and 1.
if (BufferText.indexOf("0") >= 0) return MT_BufferClearedSuccessfullyUInt;
//zero means MT buffer was cleared successfully. Test is of getting both OK and 0.
//to get here, I did not see 0 or 1
return MT_BufferClearedUnexpectedResponseUInt;
}
unsigned int IdentifyModem()
{
/************************************************
L O C A L R E T U R N C O D E S
************************************************
NoModemConnectedUInt
UnexpectedModemConnectedUInt
CorrectModemConnectedUInt
************************************************
The correct modem for the payload has an International Mobile Equipment Identity (IMEI) of 300234066436090. Only with the correct modem connected will the code talk to it.
************************************************/
TimeLimitULong = 20000; //in milliseconds
//IMEIstring should be the IMEI printed on the near modem. example: for modem 00 it is "300234066438070";
BufferText = "";
byte IMEI_DigitCountByte = 0; //limits read to length of IMEI because after IMEI we get cr lf zero cr.
StartTimeULong = millis(); //prevents subroutine hanging up if it doesn't get a response from the modem
FlushUART_Buffer(); //defensive action in case last subroutine did not empty buffer
#ifdef modemNGtest1
Serial.println(__LINE__);
Serial.print(F(" timestamp = "));
Serial.println( millis() );
#endif
Serial1.print("AT+CGSN\r"); //Ask modem for its International Mobile Equipment Identity (IMEI). It will respond with 15 digit number followed by cr lf zero cr.
delay(DelayAfterDoingPrintToModemUInt);
#ifdef modemNGtest
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
while (Serial1.available() < 1)
{
if((millis() - StartTimeULong) > TimeLimitULong)
{
Serial.println(__LINE__);
return NoModemConnectedUInt;//no response Received within TimeLimitULong ms means no modem connected
}
Serial.println(__LINE__);
//see if we ever find no bytes in buffer
}
while((Serial1.available() > 0) && (IMEI_DigitCountByte < 15)) //keep reading stream from modem until all characters collected
{
#ifdef modemNGtest1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
delay(DelayBeforeDoingReadFromModemUInt); //emperically found that waiting more than 0.5ms is needed for reliable response
character = Serial1.read();
#ifdef modemNGtest1
Serial.println(__LINE__);
Serial.print(F(" character in hex = "));
Serial.println( character, HEX );
#endif
if((character >= 0x30) && (character <= 0x39))
{
BufferText.concat(character);//read back one numeric character from the modem in ASCII
IMEI_DigitCountByte++;
#ifdef modemNGtest1
Serial.println(__LINE__);
Serial.print(F(" character in hex is "));
Serial.println( character, HEX);
#endif
}
#ifdef modemNGtest1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
} //IMEI now collected, non-numberics removed, and assembled into one number
#ifdef modemNGtest1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
FlushUART_Buffer(); // toss cr lf zero cr
#ifdef modemNGtest
Serial.print(F("line number "));
Serial.println(__LINE__);
Serial.print(F("BufferText = "));
Serial.println( BufferText );
Serial.print(F("IMEIstring = "));
Serial.println( IMEIstring );
#endif
if (BufferText == IMEIstring)
{
return CorrectModemConnectedUInt;
}
#ifdef modemNGtest
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
return UnexpectedModemConnectedUInt;
}//end of IdentifyModem()
unsigned int SetModemDefaults()
{
/************************************************
R E T U R N C O D E S
************************************************
ModemReadyForUseUInt
RingIndicationErrononiouslyEnabledUInt
TimeOutAfterGetResponseFromVerifyDisableMT_AlertUInt
************************************************
See page 122, section 8.2. Set default configuration to no flow control, SBD automatic notifications enabled. If any commands do not get a response of OK within TimeLimitULong ms, we return a failure.
************************************************/
FlushUART_Buffer(); //defensive action in case last subroutine did not empty buffer
Serial1.print("AT&K0\r"); //Disable RTS/CTS flow control
delay(DelayAfterDoingPrintToModemUInt);
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
ReturnCodeUInt = TestForOK();
if (ReturnCodeUInt == OK_SearchTimedOutUInt)return DisableFlowControlRequestTimedOutUInt;
if (ReturnCodeUInt == OK_UnexpectedResponseUInt) return DisableFlowControlRequestUnexpectedResponseUInt;
FlushUART_Buffer(); //defensive action in case last subroutine did not empty buffer
Serial1.print("AT+SBDMTA=0\r"); //Disable SBD ring indication because it generates an autonomous response
delay(DelayAfterDoingPrintToModemUInt);
ReturnCodeUInt = TestForOK();
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
if (ReturnCodeUInt == OK_SearchTimedOutUInt)return DisableSBD_RingSetupFailedDueToTimeOutUInt;
if (ReturnCodeUInt == OK_UnexpectedResponseUInt)return DisableSBD_RingSetupFailedDueToUnexpectedResponseUInt;
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
VerifyDisableMT_Alert();//send "AT+SBDMTA?<cr>" and then can read UART buffer for answer
ReturnCodeUInt = GetResponseFromVerifyDisableMT_Alert();//there is no response to AT+SBDMTA=0 but I can send AT+SBDMTA? to read back state. Response is +SBDMTA:0 for ring disabled or +SBDMTA:1 for enabled which is the default.
if (ReturnCodeUInt != RingIndicationDisabledUInt) return ReturnCodeUInt;
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
FlushUART_Buffer(); //defensive action in case last subroutine did not empty buffer
Serial1.print("AT&W0\r"); //Store the configuration as profile 0
delay(DelayAfterDoingPrintToModemUInt);
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
ReturnCodeUInt = TestForOK();
if (ReturnCodeUInt == 1)return StoreConfigurationFailedDueToTimeOutUInt;
if (ReturnCodeUInt == 2)return StoreConfigurationFailedDueToUnexpectedResponseUInt;
FlushUART_Buffer(); //defensive action in case last subroutine did not empty buffer
Serial1.print("AT&Y0\r"); //Select profile 0 as the power-up default
delay(DelayAfterDoingPrintToModemUInt);
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
ReturnCodeUInt = TestForOK();
if (ReturnCodeUInt == OK_SearchTimedOutUInt)return SelectProfileFailedDueToTimeOutUInt;
if (ReturnCodeUInt == OK_UnexpectedResponseUInt)return SelectProfileFailedDueToUnexpectedResponseUInt;
//all commands successful
return ModemDefaultsSetUInt;
}//end of SetModemDefaults()
unsigned int TestForOK()
{//subroutine reads UART buffer and saves it in BufferText. Then it scans it for "OK" and returns ReturnCodeUInt.
/************************************************
R E T U R N S
************************************************
OK_FoundUInt
OK_SearchTimedOutUInt
OK_UnexpectedResponseUInt
************************************************
If we do not get a response of OK within TimeLimitULong ms, we return with OK_SearchTimedOutUInt.
************************************************
No Return Codes are set.
************************************************/
LocalReturnCodeUInt = WaitingForOK_FromModemUInt;
TimeLimitULong = 1000; //in milliseconds. Rock7 said max time I should need to wait is 1 second.
BufferText = "";
StartTimeULong = millis(); //prevents subroutine hanging up if it doesn't get a response from the modem
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
LocalReturnCodeUInt = OK_UnexpectedResponseUInt;
while (Serial1.available() < 1)
{
if((millis() - StartTimeULong) > TimeLimitULong) return OK_SearchTimedOutUInt;//no response Received within TimeLimitULong ms means no modem connected
}
while(Serial1.available() > 0)
{//keep reading stream from modem until all characters collected
delay(DelayBeforeDoingReadFromModemUInt); // emperically found that waiting more than 0.5ms is needed for reliable response
character = Serial1.read(); //read back one character of the response
#ifdef DiagPrint1Serial.println(__LINE__);
Serial.print(F(" character is "));
Serial.println(character,HEX);
#endif
if (character == 'O')
{
FoundOhBool = true;
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
}
if ((FoundOhBool == true)&&(character == 'K'))
{
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
LocalReturnCodeUInt = OK_FoundUInt;
//note that "O" and "K" must be present in this order but can have characters between them
FlushUART_Buffer();//don't leave trash in buffer
}
//otherwise, keep looking for "O" and then "K" until buffer empty
#ifdef DiagPrint11
Serial.println(__LINE__);
Serial.print(F(" character is "));
Serial.println(character,HEX);
#endif
}
//buffer now empty so return what was found
return LocalReturnCodeUInt;
}
void DirectPath(bool HexQ)
{
//directly connects terminal emulator to modem
while(1)
{
while(Serial1.available() > 0)
{
delay(DelayBeforeDoingReadFromModemUInt);
character = Serial1.read();
if (HexQ)
{
Serial.println(character, HEX);
}else{
Serial.print(character);
}
}
while(Serial.available() > 0)
{
character = Serial.read();
Serial1.print(character);
delay(DelayAfterDoingPrintToModemUInt);
}
}
}
unsigned int GetResponseFromVerifyDisableMT_Alert()
{//it handles the response after AT+SBDMTA? has been sent to the modem
/***************************************************
R e t u r n c o d e d e s c
----------------------------------------------------
RingIndicationDisabledUInt
RingIndicationErrononiouslyEnabledUInt
TimeOutAfterGetResponseFromVerifyDisableMT_AlertUInt no valid response within TimeLimitULong ms
****************************************************/
//Command Response: +SBDMTA:<mode> where mode should be either 0 for disable ring indication or 1 for Enable ring indication (default)
//See pg 90
TimeLimitULong = 20000; //in milliseconds
BufferText="";//initialize string to zero length
StartTimeULong = millis(); //prevents subroutine hanging up if it doesn't get a response from the modem
//We will look for the expected string until time is up and will tolerate the buffer temporarily going empty.
while(Serial1.available() < 1)
//look for any bytes in buffer until either we find some or run out of time
{
if((millis() - StartTimeULong) > TimeLimitULong)return TimeOutAfterGetResponseFromVerifyDisableMT_AlertUInt;//no bytes Received within TimeLimitULong ms
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
}
//at least one byte came in
while(Serial1.available() > 0)//keep reading stream from modem until all characters collected
{
delay(DelayBeforeDoingReadFromModemUInt); //emperically found that waiting more than 0.5ms is needed for reliable response
character = Serial1.read(); //read back one character of the response from SBDWB command
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" character in hex is "));
Serial.println( character, HEX);
#endif
BufferText.concat(character); //build up BufferText string
}//have recorded entire string in buffer
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" entire BufferText string is "));
Serial.println( BufferText );
#endif
if (BufferText.indexOf("+SBDMTA:0") >=0)return RingIndicationDisabledUInt;
if (BufferText.indexOf("+SBDMTA:1") >=0)return RingIndicationErrononiouslyEnabledUInt;
return RingIndicationUnexpectedResponseUInt;
}
unsigned int NetworkStatus()
{//spontaineous reporting is turned on, read, and then turned off.
/***************************************************
R e t u r n c o d e d e s c
----------------------------------------------------
SignalStrengthTooLowUInt
NetworkAvailableWithAcceptableSignalStrengthUInt
NetworkStatusUnexpectedResponseUInt
TimeOutNetworkStatusUInt
no valid response within TimeLimitULong ms
NetworkNotAvailableUInt
****************************************************/
//See pg 35
TimeLimitULong = 20000; //in milliseconds
BufferText="";//initialize string to zero length
byte SignalStrengthByte = 0;
StartTimeULong = millis(); //prevents subroutine hanging up if it doesn't get a response from the modem
//We will look for the expected string until time is up and will tolerate the buffer temporarily going empty.
TextIndexByte = 0;//used to index through string
Serial1.print(F("AT+CIER=1,1,1\r"));//enable spontaneous event reporting, signal quality and network status
Serial.println(F("AT+CIER=1,1,1\r"));
delay(DelayAfterDoingPrintToModemUInt);
while(1)
{
while(Serial1.available() < 1)
{//look for bytes in buffer until either we find some or run out of time
if((millis() - StartTimeULong) > TimeLimitULong)
{//ran out of time
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
Serial1.print(F("AT+CIER=0,0,0\r"));//disable spontaneous event reporting, signal quality and network status
Serial.println(F("AT+CIER=0,0,0\r"));
delay(DelayAfterDoingPrintToModemUInt);
return TimeOutNetworkStatusUInt;//no response Received within TimeLimitULong ms
}
}
//at least one byte has arrived
while(Serial1.available() > 0)//keep reading stream from modem until all characters collected
{
delay(DelayBeforeDoingReadFromModemUInt); //emperically found that waiting more than 0.5ms is needed for reliable response
character = Serial1.read(); //read back one character of the response from SBDWB command
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" character in hex is "));
Serial.println( character, HEX);
#endif
BufferText.concat(character); //build up BufferText string
}//have recorded entire string in buffer
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" entire BufferText string is "));
Serial.println( BufferText );
#endif
if (BufferText.indexOf("+CIEV:1,0") >=0)
{//this means network service currently unavailable
Serial1.print(F("AT+CIER=0,0,0\r"));//disable spontaneous event reporting, signal quality and network status
Serial.println(F("AT+CIER=0,0,0\r"));
delay(DelayAfterDoingPrintToModemUInt);
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
return NetworkNotAvailableUInt;
}
if (BufferText.indexOf("+CIEV:1,1") >=0)
{//this means network service is available so look for signal strength
TextIndexByte = BufferText.indexOf("+CIEV:0,") +8; //following ASCII character is signal strength
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" TextIndexByte = "));
Serial.println( TextIndexByte );
Serial.print(F(" BufferText[TextIndexByte] = "));
Serial.println( BufferText[TextIndexByte] );
#endif
Serial1.print(F("AT+CIER=0,0,0\r"));//disable spontaneous event reporting, signal quality and network status
Serial.println(F("AT+CIER=0,0,0\r"));
delay(DelayAfterDoingPrintToModemUInt);
SignalStrengthByte = BufferText[TextIndexByte] - 0x30;//convert ASCII to number
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" Signal Strength = "));
Serial.println( SignalStrengthByte );
#endif
if(SignalStrengthByte < 3)
{
Serial.println(__LINE__);
return SignalStrengthTooLowUInt;//we will only transmit if we have at least 3 bars out of 5
}else{
return NetworkAvailableWithAcceptableSignalStrengthUInt;
}
#ifdef DiagPrint1
Serial.println(__LINE__);
Serial.print(F(" SignalStrengthByte = "));
Serial.println( SignalStrengthByte );
#endif
}else{ //didn't find +CIEV:1 network available response so
Serial1.print(F("AT+CIER=0,0,0\r"));//disable spontaneous event reporting, signal quality and network status
#ifdef DiagPrint1
Serial.println(F("AT+CIER=0,0,0\r"));
#endif
delay(DelayAfterDoingPrintToModemUInt);
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
return NetworkStatusUnexpectedResponseUInt;
}
}
}
unsigned int GetMT_Message()
{
//IridiumReceiveDataByte[45] will be populated after returning
/***************************************************
R e t u r n c o d e d e s c
----------------------------------------------------
MT_MessageIsNullUInt
MT_MessageRetrievedCorrectlyUInt
MT_MessageUnexpectedResponseUInt
MT_MessageTimeOutUInt no response within TimeLimitULong ms
MT_MessageFailedCheckSumUInt
MT_MessageTooLongUInt
****************************************************/
//GetResponseFromModemAfterSendingMessage() will define MTlengthByte which can be used to read the proper number of bytes. However, I will use the message length included in the MT message. See page 90 for +SBDRB - Short Burst Data: Read Binary Data from ISU.
TimeLimitULong = 20000; //in milliseconds
BufferText="";//initialize string to zero length
StartTimeULong = millis(); //prevents subroutine hanging up if it doesn't get a response from the modem
unsigned int LocalCheckSumUInt = 0;
unsigned int ReceivedCheckSumUInt = 0;
if (MTlengthByte <1)return MT_MessageIsNullUInt;
Serial1.print(F("AT+SBDRB\r"));//tells modem to transfer a single binary SBD message to the UART. There is no response from the modem beyond sending the SBD message.
Serial.println(F("AT+SBDRB\r"));
delay(DelayAfterDoingPrintToModemUInt);
ReturnCodeUInt = ToldModemToGiveUsTheReceivedMessageUInt;
while(Serial1.available() < 1)
{
if((millis() - StartTimeULong) > TimeLimitULong)
{
#ifdef DiagPrint1
Serial.print(F("line number "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
return MT_MessageTimeOutUInt;//no response Received within TimeLimitULong ms
}
}
//if buffer has bytes before time runs out, collect them
//format is {2-byte message length} + {binary SBD message} + {2-byte checksum}
while(Serial1.available() > 0)//keep reading stream from modem until all characters collected
{
delay(DelayBeforeDoingReadFromModemUInt); //emperically found that waiting more than 0.5ms is needed for reliable response
character = Serial1.read(); //read back one character of the MT message
#ifdef looparoundTest
Serial.println(__LINE__);
Serial.print(F(" char in hex: "));
Serial.println( character, HEX);
#endif
if ((character != '\r') && (character != '\n'))BufferText.concat(character); //build up BufferText string but exclude <cr>, and <lf>
}//have recorded entire string in buffer. It should contain {2-byte message length} + {binary SBD message} + {2-byte checksum}. {binary SBD message} seems to be just the 45 byte array.
#ifdef looparoundTest
Serial.print(F("line # "));
Serial.println(__LINE__);
Serial.print(F(" BufferText[0] in hex = "));
Serial.println( BufferText[0], HEX );
Serial.print(F(" BufferText[1] in hex = "));
Serial.println( BufferText[1], HEX );
#endif
//the first two bytes are the 2-byte message length
unsigned int MessageLengthUInt = word(BufferText[0],BufferText[1]); //build decimal value of message length from the first two hex bytes.
#ifdef looparoundTest
Serial.print(F("line # "));
Serial.println(__LINE__);
Serial.print(F(" MessageLengthUInt = "));
Serial.println( MessageLengthUInt );
#endif
if (MessageLengthUInt > 50)return MT_MessageTooLongUInt;
//MT Message length is within limits. This means that the array can't be larger than 45 bytes because we have 5 bytes of overhead.
LocalCheckSumUInt = 0;//unsigned integer is 2 bytes
for (i=2; i < MessageLengthUInt+2;i++)//checksum is over the array. Skip over first two bytes which are the message length.
{
LocalCheckSumUInt = LocalCheckSumUInt + BufferText[i];
#ifdef looparoundTest
Serial.print(F("line # "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" LocalCheckSumUInt = "));
Serial.println( LocalCheckSumUInt );
#endif
}
#ifdef looparoundTest
Serial.print(F("line # "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print( "Iridium Received Data. Message Length is ");//diag
Serial.println(MessageLengthUInt);
#endif
for (i = 2; i < MessageLengthUInt+2; i++)//normally this means going from 2 to 47 but could be fewer if user sent fewer bytes in MT message. Skip over first 2 bytes of BufferText because they are the message length.
{
IndexByte = i-2;//0 to typically 44
IridiumReceiveDataByte[IndexByte] = BufferText[i];
#ifdef looparoundTest
Serial.print( IridiumReceiveDataByte[IndexByte] );
Serial.print(",");
#endif
//IridiumReceiveDataByte[] can also be filled elseware during MPM looparound
}
/*the following code has been removed because i now do it just before I send IridiumReceiveDataFirstBlock[] and IridiumReceiveDataSecondBlock[] back to FDR
for(i=0;i<32;i++)//32 bytes
{
IridiumReceiveDataFirstBlock[i]=IridiumReceiveDataByte[i];//fill first data block in preparation for sending it back to FDR. Cannot send back more than 32 bytes at a time.
#ifdef looparoundTest
Serial.print(F("line # "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" IridiumReceiveDataFirstBlock[i]= "));
Serial.println( IridiumReceiveDataFirstBlock[i] );
#endif
}
for(i=32;i<45;i++)//13 more bytes. I am assuming array is always 45 bytes. If it is not, the rest will be junk but should have valid addresses.
{
IndexByte = i-32;//gives me 0 to 13
IridiumReceiveDataSecondBlock[IndexByte]=IridiumReceiveDataByte[i];//fill second data block in preparation for sending it back to FDR.
#ifdef looparoundTest
Serial.print(F("line # "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F(" IridiumReceiveDataSecondBlock[IndexByte] = "));
Serial.println( IridiumReceiveDataSecondBlock[IndexByte] );
#endif
}
*/
/************************************************************
Layout of MT buffer contents:
index contents
0,1 message length
2-46 array bytes 0-44
47 MSB of checksum (45+2)
48 LSM of checksum (45+3)
*************************************************************/
unsigned int CheckSumUpperBytePointerUInt = MessageLengthUInt+2;
unsigned int CheckSumLowerBytePointerUInt = MessageLengthUInt+3;
//Verify message wasn't corrupted.
ReceivedCheckSumUInt = word(BufferText[CheckSumUpperBytePointerUInt],BufferText[CheckSumLowerBytePointerUInt]);
#ifdef looparoundTest
Serial.println(__LINE__);
Serial.print(F(" ReceivedCheckSumUInt = "));
Serial.println( ReceivedCheckSumUInt );
Serial.println(__LINE__);
Serial.print(F(" LocalCheckSumUInt = "));
Serial.println( LocalCheckSumUInt );
#endif
if(LocalCheckSumUInt != ReceivedCheckSumUInt)return MT_MessageFailedCheckSumUInt;
//IridiumTransmitDataByte was not corrupted so can return sucessfully
return MT_MessageRetrievedCorrectlyUInt;
}
void LoopAround()//I could not loop around through modem to work
{
if(modemLooparoundEnabledQbool)//looparound through modem
{
//This is a user invoked diagnostic tool that will take what is in the MO buffer and put it into the MT buffer. See page 95.
Serial1.print(F("AT+SBDTC\r"));//tells modem to transfer the contents of the MO buffer to the MT buffer
delay(DelayAfterDoingPrintToModemUInt);
LocalReturnCodeUInt = TestForOK();//It will wait up to 1 second for "OK"
if (LocalReturnCodeUInt != OK_FoundUInt)
{
Serial.print(F("Loop around request to modem failed"));
}
}
if(MPMonlyLooparoundEnabledQbool)//looparound only through MPM
{
#ifdef looparoundTest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
Serial.println(F("IridiumReceiveDataByte[]:"));
#endif
for(byte index = 0; index < 45;index++)
{
IridiumReceiveDataByte[index] = IridiumTransmitDataByte[index];
#ifdef looparoundTest
Serial.print(IridiumReceiveDataByte[index]);
Serial.print(F(" "));
#endif
}
}
}//end of LoopAround()
void IridiumModemSetup()
{
/***************************************************************************
Possible Return Codes
****************************************************************************
ModemFailedAtSetupUInt
ModemReadyForUseUInt
***************************************************************************/
byte i=0;//used in data counter
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
ReturnCodeUInt = ModemSetupProceedingUInt;
SetToTextualResponses(); //all subroutines depend on seeing Textual Responses responses. It is the default but this is defensive
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis());
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
#endif
ReturnCodeUInt = IdentifyModem();
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(" ReturnCodeUInt is "));
Serial.println( ReturnCodeUInt );
#endif
//failures are returned; success advances us to next step in setup
if(ReturnCodeUInt != CorrectModemConnectedUInt)
{
ReturnCodeUInt = WrongModemConnectedCheckSerialNumberUInt;
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(" ReturnCodeUInt is "));
Serial.println( ReturnCodeUInt );
#endif
return;
}
ReturnCodeUInt = SetModemDefaults();
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
if(ReturnCodeUInt != ModemDefaultsSetUInt)
{
ReturnCodeUInt = ModemFailedAtSetupUInt;
return;
}
ReturnCodeUInt = ModemReadyForUseUInt;
return;
}//end of IridiumModemSetup()
void PerformTransmitAndReceiveSequence()
{
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
/************************************************
input is IridiumTransmitDataByte[45]. It was initialized to a decending count starting at 255 and ending at 211. I test for this sequence and if I see it, will return an error code of DuplicateTransmitOfDataAttemptedUInt. After sending valid data, IridiumTransmitDataByte[45] is initialized to this data.
output is return code and, if all ok, IridiumReceiveDataByte[45]
We only return upon success or failure. FDR can read ReturnCodeUInt at any time for status.
If Modem loop around has been enabled via the modemLooparoundEnabledQbool flag, I echo data via the modem regardless of sateline state and return success as return code. However, I could not get it to work so will loop around only as far as MPM with the MPMonlyLooparoundEnabledQbool flag.
*************************************************/
delay(20);//this delay provides a time window for interrupts that receive data from FDR to work. These interrupts deliver receive data necessary for PerformTransmitAndReceiveSequence() to work.
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.println(__LINE__);
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
#endif
//check for duplicate data being transmitted if not in looparound
byte arrayIndexOneByte = 0;
byte arrayIndexTwoByte = 0;
bool identicalArrayQbool = false;
//we expect a valid IridiumTransmitDataByte[] coming in
if((!modemLooparoundEnabledQbool) && (!MPMonlyLooparoundEnabledQbool))//neither loop around active
{
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.println(__LINE__);
#endif
for (arrayIndexOneByte = 0;arrayIndexOneByte < 45;)//check each byte in array compared to previous byte. If all the same, generate errors.
{
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.println(__LINE__);
#endif
if(IridiumTransmitDataByte[arrayIndexOneByte] == PreviousIridiumTransmitDataByte[arrayIndexOneByte])
{
arrayIndexOneByte++;//advance to next value of arrayIndexOneByte and check next element
identicalArrayQbool = true;//identicalArrayQbool is true so far in the comparison
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.println(__LINE__);
#endif
}else
{//one byte is different so update previous array with current array
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.println(__LINE__);
#endif
for (arrayIndexTwoByte = 0; arrayIndexTwoByte < 45;arrayIndexTwoByte++)//since array was successfully transmitted, save it as previous array
{
PreviousIridiumTransmitDataByte[arrayIndexTwoByte] = IridiumTransmitDataByte[arrayIndexTwoByte];//save a copy of what was just transmitted. We use it to prevent sending the same data twice in a row
}
identicalArrayQbool = false;
arrayIndexOneByte = 255;//forces loop to terminate early
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.println(__LINE__);
#endif
}
}//check next byte
//all bytes now checked until we see a difference
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.println(__LINE__);
#endif
if(identicalArrayQbool)//if all elements were equal so set a failure return code.
{
ReturnCodeUInt = DuplicateTransmitOfDataAttemptedUInt;
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.println(__LINE__);
#endif
return;
}
}//array not a duplicate so continue transmit process; I only check when not in looparound
if((!modemLooparoundEnabledQbool) && (!MPMonlyLooparoundEnabledQbool))//Do check of satelite only if not in loop around
{
ReturnCodeUInt = NetworkStatus();//we will only transmit if we have at least 3 bars
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.println(__LINE__);
#endif
}else
{//in looparound so fake sateline is OK
ReturnCodeUInt = NetworkAvailableWithAcceptableSignalStrengthUInt;
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.println(__LINE__);
#endif
}
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.println(__LINE__);
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
#endif
if(ReturnCodeUInt != NetworkAvailableWithAcceptableSignalStrengthUInt)
{
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.println(__LINE__);
#endif
return;
}
//Otherwise, proceed with transmission sequence if MPMonlyLooparoundEnabledQbool is false. That means we can be operational or modem looparound.
if(!MPMonlyLooparoundEnabledQbool)
{
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.println(__LINE__);
#endif
ClearMO_MessageBuffer();//defensive since buffer gets overwritten
GetResponseFromClearMO_MessageBuffer();
if (ReturnCodeUInt != MO_BufferClearedSuccessfullyUInt)
{
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.println(__LINE__);
#endif
return;
}
ClearMT_MessageBuffer();//no response from modem at this point
ReturnCodeUInt = GetResponseFromClearMT_MessageBuffer();
if (ReturnCodeUInt != MT_BufferClearedSuccessfullyUInt)
{
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.println(__LINE__);
#endif
return;
}
TellModemWeWillBeSending45BytesPlusOverhead();
GetResponseFromModemAfterSendingMessageSize();//if size of array is within limits, we get READY which translates to MessageSizeAcceptedUInt. If beyond these limits, we get "3" which translates to SBD_MessageSizeTooBigOrTooSmallUInt.
if (ReturnCodeUInt != MessageSizeAcceptedUInt)
{
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.println(__LINE__);
#endif
return;
}
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis());
Serial.println(F("data to modem:"));
for(i=1;i<45;i++)
{
Serial.print(IridiumTransmitDataByte[i]);
Serial.print(",");
}
Serial.println();
#endif
WriteDataToMobileOriginatedBuffer();//data comes from IridiumTransmitDataByte[45]. RockBlock address information plus checksum are added.
#ifdef looparoundTest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis());
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
#endif
ReturnCodeUInt = GetResponseFromModemAfterWritingDataToMobileOriginatedBuffer();
if(ReturnCodeUInt != SBD_MessageSuccessfullyWrittenUInt)
{
#ifdef looparoundTest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
return;
}
}
//data is now in the Mobile Originated Buffer unless I'm doing MPM looparound, and I can either loop it back or transmit it
if(modemLooparoundEnabledQbool || MPMonlyLooparoundEnabledQbool)
{
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.println(__LINE__);
#endif
LoopAround();
/************************************************
if modemLooparoundEnabledQbool true: Contents of MO buffer are moved to MT buffer
if MPMonlyLooparoundEnabledQbool true: IridiumReceiveDataByte[] is set equal to IridiumTransmitDataByte[]
*************************************************/
delay(20);//this delay provides a time window for interrupts that receive data from FDR to work. These interrupts deliver receive data necessary for LoopAround() to work.
#ifdef looparoundTest
Serial.print(__FUNCTION__);
Serial.print(F("(): MPM "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
Serial.print(F("responds from GetMT_Message(): ")); Serial.println(GetMT_Message());//it tries to populates IridiumReceiveDataByte[45] but will report faults. I'l print out the local return code
Serial.println(F("data received from modem (MT buffer) or from MPM looparound: "));
for(i=0;i<45;i++)
{
Serial.print(IridiumReceiveDataByte[i]);
Serial.print(",");
}
Serial.println();
#endif
}else
{//no looparound active
SendMessageInMobileOriginatedBuffer();//initiate transmit and receive exchange with satellite
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis());
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
#endif
}
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis());
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
#endif
if(!MPMonlyLooparoundEnabledQbool)
{//if in normal mode or looparound through modem, do the following
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.println(__LINE__);
#endif
GetResponseFromModemAfterSendingMessage();
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.println(__LINE__);
#endif
}//if MPMonlyLooparoundEnabledQbool true, we do nothing with the modem
/********************************
populated variables are
MOstatusByte
MOMSNUInt
MTstatusByte
MTMSNUInt
MTlengthByte
MTqueuedByte
*********************************/
if((modemLooparoundEnabledQbool)||(MPMonlyLooparoundEnabledQbool))
{
ReturnCodeUInt = TransmitAndReceiveSuccessfulUInt;
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis());
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
#endif
}
//there won't be a modem response because no transmit/receive took place but we will still say transmission was success
if ((ReturnCodeUInt == TransmitAndReceiveSuccessfulUInt)||(ReturnCodeUInt == TransmitAndReceiveSuccessfulPlusReceivePendingUInt))
{//if we did Receive a message, get it
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis());
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
#endif
if(!MPMonlyLooparoundEnabledQbool)
{
ReturnCodeUInt = GetMT_Message(); //it tries to populates IridiumReceiveDataByte[45] but will report faults
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis());
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
#endif
}else
{//if MPMonlyLooparoundEnabledQbool is true, we don't deal with modem but must say Transmit and Receive Successful so looped back data is sent back to FDR
ReturnCodeUInt = TransmitAndReceiveSuccessfulUInt;
}//if we are doing MPM loop around, force return code to so no data received from ground
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis());
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
#endif
}
//translating from internal jagon to external simple terminology.
if(ReturnCodeUInt == MT_MessageIsNullUInt)
{
ReturnCodeUInt = TransmitSuccessfulAndNoReceiveUInt;
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis());
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
#endif
}
if (ReturnCodeUInt == MT_MessageRetrievedCorrectlyUInt)
{
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis());
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
#endif
if(MTqueuedByte == 0)ReturnCodeUInt = TransmitAndReceiveSuccessfulUInt;
if(MTqueuedByte > 0)ReturnCodeUInt = TransmitAndReceiveSuccessfulPlusReceivePendingUInt;
//I don't expect this case but it appears that data has been left in MT queue. I'm not sure how we get it out with current architecture. I had assumed the data was on the server.
#ifdef DiagPrint1
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis());
Serial.print(F(" ReturnCodeUInt = "));
Serial.println( ReturnCodeUInt );
#endif
}
//all other return codes are faults or loopback so pass them through
return;
}//end of PerformTransmitAndReceiveSequence()
bool IridiumTransmitDataByteArrayIsDuplicate()
{
for (i = 0; i<45; i++)
{
if (IridiumTransmitDataByte[i] != PreviousIridiumTransmitDataByte[i])
{
for (i = 0; i<45; i++)
{
PreviousIridiumTransmitDataByte[i] = IridiumTransmitDataByte[i];
}
return false;
}
}
//all 45 bytes match so array user wants to transmitted matches the array just transmitted. It will not be sent.
return true;
}
void WireWriteUInt(unsigned int AnInteger)
{
byte Command[2];
Command[0] = lowByte(AnInteger);//take apart outgoing return code
Command[1] = highByte(AnInteger);
Wire.write(Command,2);//send FDR return code which is an unsigned integer. Since this is the MPM_, we do not deal with beginTransmission() and endTransmission().
}
void VerifyDisableMT_Alert()
//ask modem to return the ring function state. reference: page 90.
{
FlushUART_Buffer(); //defensive action in case last subroutine did not empty buffer
Serial1.print("AT+SBDMTA?\r");
delay(DelayAfterDoingPrintToModemUInt);
ReturnCodeUInt = VerifyDisableMT_AlertUInt;
}
void FlushUART_Buffer()
{
while(Serial1.available() > 0)
{//keep reading stream from modem until all characters collected
delay(DelayBeforeDoingReadFromModemUInt); //emperically found that waiting more than 0.5ms is needed for reliable response
Serial1.read();
}
delay(DelayAfterDoingPrintToModemUInt); //emperically found that I need to wait before sending anything to UART
//ReturnCodeUInt = FlushedUART_BufferUInt;//not useful status
}
void SetToTextualResponses()
{
Serial1.print("AT V1\r");//switch to Textual Responses
delay(DelayAfterDoingPrintToModemUInt);
FlushUART_Buffer();//dump response
}
void SetToNumericResponses()
{
Serial1.print("AT V0\r");//switch to Numeric
delay(DelayAfterDoingPrintToModemUInt);
FlushUART_Buffer();//dump response
}
void InitializeIridiumTransmitDataByteArray()
{
for (i = 0; i < 45; i++)
{
IridiumTransmitDataByte[i] = 255 - i;
}
}
bool CycleActiveQ()
{//if we are still reporting status, we are not done yet.
if ((ReturnCodeUInt >= StatusRangeMinUInt) || (ReturnCodeUInt >= StatusRangeMaxUInt))return true;
return false;//if not reporting status, then we either failed or succeeded so are done.
}
void FlushWireBuffer()
{//ensure nothing left in buffer
while (Wire.available() > 0)
{
Wire.read();
}
}
void PingModem()
{
/****************************************************
R E T U R N C O D E S
*****************************************************
PingThroughMPM_AndModemSuccessUInt
This is a low level function so have hidden its internal return codes from FDR
*****************************************************/
#ifdef nearfasrtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
ReturnCodeUInt = PerformingPingUInt;
Serial1.print(F("AT\r"));//modem should respond to "AT" with "OK"
LocalReturnCodeUInt = TestForOK();//It will wait up to 1 second for "OK"
if (LocalReturnCodeUInt == OK_FoundUInt)
{
#ifdef nearfasrtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
ReturnCodeUInt = PingThroughMPM_AndModemSuccessUInt;
}else{
#ifdef nearfasrtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
ReturnCodeUInt = PingToMPM_SuccessButToModemFailedUInt;
}
return;
}
unsigned int GetResponseFromModemAfterWritingDataToMobileOriginatedBuffer()
{
//it handles the response after "READY" which is a response to AT+SBDWB=
/***************************************************
R e t u r n c o d e
----------------------------------------------------
SBD_MessageSuccessfullyWrittenUInt
TimeOutAfterSendingMessageUInt no response within TimeLimitULong ms
SBD_MessageTimeOutByModemUInt timing done by modem
SBD_MessageChecksumWrongUInt
SBD_MessageSizeWrongUInt
UnexpectedResponseAfterSendingMessageSizeUInt
****************************************************/
TimeLimitULong = 100000; //in milliseconds was 20 seconds but for diag changed to 100 seconds
//See pg 95
BufferText="";//initialize string to zero length
StartTimeULong = millis(); //prevents subroutine hanging up if it doesn't get a response from the modem
while (Serial1.available() < 1)
{
if((millis() - StartTimeULong) > TimeLimitULong) return TimeOutAfterSendingMessageUInt;//no response Received within TimeLimitULong ms
}
while(Serial1.available() > 0)//keep reading stream from modem until all characters collected
{
delay(DelayBeforeDoingReadFromModemUInt); //emperically found that waiting more than 0.5ms is needed for reliable response
character = Serial1.read(); //read back one character of the response from SBDWB/READY response
#ifdef charCheck
Serial.println(__LINE__);
Serial.print(F(" character in hex is "));
Serial.println( character, HEX);
#endif
BufferText.concat(character); //build up BufferText string
}//have recorded entire string
#ifdef charCheck
Serial.println(__LINE__);
Serial.print(F(" entire BufferText string is "));
Serial.println( BufferText );
#endif
if (BufferText.indexOf("0") >= 0)return SBD_MessageSuccessfullyWrittenUInt;
if (BufferText.indexOf("1") >= 0)return SBD_MessageTimeOutByModemUInt;
if (BufferText.indexOf("2") >= 0)return SBD_MessageChecksumWrongUInt;
if (BufferText.indexOf("3") >= 0)return SBD_MessageSizeWrongUInt;
return UnexpectedResponseAfterSendingMessageSizeUInt;
}//end of GetResponseFromModemAfterWritingDataTo MobileOriginatedBuffer()
void ReceiveFromFDR_Event()
{//interrupt code
#ifdef interruptDelim
Serial.println();
Serial.println(F("INTRPT BELOW"));
#endif
if (Wire.available() == 2) //two bytes means a command was sent
{
#ifdef looparoundTest
Serial.println();//since this is interrupt, add line so I don't blend into base level prints
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
#endif
byte CommandLowByte = Wire.read(); //this assumes low byte arrives first
byte CommandHighByte = Wire.read();
NewFDR_CommandUInt = word(CommandHighByte,CommandLowByte);//assemble incoming command
#ifdef looparoundTest
Serial.println();//since this is interrupt, add line so I don't blend into base level prints
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.println(millis() - StartForTimeStampULong);
Serial.print(F("NewFDR_CommandUInt = "));
Serial.println( NewFDR_CommandUInt );
#endif
if(NewFDR_CommandUInt == SendBackFirstBlockOfReceivedArrayUInt)
{
SendFirstBlockOfReceivedArrayBool = true;//this command is kept locally. Flag cleared when first block sent back to FDR.
firstBlockNotFilledBool = true;
#ifdef nearfasrtest
Serial.println();//since this is interrupt, add line so I don't blend into base level prints
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
#ifdef interruptDelim
Serial.println();
Serial.println(F("INTRPT ABOVE"));
#endif
return;
}
if(NewFDR_CommandUInt == SendBackSecondBlockOfReceivedArrayUInt)
{
SendSecondBlockOfReceivedArrayBool = true;//this command is kept locally. Flag cleared when secnd block sent back to FDR.
secondBlockNotFilledBool = true;
#ifdef nearfasrtest
Serial.println();//since this is interrupt, add line so I don't blend into base level prints
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
#ifdef interruptDelim
Serial.println();
Serial.println(F("INTRPT ABOVE"));
#endif
return;
}
//if not a send received array command,
FDR_CommandUInt = NewFDR_CommandUInt;
#ifdef interruptDelim
Serial.println();
Serial.println(F("INTRPT ABOVE"));
#endif
return;
}
byte i = 0;
if (Wire.available() == 32) //array to be transmitted is sent as a block of 32 bytes and then a block of 13 for a total of 45 bytes
{
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
for (i = 0; i < 32; i++)
{
IridiumTransmitDataByte[i] = Wire.read();
#ifdef modemNGtest
Serial.print(IridiumTransmitDataByte[i]);
Serial.print(" ");
#endif
}
#ifdef interruptDelim
Serial.println();
Serial.println(F("INTRPT ABOVE"));
#endif
return;
}
if (Wire.available() == 13) //array to be transmitted is sent as a block of 32 bytes and the a block of 13 for a total of 45 bytes
{
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
for (i = 32; i < 45; i++)
{
IridiumTransmitDataByte[i] = Wire.read();
#ifdef modemNGtest
Serial.print(IridiumTransmitDataByte[i]);
Serial.print(" ");
#endif
}
//suspect
#ifdef interruptDelim
Serial.println();
Serial.println(F("INTRPT ABOVE "));
#endif
return;
}
}//end of ReceiveFromFDR_Event()
void FDR_RequestsData()
{//interrupt code
#ifdef interruptDelim
Serial.println();
Serial.println(F("INTRPT BELOW"));
#endif
#ifdef modemNGtest
Serial.println();//since this is interrupt, add line so I don't blend into base level prints
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
if(SendFirstBlockOfReceivedArrayBool)
{
#ifdef modemNGtest
Serial.println();//since this is interrupt, add line so I don't blend into base level prints
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
for(byte index=0;index<32;index++)//get newest copy of data
{
IridiumReceiveDataFirstBlock[index] = IridiumReceiveDataByte[index];
}
Wire.write(IridiumReceiveDataFirstBlock,32);//array is 45 bytes but we can only send 32 at a time.
SendFirstBlockOfReceivedArrayBool = false;
ReturnCodeUInt = idleUInt ;//we have completed the task so go to idle state
#ifdef interruptDelim
Serial.println();
Serial.println(F("INTRPT ABOVE"));
#endif
return;
}
if(SendSecondBlockOfReceivedArrayBool)
{
#ifdef modemNGtest
Serial.println();//since this is interrupt, add line so I don't blend into base level prints
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
for(byte index=0;index<13;index++)//get newest copy of data
{
IridiumReceiveDataSecondBlock[index] = IridiumReceiveDataByte[index+32];
}
Wire.write(IridiumReceiveDataSecondBlock,13);//send second block of data
SendSecondBlockOfReceivedArrayBool = false;
delay(20);//give hardware time to work
ReturnCodeUInt = idleUInt ;//we have completed the task so go to idle state
#ifdef interruptDelim
Serial.println();
Serial.println(F("INTRPT ABOVE"));
#endif
return;
}
//otherwise, return current Return Code
WireWriteUInt(ReturnCodeUInt);
#ifdef modemNGtest
Serial.println();//since this is interrupt, add line so I don't blend into base level prints
Serial.print(__FUNCTION__);
Serial.print(F("(): "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
#endif
if(FDR_CommandUInt == NoActiveFDR_CommandUInt)ReturnCodeUInt = idleUInt;//we have completed the task so go to idle state after reporting last active state to FDR
#ifdef interruptDelim
Serial.println();
Serial.println(F("INTRPT ABOVE"));
#endif
}//end of interrupt code FDR_RequestsData()
void getModemSerialNumbers() {
/**************************************************
Input is nearFarmodemSNbyte where the two higher bits are the near User RB serial number and the two lower bits are the far URBsn.
Outputs are the IMEI of the near modem (IMEIstring) and the far RockBLOCK sn (farRBsnULong) which is part of the transmitted array. **************************************************/
/** Provision codes as of 03/14/2024
* 0b0000 aka 0 = 300234066438070 and 13301
* 0b0101 aka 5 = 300534065390120 and 218642
* 0b1010 aka 10 = 300234066436090 and 13298
* 0b1111 aka 15 = 300534065396130 and 218641
*/
byte nearURBsnByte = (0b1100 & nearFarmodemSNbyte) >> 2; //mask off the two top bits and then shift them down two places.
/*
Serial.print("nearFarmodemSNbyte :: "); Serial.println(nearFarmodemSNbyte);
Serial.print("nearFarmodemSNbyte & 0b1100 :: "); Serial.println((0b1100 & nearFarmodemSNbyte));
Serial.print("(nearFarmodemSNbyte & 0b1100) >> 2 :: "); Serial.println((0b1100 & nearFarmodemSNbyte) >> 2);
Serial.print("nearURBsnByte :: "); Serial.println(nearURBsnByte);
*/
byte farUSRsnByte = 0b11 & nearFarmodemSNbyte; //mask off upper two bits to leave lower two bits which are the far User's modem s
/*
Serial.print("0b11 & nearFarmodemSNbyte :: "); Serial.println(0b11 & nearFarmodemSNbyte);
Serial.print("farUSRsnByte :: "); Serial.println(farUSRsnByte);
*/
IMEIstring = nearIMEIstring[nearURBsnByte]; //select the IMEI for the near modem
farRBsnULong = RBsnULong[farUSRsnByte]; //select the far modem's RockBLOCK serial number written on modem
//Serial.print("IMEIstring :: "); Serial.println(IMEIstring);
//Serial.print("farRBsnULong:: "); Serial.println(farRBsnULong);
#ifdef modemNGtest
Serial.print(__FUNCTION__);
Serial.print(F("() MPM: "));
Serial.print(__LINE__);
Serial.print(F(". TS "));
Serial.print (millis() - StartForTimeStampULong);
Serial.println(F(" ms"));
Serial.print(F("IMEIstring = "));
Serial.println(IMEIstring);
Serial.print(F("nearURBsnByte = "));
Serial.println(nearURBsnByte);
WriteDataToMobileOriginatedBuffer();//should print far RB sn by byte
#endif
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment