Thank you, Paul and Mike.
Unfortunately, those did not resolve the overall issue. From follow-on troubleshooting I’ve been able to conclude that it does have to do with the reinitialization of the sensors or the mux (depending on what’s connected – results in the same error).
The code works repeatedly when there are no edits, and there is the reinitialization of GNSS and iridium, I have tried power cycling the mux and that allows the code to run repeatedly. However, as I am power cycling the sensors as well through this process, they are unable to be read.
** note I have an SHT in this code as opposed to the VCNL because I first tried the code recommended by Mike addressing the sensors not being reentrant (which did not fix it either)
Troubleshooting efforts:
-
ran through all applicable examples (1,3,5,9,10) and added MUX w/ 2 SHTC3’s, and they each work for many loop iterations
-
my next step was to get example 14 working (which is very similar to 16 in structure):
-
I added a case “read_SHT” (which requires wire.begin for the mux to work), that had the same code as used in the previous examples
-
it compiled and ran 1 time through, but upon reinitiation crashed with the same mbed os hardfault, I believe it has something to do with the qwiic I2C SCL/SDA; specifically it crashes just after agtWire.begin before the clock function in start_GNSS
-
I began further investigating the “zzz” and “wakeup” cases to see how things were turned off and restarted, which lead me to the [apollo3 issues
-
I tried manually re-enabling qwiic I2C through IOM4, such is in the example of the link, both in the wake-up case or in my read_sht case; neither of which worked and resulted in the same error just before the clock
The following is my code:
I have removed everything past diagnostics for brevity; I also have commented out the text transmission and changed the interval to 2 minutes for faster iteration as I try to debug; I have add “chase pixa” too all edits, and commented out edits were unsuccessful attempts
// Artemis Tracker pin definitions
#define spiCS1 4 // D4 can be used as an SPI chip select or as a general purpose IO pin
#define geofencePin 10 // Input for the ZOE-M8Q's PIO14 (geofence) pin
#define busVoltagePin 13 // Bus voltage divided by 3 (Analog in)
#define iridiumSleep 17 // Iridium 9603N ON/OFF (sleep) pin: pull high to enable the 9603N
#define iridiumNA 18 // Input for the Iridium 9603N Network Available
#define LED 19 // White LED
#define iridiumPwrEN 22 // ADM4210 ON: pull high to enable power for the Iridium 9603N
#define gnssEN 26 // GNSS Enable: pull low to enable power for the GNSS (via Q2)
#define gnssBckpBatChgEN 44 // GNSS backup battery charge enable; when set as INPUT = disabled, when OUTPUT+LOW = charging.
#define superCapChgEN 27 // LTC3225 super capacitor charger: pull high to enable the super capacitor charger
#define superCapPGOOD 28 // Input for the LTC3225 super capacitor charger PGOOD signal
#define busVoltageMonEN 34 // Bus voltage monitor enable: pull high to enable bus voltage monitoring (via Q4 and Q3)
#define spiCS2 35 // D35 can be used as an SPI chip select or as a general purpose IO pin
#define iridiumRI 41 // Input for the Iridium 9603N Ring Indicator
// Make sure you do not have gnssEN and iridiumPwrEN enabled at the same time!
// If you do, bad things might happen to the AS179 RF switch!
//#define noLED // Uncomment this line to disable the White LED
//#define noTX // Uncomment this line to disable the Iridium SBD transmit if you want to test the code without using message credits
// We use Serial1 to communicate with the Iridium modem. Serial1 on the ATP uses pin 24 for TX and 25 for RX. AGT uses the same pins.
#include <IridiumSBD.h> //http://librarymanager/All#IridiumSBDI2C
#define DIAGNOSTICS false // Change this to true to see IridiumSBD diagnostics
// Declare the IridiumSBD object (including the sleep (ON/OFF) and Ring Indicator pins)
IridiumSBD modem(Serial1, iridiumSleep, iridiumRI);
#include <Wire.h> // Needed for I2C
const byte PIN_AGTWIRE_SCL = 8;
const byte PIN_AGTWIRE_SDA = 9;
TwoWire agtWire(PIN_AGTWIRE_SDA, PIN_AGTWIRE_SCL); //Create an I2C port using pads 8 (SCL) and 9 (SDA)
#include "SparkFun_u-blox_GNSS_Arduino_Library.h" //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
#include <SparkFun_PHT_MS8607_Arduino_Library.h> //http://librarymanager/All#SparkFun_MS8607
MS8607 barometricSensor; //Create an instance of the MS8607 object
#include "RTC.h" //Include RTC library included with the Arduino_Apollo3 core
// Define how often messages are sent in SECONDS
// This is the _quickest_ messages will be sent. Could be much slower than this depending on:
// capacitor charge time; gnss fix time; Iridium timeout; etc.
// Chase Pixa, changing text interval
//unsigned long INTERVAL = 15 * 60; // 15 minutes
unsigned long INTERVAL = 2 * 60; // 2 minutes
//#define ISBD_DEFAULT_SENDRECEIVE_TIME 60 // (second) from iridiumSBD.h
// Chase Pixa
#include <Wire.h> //mux
#include "SparkFun_SHTC3.h" // Click here to get the library: http://librarymanager/All#SparkFun_SHTC3
//SHTC3 mySHTC3; // Declare an instance of the SHTC3 class
#define NUMBER_OF_SENSORS 2
SHTC3 mySHTC3[NUMBER_OF_SENSORS]; // Declare an array of instances of the SHTC3 class, one per mux port
// Use this to keep a count of the second alarms from the rtc
volatile unsigned long secondsCount = 0;
// This flag indicates an interval alarm has occurred
volatile bool intervalAlarm = false;
// iterationCounter is incremented each time a transmission is attempted.
// It helps keep track of whether messages are being sent successfully.
// It also indicates if the tracker has been reset (the count will go back to zero).
long iterationCounter = 0;
// More global variables
float agtVbat = 5.0; // Battery voltage
float agtLatitude = 0.0; // Latitude in degrees
float agtLongitude = 0.0; // Longitude in degrees
long agtAltitude = 0; // Altitude above Median Seal Level in m
float agtSpeed = 0.0; // Ground speed in m/s
byte agtSatellites = 0; // Number of satellites (SVs) used in the solution
long agtCourse = 0; // Course (heading) in degrees
int agtPDOP = 0; // Positional Dilution of Precision in m
int agtYear = 1970; // GNSS Year
byte agtMonth = 1; // GNSS month
byte agtDay = 1; // GNSS day
byte agtHour = 0; // GNSS hours
byte agtMinute = 0; // GNSS minutes
byte agtSecond = 0; // GNSS seconds
int agtMilliseconds = 0; // GNSS milliseconds
float agtPascals = 0.0; // Atmospheric pressure in Pascals
float agtTempC = 0.0; // Temperature in Celcius
byte agtFixType = 0; // GNSS fix type: 0=No fix, 1=Dead reckoning, 2=2D, 3=3D, 4=GNSS+Dead reckoning
bool agtPGOOD = false; // Flag to indicate if LTC3225 PGOOD is HIGH
int agtErr; // Error value returned by IridiumSBD.begin
#define VBAT_LOW 2.8 // Minimum voltage for LTC3225
// Timeout after this many _minutes_ when waiting for a 3D GNSS fix
// (UL = unsigned long)
#define GNSS_timeout 5UL
// Timeout after this many _minutes_ when waiting for the super capacitors to charge
// 1 min should be OK for 1F capacitors at 150mA.
// Charging 10F capacitors at 60mA can take a long time! Could be as much as 10 mins.
#define CHG_timeout 2UL
// Top up the super capacitors for this many _seconds_.
// 10 seconds should be OK for 1F capacitors at 150mA.
// Increase the value for 10F capacitors.
#define TOPUP_timeout 10UL
// Loop Steps - these are used by the switch/case in the main loop
// This structure makes it easy to go from any of the steps directly to zzz when (e.g.) the batteries are low
typedef enum {
loop_init = 0, // Send the welcome message, check the battery voltage
start_GNSS, // Enable the ZOE-M8Q, check the battery voltage
read_GNSS, // Wait for up to GNSS_timeout minutes for a valid 3D fix, check the battery voltage
read_SHT, // Read the SHTC3 sensors //Chase Pixa
read_pressure, // Read the pressure and temperature from the MS8607
start_LTC3225, // Enable the LTC3225 super capacitor charger and wait for up to CHG_timeout minutes for PGOOD to go high
wait_LTC3225, // Wait TOPUP_timeout seconds to make sure the capacitors are fully charged
start_9603, // Power on the 9603N, send the message, check the battery voltage
zzz, // Turn everything off and put the processor into deep sleep
wakeUp // Wake from deep sleep, restore the processor clock speed
} loop_steps;
loop_steps loop_step = loop_init; // Make sure loop_step is set to loop_init
// RTC alarm Interrupt Service Routine
// Clear the interrupt flag and increment seconds_count
// If INTERVAL has been reached, set the interval_alarm flag and reset seconds_count
// (Always keep ISRs as short as possible, don't do anything clever in them,
// and always use volatile variables if the main loop needs to access them too.)
extern "C" void am_rtc_isr(void) {
// Clear the RTC alarm interrupt
rtc.clearInterrupt();
// Increment seconds_count
secondsCount = secondsCount + 1;
// Check if intervalAlarm should be set
if (secondsCount >= INTERVAL) {
intervalAlarm = true;
secondsCount = 0;
}
}
// Get the battery (bus) voltage
// Enable the bus voltage monitor
// Read the bus voltage and store it in agtVbat
// Disable the bus voltage monitor to save power
// Converts the analogread into Volts, compensating for
// the voltage divider (/3) and the Apollo voltage reference (2.0V)
// Include a correction factor of 1.09 to correct for the divider impedance
void getVbat() {
digitalWrite(busVoltageMonEN, HIGH); // Enable the bus voltage monitor
//analogReadResolution(14); //Set resolution to 14 bit
delay(1); // Let the voltage settle
agtVbat = ((float)analogRead(busVoltagePin)) * 3.0 * 1.09 * 2.0 / 16384.0;
digitalWrite(busVoltageMonEN, LOW); // Disable the bus voltage monitor
}
// IridiumSBD Callback - this code is called while the 9603N is trying to transmit
bool ISBDCallback() {
#ifndef noLED
// Flash the LED at 4 Hz
if ((millis() / 250) % 2 == 1) {
digitalWrite(LED, HIGH);
} else {
digitalWrite(LED, LOW);
}
#endif
// Check the battery voltage now we are drawing current for the 9603
// If voltage is low, stop Iridium send
getVbat(); // Read the battery (bus) voltage
if (agtVbat < VBAT_LOW) {
Serial.print(F("*** LOW VOLTAGE (ISBDCallback) "));
Serial.print(agtVbat, 2);
Serial.println(F("V ***"));
return false; // Returning false causes IridiumSBD to terminate
} else {
return true;
}
}
void gnssON(void) // Enable power for the GNSS
{
am_hal_gpio_pincfg_t pinCfg = g_AM_HAL_GPIO_OUTPUT; // Begin by making the gnssEN pin an open-drain output
pinCfg.eGPOutcfg = AM_HAL_GPIO_PIN_OUTCFG_OPENDRAIN;
pin_config(PinName(gnssEN), pinCfg);
delay(1);
digitalWrite(gnssEN, LOW); // Enable GNSS power (HIGH = disable; LOW = enable)
}
void gnssOFF(void) // Disable power for the GNSS
{
am_hal_gpio_pincfg_t pinCfg = g_AM_HAL_GPIO_OUTPUT; // Begin by making the gnssEN pin an open-drain output
pinCfg.eGPOutcfg = AM_HAL_GPIO_PIN_OUTCFG_OPENDRAIN;
pin_config(PinName(gnssEN), pinCfg);
delay(1);
digitalWrite(gnssEN, HIGH); // Disable GNSS power (HIGH = disable; LOW = enable)
}
// Overwrite the IridiumSBD beginSerialPort function - a fix for https://github.com/sparkfun/Arduino_Apollo3/issues/423
void IridiumSBD::beginSerialPort() // Start the serial port connected to the satellite modem
{
diagprint(F("custom IridiumSBD::beginSerialPort\r\n"));
// Configure the standard ATP pins for UART1 TX and RX - endSerialPort may have disabled the RX pin
am_hal_gpio_pincfg_t pinConfigTx = g_AM_BSP_GPIO_COM_UART_TX;
pinConfigTx.uFuncSel = AM_HAL_PIN_24_UART1TX;
pin_config(D24, pinConfigTx);
am_hal_gpio_pincfg_t pinConfigRx = g_AM_BSP_GPIO_COM_UART_RX;
pinConfigRx.uFuncSel = AM_HAL_PIN_25_UART1RX;
pinConfigRx.ePullup = AM_HAL_GPIO_PIN_PULLUP_WEAK; // Put a weak pull-up on the Rx pin
pin_config(D25, pinConfigRx);
Serial1.begin(19200);
}
// Overwrite the IridiumSBD endSerialPort function - a fix for https://github.com/sparkfun/Arduino_Apollo3/issues/423
void IridiumSBD::endSerialPort() {
diagprint(F("custom IridiumSBD::endSerialPort\r\n"));
// Disable the Serial1 RX pin to avoid the code hang
am_hal_gpio_pinconfig(PinName(D25), g_AM_HAL_GPIO_DISABLE);
}
///////////////////////////////////////////////////////////////////////
// Chase Pixa
/// SHTC3
void printInfo(int num) {
if (mySHTC3[num].lastStatus == SHTC3_Status_Nominal) // You can also assess the status of the last command by checking the ".lastStatus" member of the object
{
Serial.print(num);
Serial.print("RH = ");
Serial.print(mySHTC3[num].toPercent()); // "toPercent" returns the percent humidity as a floating point number
Serial.print("%, T = ");
Serial.print(mySHTC3[num].toDegF()); // "toDegF" and "toDegC" return the temperature as a flaoting point number in deg F and deg C respectively
Serial.println(" deg F");
} else {
Serial.print("Update failed, error: ");
errorDecoder(mySHTC3[num].lastStatus);
Serial.println();
}
}
void errorDecoder(SHTC3_Status_TypeDef message) // The errorDecoder function prints "SHTC3_Status_TypeDef" resultsin a human-friendly way
{
switch (message) {
case SHTC3_Status_Nominal: Serial.print("Nominal"); break;
case SHTC3_Status_Error: Serial.print("Error"); break;
case SHTC3_Status_CRC_Fail: Serial.print("CRC Fail"); break;
default: Serial.print("Unknown return code"); break;
}
}
///////////////////////////////////////////////////////////////////////
void setup() {
// Let's begin by setting up the I/O pins
pinMode(LED, OUTPUT); // Make the LED pin an output
gnssOFF(); // Disable power for the GNSS
pinMode(gnssBckpBatChgEN, INPUT); // GNSS backup batttery charge control; input = disable charging; output+low=charging.
pinMode(geofencePin, INPUT); // Configure the geofence pin as an input
pinMode(iridiumPwrEN, OUTPUT); // Configure the Iridium Power Pin (connected to the ADM4210 ON pin)
digitalWrite(iridiumPwrEN, LOW); // Disable Iridium Power (HIGH = enable; LOW = disable)
pinMode(superCapChgEN, OUTPUT); // Configure the super capacitor charger enable pin (connected to LTC3225 !SHDN)
digitalWrite(superCapChgEN, LOW); // Disable the super capacitor charger (HIGH = enable; LOW = disable)
pinMode(iridiumSleep, OUTPUT); // Iridium 9603N On/Off (Sleep) pin
digitalWrite(iridiumSleep, LOW); // Put the Iridium 9603N to sleep (HIGH = on; LOW = off/sleep)
pinMode(iridiumRI, INPUT); // Configure the Iridium Ring Indicator as an input
pinMode(iridiumNA, INPUT); // Configure the Iridium Network Available as an input
pinMode(superCapPGOOD, INPUT); // Configure the super capacitor charger PGOOD input
// Make sure the Serial1 RX pin is disabled to prevent the power-on glitch on the modem's RX(OUT) pin
// causing problems with v2.1.0 of the Apollo3 core. Requires v3.0.5 of IridiumSBDi2c.
modem.endSerialPort();
pinMode(busVoltageMonEN, OUTPUT); // Make the Bus Voltage Monitor Enable an output
digitalWrite(busVoltageMonEN, LOW); // Set it low to disable the measurement to save power
analogReadResolution(14); //Set resolution to 14 bit
// Initialise the globals
iterationCounter = 0; // Make sure iterationCounter is set to zero (indicating a reset)
loop_step = loop_init; // Make sure loop_step is set to loop_init
secondsCount = 0; // Make sure seconds_count is reset
intervalAlarm = false; // Make sure the interval alarm flag is clear
// Set up the RTC for 1 second interrupts
/*
0: Alarm interrupt disabled
1: Alarm match every year (hundredths, seconds, minutes, hour, day, month)
2: Alarm match every month (hundredths, seconds, minutes, hours, day)
3: Alarm match every week (hundredths, seconds, minutes, hours, weekday)
4: Alarm match every day (hundredths, seconds, minute, hours)
5: Alarm match every hour (hundredths, seconds, minutes)
6: Alarm match every minute (hundredths, seconds)
7: Alarm match every second (hundredths)
*/
rtc.setAlarmMode(7); // Set the RTC alarm mode
rtc.attachInterrupt(); // Attach RTC alarm interrupt
}
void loop() {
// loop is one large switch/case that controls the sequencing of the code
switch (loop_step) {
// ************************************************************************************************
// Initialise things
case loop_init:
// Start the console serial port and send the welcome message
Serial.begin(115200);
delay(1000); // Wait for the user to open the serial monitor (extend this delay if you need more time)
Serial.println();
Serial.println();
Serial.println(F("Artemis Global Tracker"));
Serial.println(F("Example: Simple Tracker"));
Serial.println();
Serial.print(F("Using an INTERVAL of "));
Serial.print(INTERVAL);
Serial.println(F(" seconds"));
// Setup the IridiumSBD
modem.setPowerProfile(IridiumSBD::USB_POWER_PROFILE); // Change power profile to "low current"
// Check battery voltage
// If voltage is low, go to sleep
getVbat(); // Get the battery (bus) voltage
if (agtVbat < VBAT_LOW) {
Serial.print(F("*** LOW VOLTAGE (init) "));
Serial.print(agtVbat, 2);
Serial.println(F(" ***"));
loop_step = zzz; // Go to sleep
} else {
loop_step = start_GNSS; // Move on, start the GNSS
}
break; // End of case loop_init
// ************************************************************************************************
// Power up the GNSS (ZOE-M8Q)
case start_GNSS:
Serial.println(F("Powering up the GNSS..."));
gnssON(); // Enable power for the GNSS
Serial.println(F("GNSS on"));
pinMode(gnssBckpBatChgEN, OUTPUT); // GNSS backup battery charge control; output + low = enable charging
digitalWrite(gnssBckpBatChgEN, LOW);
//pinMode(gnssBckpBatChgEN, INPUT); // GNSS backup battery charge control; input = disable charging
Serial.println(F("pins configured"));
delay(2000); // Give it time to power up
agtWire.begin(); // Set up the I2C pins
Serial.println(F("AGT wire begin"));
delay(2000);
agtWire.setClock(100000); // Use 100kHz for best performance
Serial.println(F("AGT clock set"));
setAGTWirePullups(0); // Remove the pull-ups from the I2C pins (internal to the Artemis) for best performance
Serial.println(F("AGT wire pullups"));
// Check battery voltage now we are drawing current for the GNSS
getVbat(); // Get the battery (bus) voltage
if (agtVbat < VBAT_LOW) {
// If voltage is low, turn off the GNSS and go to sleep
Serial.print(F("*** LOW VOLTAGE (start_GNSS) "));
Serial.print(agtVbat, 2);
Serial.println(F("V ***"));
gnssOFF(); // Disable power for the GNSS
loop_step = zzz; // Go to sleep
}
else { // If the battery voltage is OK
if (myGNSS.begin(agtWire) == false) //Connect to the u-blox module using agtWire port
{
// If we were unable to connect to the ZOE-M8Q:
// Send a warning message
Serial.println(F("*** Ublox GNSS not detected at default I2C address ***"));
// Set the lat, long etc. to default values
agtLatitude = 0.0; // Latitude in degrees
agtLongitude = 0.0; // Longitude in degrees
agtAltitude = 0; // Altitude above Median Seal Level in m
agtSpeed = 0.0; // Ground speed in m/s
agtSatellites = 0; // Number of satellites (SVs) used in the solution
agtCourse = 0; // Course (heading) in degrees
agtPDOP = 0; // Positional Dilution of Precision in m
agtYear = 1970; // GNSS Year
agtMonth = 1; // GNSS month
agtDay = 1; // GNSS day
agtHour = 0; // GNSS hours
agtMinute = 0; // GNSS minutes
agtSecond = 0; // GNSS seconds
agtMilliseconds = 0; // GNSS milliseconds
// Power down the GNSS
gnssOFF(); // Disable power for the GNSS
loop_step = read_SHT; //Chase Pixa // Move on, skip reading the GNSS fix
}
else { // If the GNSS started up OK
//myGNSS.enableDebugging(); // Enable debug messages
myGNSS.setI2COutput(COM_TYPE_UBX); // Limit I2C output to UBX (disable the NMEA noise)
// If we are going to change the dynamic platform model, let's do it here.
// Possible values are:
// PORTABLE, STATIONARY, PEDESTRIAN, AUTOMOTIVE, SEA, AIRBORNE1g, AIRBORNE2g, AIRBORNE4g, WRIST, BIKE
if (myGNSS.setDynamicModel(DYN_MODEL_PORTABLE) == false) {
Serial.println(F("*** Warning: setDynamicModel may have failed ***"));
} else {
Serial.println(F("Dynamic Model updated"));
}
loop_step = read_GNSS; // Move on, read the GNSS fix
}
}
break; // End of case start_GNSS
// ************************************************************************************************
// Read a fix from the ZOE-M8Q
case read_GNSS:
Serial.println(F("Waiting for a 3D GNSS fix..."));
agtFixType = 0; // Clear the fix type
// Look for GNSS signal for up to GNSS_timeout minutes
for (unsigned long tnow = millis(); (agtFixType != 3) && ((millis() - tnow) < (GNSS_timeout * 60UL * 1000UL));) {
agtFixType = myGNSS.getFixType(); // Get the GNSS fix type
// Check battery voltage now we are drawing current for the GNSS
// If voltage is low, stop looking for GNSS and go to sleep
getVbat();
if (agtVbat < VBAT_LOW) {
break; // Exit the for loop now
}
#ifndef noLED
// Flash the LED at 1Hz
if ((millis() / 1000) % 2 == 1) {
digitalWrite(LED, HIGH);
} else {
digitalWrite(LED, LOW);
}
#endif
delay(100); // Don't pound the I2C bus too hard!
}
// If voltage is low then go straight to sleep
if (agtVbat < VBAT_LOW) {
Serial.print(F("*** LOW VOLTAGE (read_GNSS) "));
Serial.print(agtVbat, 2);
Serial.println(F("V ***"));
loop_step = zzz;
}
else if (agtFixType == 3) // Check if we got a valid 3D fix
{
// Get the time and position etc.
// Get the time first to hopefully avoid second roll-over problems
agtMilliseconds = myGNSS.getMillisecond();
agtSecond = myGNSS.getSecond();
agtMinute = myGNSS.getMinute();
agtHour = myGNSS.getHour();
agtDay = myGNSS.getDay();
agtMonth = myGNSS.getMonth();
agtYear = myGNSS.getYear(); // Get the year
agtLatitude = (float)myGNSS.getLatitude() / 10000000.0; // Get the latitude in degrees
agtLongitude = (float)myGNSS.getLongitude() / 10000000.0; // Get the longitude in degrees
agtAltitude = myGNSS.getAltitudeMSL() / 1000; // Get the altitude above Mean Sea Level in m
agtSpeed = (float)myGNSS.getGroundSpeed() / 1000.0; // Get the ground speed in m/s
agtSatellites = myGNSS.getSIV(); // Get the number of satellites used in the fix
agtCourse = myGNSS.getHeading() / 10000000; // Get the heading in degrees
agtPDOP = myGNSS.getPDOP() / 100; // Get the PDOP in m
Serial.println(F("A 3D fix was found!"));
Serial.print(F("Latitude (degrees): "));
Serial.println(agtLatitude, 6);
Serial.print(F("Longitude (degrees): "));
Serial.println(agtLongitude, 6);
Serial.print(F("Altitude (m): "));
Serial.println(agtAltitude);
loop_step = read_SHT; //Chase Pixa // Move on, read the pressure and temperature
}
else {
// We didn't get a 3D fix so
// set the lat, long etc. to default values
agtLatitude = 0.0; // Latitude in degrees
agtLongitude = 0.0; // Longitude in degrees
agtAltitude = 0; // Altitude above Median Seal Level in m
agtSpeed = 0.0; // Ground speed in m/s
agtSatellites = 0; // Number of satellites (SVs) used in the solution
agtCourse = 0; // Course (heading) in degrees
agtPDOP = 0; // Positional Dilution of Precision in m
agtYear = 1970; // GNSS Year
agtMonth = 1; // GNSS month
agtDay = 1; // GNSS day
agtHour = 0; // GNSS hours
agtMinute = 0; // GNSS minutes
agtSecond = 0; // GNSS seconds
agtMilliseconds = 0; // GNSS milliseconds
Serial.println(F("A 3D fix was NOT found!"));
Serial.println(F("Using default values..."));
loop_step = read_SHT; //Chase Pixa // Move on, read the pressure and temperature
}
// Power down the GNSS
Serial.println(F("Powering down the GNSS..."));
gnssOFF(); // Disable power for the GNSS
break; // End of case read_GNSS
// ************************************************************************************************
//Chase Pixa
// read SHT from mux
case read_SHT:
while (Serial == false) {};
Serial.println("Qwiic Mux Shield Read: SHTC3");
delay(1000);
//Re=enable IOM4 manually. Wire.begin does not do it for us
//am_hal_pwrctrl_periph_enable(AM_HAL_PWRCTRL_PERIPH_IOM4);
Wire.begin();
//Disable all eight mux ports initially, then we can enable them one at a time
for (byte x = 0; x <= 7; x++) {
disableMuxPort(x);
Serial.print("Disabled port: ");
Serial.println(x);
}
//Initialize all the sensors
for (byte x = 0; x < NUMBER_OF_SENSORS; x++) {
enableMuxPort(x); //Tell mux to connect to port X
delay(250);
Serial.print("Beginning sensor. Result = "); // Most SHTC3 functions return a variable of the type "SHTC3_Status_TypeDef" to indicate the status of their execution
errorDecoder(mySHTC3[x].begin());
Serial.println("");
//SHTC3_Status_TypeDef result = mySHTC3[x].update(); // Call "update()" to command a measurement, wait for measurement to complete, and update the RH and T members of the object
//printInfo(x); // This function is used to print a nice little line of info to the serial port
disableMuxPort(x);
}
Serial.println("Mux Shield online");
//Wire.end();
//am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_IOM4);
loop_step = read_pressure; // Move on, read the pressure and temperature
break; // End of case read_SHT
// ************************************************************************************************
// Read the pressure and temperature from the MS8607
case read_pressure:
Serial.println(F("Getting the pressure and temperature readings..."));
setAGTWirePullups(1); // MS8607 needs pull-ups
bool barometricSensorOK;
barometricSensorOK = barometricSensor.begin(agtWire); // Begin the PHT sensor
if (barometricSensorOK == false) {
// Send a warning message if we were unable to connect to the MS8607:
Serial.println(F("*** Could not detect the MS8607 sensor. Trying again... ***"));
barometricSensorOK = barometricSensor.begin(agtWire); // Re-begin the PHT sensor
if (barometricSensorOK == false) {
// Send a warning message if we were unable to connect to the MS8607:
Serial.println(F("*** MS8607 sensor not detected at default I2C address ***"));
}
}
if (barometricSensorOK == false) {
// Set the pressure and temperature to default values
agtPascals = 0.0;
agtTempC = 0.0;
} else {
agtTempC = barometricSensor.getTemperature();
agtPascals = barometricSensor.getPressure() * 100.0; // Convert pressure from hPa to Pascals
Serial.print(F("Temperature="));
Serial.print(agtTempC, 1);
Serial.print(F("(C)"));
Serial.print(F(" Pressure="));
Serial.print(agtPascals, 1);
Serial.println(F("(Pa)"));
}
setAGTWirePullups(0); // Disable pull-ups
loop_step = start_LTC3225; // Move on, start the super capacitor charger
break; // End of case read_pressure
// ************************************************************************************************
// Start the LTC3225 supercapacitor charger
case start_LTC3225:
// Enable the supercapacitor charger
Serial.println(F("Enabling the supercapacitor charger..."));
digitalWrite(superCapChgEN, HIGH); // Enable the super capacitor charger
Serial.println(F("Waiting for supercapacitors to charge..."));
delay(2000);
agtPGOOD = false; // Flag to show if PGOOD is HIGH
// Wait for PGOOD to go HIGH for up to CHG_timeout minutes
for (unsigned long tnow = millis(); (!agtPGOOD) && (millis() - tnow < CHG_timeout * 60UL * 1000UL);) {
agtPGOOD = digitalRead(superCapPGOOD); // Read the PGOOD pin
// Check battery voltage now we are drawing current for the LTC3225
// If voltage is low, stop charging and go to sleep
getVbat();
if (agtVbat < VBAT_LOW) {
break;
}
#ifndef noLED
// Flash the LED at 2Hz
if ((millis() / 500) % 2 == 1) {
digitalWrite(LED, HIGH);
} else {
digitalWrite(LED, LOW);
}
#endif
delay(100); // Let's not pound the bus voltage monitor too hard!
}
// If voltage is low then go straight to sleep
if (agtVbat < VBAT_LOW) {
Serial.print(F("*** LOW VOLTAGE (start_LTC3225) "));
Serial.print(agtVbat, 2);
Serial.println(F("V ***"));
loop_step = zzz;
}
else if (agtPGOOD) {
// If the capacitors charged OK
Serial.println(F("Supercapacitors charged!"));
loop_step = wait_LTC3225; // Move on and give the capacitors extra charging time
}
else {
// The super capacitors did not charge so power down and go to sleep
Serial.println(F("*** Supercapacitors failed to charge ***"));
loop_step = zzz;
}
break; // End of case start_LTC3225
// ************************************************************************************************
// Give the super capacitors some extra time to charge
case wait_LTC3225:
Serial.println(F("Giving the supercapacitors extra time to charge..."));
// Wait for TOPUP_timeout seconds, keep checking PGOOD and the battery voltage
for (unsigned long tnow = millis(); (millis() - tnow) < (TOPUP_timeout * 1000UL);) {
// Check battery voltage now we are drawing current for the LTC3225
// If voltage is low, stop charging and go to sleep
getVbat();
if (agtVbat < VBAT_LOW) {
break;
}
#ifndef noLED
// Flash the LED at 2Hz
if ((millis() / 500) % 2 == 1) {
digitalWrite(LED, HIGH);
} else {
digitalWrite(LED, LOW);
}
#endif
delay(100); // Let's not pound the bus voltage monitor too hard!
}
// If voltage is low then go straight to sleep
if (agtVbat < VBAT_LOW) {
Serial.print(F("*** LOW VOLTAGE (wait_LTC3225) "));
Serial.print(agtVbat, 2);
Serial.println(F("V ***"));
loop_step = zzz;
}
else if (agtPGOOD) {
// If the capacitors are still charged OK
Serial.println(F("Supercapacitors charged!"));
loop_step = start_9603; // Move on and start the 9603N
}
else {
// The super capacitors did not charge so power down and go to sleep
Serial.println(F("*** Supercapacitors failed to hold charge in wait_LTC3225 ***"));
loop_step = zzz;
}
break; // End of case wait_LTC3225
// ************************************************************************************************
// Enable the 9603N and attempt to send a message
case start_9603:
// Enable power for the 9603N
Serial.println(F("Enabling 9603N power..."));
digitalWrite(iridiumPwrEN, HIGH); // Enable Iridium Power
delay(1000);
// Relax timing constraints waiting for the supercap to recharge.
modem.setPowerProfile(IridiumSBD::USB_POWER_PROFILE);
// Begin satellite modem operation
// Also begin the serial port connected to the satellite modem via IridiumSBD::beginSerialPort
Serial.println(F("Starting modem..."));
agtErr = modem.begin();
// Check if the modem started correctly
if (agtErr != ISBD_SUCCESS) {
// If the modem failed to start, disable it and go to sleep
Serial.print(F("*** modem.begin failed with error "));
Serial.print(agtErr);
Serial.println(F(" ***"));
loop_step = zzz;
}
else {
// The modem started OK so let's try to send the message
char outBuffer[120]; // Use outBuffer to store the message. Always try to keep message short to avoid wasting credits
// Apollo3 v2.1 does not support printf or sprintf correctly. We need to manually add preceeding zeros
// and convert floats to strings
// Convert the floating point values into strings
char latStr[15]; // latitude string
ftoa(agtLatitude, latStr, 6, 15);
char lonStr[15]; // longitude string
ftoa(agtLongitude, lonStr, 6, 15);
char altStr[15]; // altitude string
ftoa(agtAltitude, altStr, 2, 15);
char vbatStr[6]; // battery voltage string
ftoa(agtVbat, vbatStr, 2, 6);
char speedStr[8]; // speed string
ftoa(agtSpeed, speedStr, 2, 8);
char pressureStr[9]; // pressure string
ftoa(agtPascals, pressureStr, 0, 9);
char temperatureStr[10]; // temperature string
ftoa(agtTempC, temperatureStr, 1, 10);
// Convert the date and time into strings
char gnssDay[3];
char gnssMonth[3];
if (agtDay < 10)
sprintf(gnssDay, "0%d", agtDay);
else
sprintf(gnssDay, "%d", agtDay);
if (agtMonth < 10)
sprintf(gnssMonth, "0%d", agtMonth);
else
sprintf(gnssMonth, "%d", agtMonth);
char gnssHour[3];
char gnssMin[3];
char gnssSec[3];
if (agtHour < 10)
sprintf(gnssHour, "0%d", agtHour);
else
sprintf(gnssHour, "%d", agtHour);
if (agtMinute < 10)
sprintf(gnssMin, "0%d", agtMinute);
else
sprintf(gnssMin, "%d", agtMinute);
if (agtSecond < 10)
sprintf(gnssSec, "0%d", agtSecond);
else
sprintf(gnssSec, "%d", agtSecond);
// Assemble the message using sprintf
sprintf(outBuffer, "%d%s%s%s%s%s,%s,%s,%s,%s,%d,%d,%d,%s,%s,%s,%d", agtYear, gnssMonth, gnssDay, gnssHour, gnssMin, gnssSec,
latStr, lonStr, altStr, speedStr, agtCourse, agtPDOP, agtSatellites, pressureStr, temperatureStr, vbatStr, iterationCounter);
// Send the message
Serial.print(F("Transmitting message '"));
Serial.print(outBuffer);
Serial.println(F("'"));
/*
// Chase Pixa
commenting out send text for faster loops in testing
#ifndef noTX
agtErr = modem.sendSBDText(outBuffer); // This could take many seconds to complete and will call ISBDCallback() periodically
#else
agtErr = ISBD_SUCCESS;
#endif
// Check if the message sent OK
if (agtErr != ISBD_SUCCESS)
{
Serial.print(F("Transmission failed with error code "));
Serial.println(agtErr);
#ifndef noLED
// Turn on LED to indicate failed send
digitalWrite(LED, HIGH);
#endif
}
else
{
Serial.println(F(">>> Message sent! <<<"));
#ifndef noLED
// Flash LED rapidly to indicate successful send
for (int i = 0; i < 10; i++)
{
digitalWrite(LED, HIGH);
delay(100);
digitalWrite(LED, LOW);
delay(100);
}
#endif
}
// Clear the Mobile Originated message buffer
Serial.println(F("Clearing the MO buffer."));
agtErr = modem.clearBuffers(ISBD_CLEAR_MO); // Clear MO buffer
if (agtErr != ISBD_SUCCESS)
{
Serial.print(F("*** modem.clearBuffers failed with error "));
Serial.print(agtErr);
Serial.println(F(" ***"));
}
*/
// Power down the modem
// Also disable the Serial1 RX pin via IridiumSBD::endSerialPort
Serial.println(F("Putting the 9603N to sleep."));
agtErr = modem.sleep();
if (agtErr != ISBD_SUCCESS) {
Serial.print(F("*** modem.sleep failed with error "));
Serial.print(agtErr);
Serial.println(F(" ***"));
}
iterationCounter = iterationCounter + 1; // Increment the iterationCounter
loop_step = zzz; // Now go to sleep
}
break; // End of case start_9603
// ************************************************************************************************
// Go to sleep
case zzz:
{
Serial.println(F("Getting ready to put the Apollo3 into deep sleep..."));
// Power down the GNSS
Serial.println(F("Powering down the GNSS..."));
gnssOFF(); // Disable power for the GNSS
// Make sure the Serial1 RX pin is disabled
modem.endSerialPort();
// Disable 9603N power
Serial.println(F("Disabling 9603N power..."));
digitalWrite(iridiumSleep, LOW); // Disable 9603N via its ON/OFF pin (modem.sleep should have done this already)
delay(1000);
digitalWrite(iridiumPwrEN, LOW); // Disable Iridium Power
delay(1000);
// Disable the supercapacitor charger
Serial.println(F("Disabling the supercapacitor charger..."));
digitalWrite(superCapChgEN, LOW); // Disable the super capacitor charger
// Close the Iridium serial port
Serial1.end();
// Close the I2C port
//agtWire.end(); //DO NOT Power down I2C - causes badness with v2.1 of the core: https://github.com/sparkfun/Arduino_Apollo3/issues/412
//Chase Pixa
//Wire.end();
digitalWrite(busVoltageMonEN, LOW); // Disable the bus voltage monitor
digitalWrite(LED, LOW); // Disable the LED
// Close and detach the serial console
Serial.println(F("Going into deep sleep until next INTERVAL..."));
Serial.flush(); //Finish any prints
Serial.end(); // Close the serial console
// Code taken (mostly) from Apollo3 Example6_Low_Power_Alarm
// Disable ADC
powerControlADC(false);
// Force the peripherals off
am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_IOM0); // SPI
am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_IOM1); // agtWire I2C
am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_IOM2);
am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_IOM3);
am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_IOM4); // Qwiic I2C //Chase Pixa -- also tried commenting this out and adding it at end of read_sht
am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_IOM5);
am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_ADC);
//am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_UART0); // Leave UART0 on to avoid printing erroneous characters to Serial
am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_UART1); // Serial1
// Disable all unused pins - including: SCL (8), SDA (9), UART0 TX (48) and RX (49) and UART1 TX (24) and RX (25)
const int pinsToDisable[] = { 0, 1, 2, 8, 9, 11, 12, 14, 15, 16, 20, 21, 24, 25, 29, 31, 32, 33, 36, 37, 38, 42, 43, 44, 45, 48, 49, -1 };
for (int x = 0; pinsToDisable[x] >= 0; x++) {
pin_config(PinName(pinsToDisable[x]), g_AM_HAL_GPIO_DISABLE);
}
//Power down CACHE, flashand SRAM
am_hal_pwrctrl_memory_deepsleep_powerdown(AM_HAL_PWRCTRL_MEM_ALL); // Power down all flash and cache
am_hal_pwrctrl_memory_deepsleep_retain(AM_HAL_PWRCTRL_MEM_SRAM_384K); // Retain all SRAM (0.6 uA)
// Keep the 32kHz clock running for RTC
am_hal_stimer_config(AM_HAL_STIMER_CFG_CLEAR | AM_HAL_STIMER_CFG_FREEZE);
am_hal_stimer_config(AM_HAL_STIMER_XTAL_32KHZ);
// This while loop keeps the processor asleep until INTERVAL seconds have passed
while (!intervalAlarm) // Wake up every INTERVAL seconds
{
// Go to Deep Sleep.
am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP);
}
intervalAlarm = false; // Clear the alarm flag now
// Wake up!
loop_step = wakeUp;
}
break; // End of case zzz
// ************************************************************************************************
// Wake from sleep
case wakeUp:
// Code taken (mostly) from Apollo3 Example6_Low_Power_Alarm
// Go back to using the main clock
am_hal_stimer_config(AM_HAL_STIMER_CFG_CLEAR | AM_HAL_STIMER_CFG_FREEZE);
am_hal_stimer_config(AM_HAL_STIMER_HFRC_3MHZ);
// Power up SRAM, turn on entire Flash
am_hal_pwrctrl_memory_deepsleep_powerdown(AM_HAL_PWRCTRL_MEM_MAX);
// Renable UART0 pins: TX (48) and RX (49)
pin_config(PinName(48), g_AM_BSP_GPIO_COM_UART_TX);
pin_config(PinName(49), g_AM_BSP_GPIO_COM_UART_RX);
/*
//Chase Pixa
//Re=enable IOM4 manually. Wire.begin does not do it for us
am_hal_pwrctrl_periph_enable(AM_HAL_PWRCTRL_PERIPH_IOM4);
Wire.begin();
//Wire.beginTransmission(0x55);
Serial.flush();
*/
// Do not renable the UART1 pins here as the modem is still powered off. Let modem.begin do it via beginSerialPort.
// Enable ADC
powerControlADC(true);
// Do it all again!
loop_step = loop_init;
break; // End of case wake
} // End of switch (loop_step)
} // End of loop()
From all of my debugging/troubleshooting efforts my conclusion is there is confliction between agtwire and wire preventing communication being switched between sensors following being powered off
I hope this provides sufficient detail and I would be grateful for any and all recommendations of how to move forward. I would also be more than happy to provide any of my other code or steps taken in troubleshooting.]([Observation] Disabling an IOM causes much badness on v2.1 · Issue #412 · sparkfun/Arduino_Apollo3 · GitHub)