Last active
March 21, 2024 03:49
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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 file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/**************************************** | |
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//#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