Artemis Global Tracker MbedOS hardfault after 1st data transmission w/ defined user values

Hi everyone,

I am trying to use the AGT for remote sensing applications. The AGT is connected to a MUX with multiple sensors attached (3x VCNL ambient light sensor, 1x SHTC3 pressure/temp/humidity sensor, and an in-house multi-gas sensor), MUX used for address management.

Building off of Example16_GlobalTracker - I have used the respective github repos for taking portions of code to return values from the sensors above. However following the first measurement and attempted data transmission I get a MbedOS HardFault. The fault comes when it gets to the line of code to begin the first sensor again.

Code notes: I am using excessive serial.println to debug, this is how I determined when the hardfault was coming in. This code only reflects the VCNL light sensors.

/
// Artemis Global Tracker: User Functions
#include <Wire.h> //Click here to get the library: http://librarymanager/All#SparkFun_VCNL4040
#include "SparkFun_VCNL4040_Arduino_Library.h"
VCNL4040 proximitySensor;
long startingProxValue = 0;
long deltaNeeded = 0;
boolean nothingThere = false;

//global variable definitions - used for VCNL light sensors 
byte ambientValue0;
byte ambientValue1;
uint16_t ambientValue2;

// Add your own code to these functions to return USERVAL's or execute USERFUNC's

void USER_FUNC_1()
{
  debugPrintln(F("Executing USERFUNC1..."));
  // Add your own code here - it will be executed when the Tracker receives a "USERFUNC1" (0x58) message field
}

void USER_FUNC_2()
{
  debugPrintln(F("Executing USERFUNC2..."));
  // Add your own code here - it will be executed when the Tracker receives a "USERFUNC2" (0x59) message field
}

void USER_FUNC_3()
{
  debugPrintln(F("Executing USERFUNC3..."));
  // Add your own code here - it will be executed when the Tracker receives a "USERFUNC3" (0x5a) message field
}

void USER_FUNC_4()
{
  debugPrintln(F("Executing USERFUNC4..."));
  // Add your own code here - it will be executed when the Tracker receives a "USERFUNC4" (0x5b) message field
}

void USER_FUNC_5(uint16_t myVar)
{
  debugPrintln(F("Executing USERFUNC5..."));
  // Add your own code here - it will be executed when the Tracker receives a "USERFUNC5" (0x5c) message field
}

void USER_FUNC_6(uint16_t myVar)
{
  debugPrintln(F("Executing USERFUNC6..."));
  // Add your own code here - it will be executed when the Tracker receives a "USERFUNC6" (0x5d) message field
}

void USER_FUNC_7(uint32_t myVar)
{
  debugPrintln(F("Executing USERFUNC7..."));
  // Add your own code here - it will be executed when the Tracker receives a "USERFUNC7" (0x5e) message field
}

void USER_FUNC_8(uint32_t myVar)
{
  debugPrintln(F("Executing USERFUNC8..."));
  // Add your own code here - it will be executed when the Tracker receives a "USERFUNC8" (0x5f) message field
}

byte USER_VAL_1()
{
  // #include <Wire.h>
  //Click here to get the library: http://librarymanager/All#SparkFun_VCNL4040
  //#include "SparkFun_VCNL4040_Arduino_Library.h"
  //VCNL4040 proximitySensor;

  #define NUMBER_OF_SENSORS 3
  Serial.println("Qwiic Mux Shield Read ");
  delay(1000);
  Wire.begin();
  Serial.println("Wire Up ");


  for (byte x = 0 ; x < NUMBER_OF_SENSORS ; x++)
  {
    Serial.println("in mux on loop ");
    enableMuxPort(x); //Tell mux to connect to port X
    Serial.println("enabled mux port ");

    proximitySensor.begin(); //Initialize the sensor
    Serial.println("sensor began ");

    proximitySensor.powerOffProximity(); //Power down the proximity portion of the sensor
    Serial.println("powered off prox ");

    proximitySensor.powerOnAmbient(); //Power up the ambient sensor
    Serial.println("powered on amb ");

    Serial.print("Light sensor online: ");
    Serial.println(x);

    disableMuxPort(x);
    Serial.println(" disabled mux port ");

  }

  Serial.println("Mux Shield online");

  unsigned int ambv[] = {0,0,0};
  
  for (byte x = 0 ; x < NUMBER_OF_SENSORS ; x++)
  {
    enableMuxPort(x); //Tell mux to connect to this port, and this port only

    unsigned int ambientValue = proximitySensor.getAmbient();
    ambv[x] = ambientValue;

    disableMuxPort(x); //Tell mux to disconnect from this port
  }

  Serial.print("ambv[0] is: ");
  Serial.println(ambv[0]);
  Serial.print("ambv[1] is: ");
  Serial.println(ambv[1]);
  Serial.print("ambv[2] is: ");
  Serial.println(ambv[2]);

  Serial.println();
  delay(1000); //Wait for next reading

  //scale lux max value 6553 to byte max 255 (factor = 25.6980392)
  ambv[0] = ambv[0] / 25.6980392; //truncated
  ambv[1] = ambv[1] / 25.6980392;

  Serial.print("ambv[0] post scaling is: ");
  Serial.println(ambv[0]);
  Serial.print("ambv[1] post scaling is: ");
  Serial.println(ambv[1]);

  //converting unsigned ints to byte (0,1) and uint16_t (2)
  ambientValue0 = ambv[0];
  ambientValue1 = ambv[1];
  ambientValue2 = ambv[2];

  return (ambientValue0);
  
}

byte USER_VAL_2()
{
    return (ambientValue1);
}

uint16_t USER_VAL_3()
{
   return (ambientValue2);
}
uint16_t USER_VAL_4()
{
  // Add your own code here - e.g. read an external sensor. The return value will be sent as a "USERVAL4" (0x23) message field
  uint16_t retVal; // The return value.
  retVal = 4; // Set retVal to 4 for testing - delete this line if you add your own code
  return (retVal);
}

uint32_t USER_VAL_5()
{
  // Add your own code here - e.g. read an external sensor. The return value will be sent as a "USERVAL5" (0x24) message field
  uint32_t retVal; // The return value.
  retVal = 5; // Set retVal to 5 for testing - delete this line if you add your own code
  return (retVal);
}

uint32_t USER_VAL_6()
{
  // Add your own code here - e.g. read an external sensor. The return value will be sent as a "USERVAL6" (0x25) message field
  uint32_t retVal; // The return value.
  retVal = 6; // Set retVal to 6 for testing - delete this line if you add your own code
  return (retVal);
}

float USER_VAL_7()
{
  
  // Add your own code here - e.g. read an external sensor. The return value will be sent as a "USERVAL7" (0x26) message field
  float retVal; // The return value.
  retVal = 7.0; // Set retVal to 7.0 for testing - delete this line if you add your own code
  return (retVal);

}

float USER_VAL_8()
{
  // Add your own code here - e.g. read an external sensor. The return value will be sent as a "USERVAL8" (0x27) message field
  float retVal; // The return value.
  retVal = 8.0E8; // Set retVal to 8.0E8 for testing - delete this line if you add your own code
  return (retVal);
}

void ALARM_FUNC(uint8_t alarmType)
{
  debugPrintln(F("Executing ALARM_FUNC ..."));
  // Add your own code to be executed when an alarm is set off.
}

Note: I am also using the AGT configuration tool to select which user values to transmit.

Thank you for you help!

I do not see anything wrong in the code. Can you share/attach the complete sketch ?

Hi crpixa,

I suggest trying your code in stages / steps:

Does Example16 work correctly unmodified?

Does Example16 work if you add a single VCNL4040 - without the mux?

Does Example16 work if you add a single VCNL4040 using the mux?

I can not see your code for enableMuxPort, but I assume you are calling the Qwiic Mux library function enablePort ? Likewise, I assume you are calling disablePort inside disableMuxPort ? I suggest calling setPortState(0) first to ensure all eight ports are disabled. I think that all ports are enabled by default. The hard fault may be caused because you then have multiple sensors connected simultaneously?

I hope this helps,

Paul (PaulZC)

Looks like the usual issue of switching a mux behind the back of the driver.

You have one software instance of proximitySensor. You instantiate it once, connect it to one device (where it likely configures things on the sensor) and then switch the mux to another device and try talking to it without initializing it…

Look at viewtopic.php?f=105&t=54277&hilit=entrant#p220782 for an example of using an array of instances for each mux channel.

/mike

Wow - thanks Mike! That code example has been bad for a very long time…

@crpixa: apologies for the bad example. I have updated the Qwiic Mux hookup guide example. Please see the new code and zip file at: https://learn.sparkfun.com/tutorials/qw … no-example

I hope this gets your project going,

Paul

PS. I was wrong about the mux ports all being enabled by default. According to the NXP PCA9548 datasheet, they are disabled by default (the port register value defaults to 0x00). The extra code I added to disable all eight ports first is actually redundant, but I’m going to leave it in there - just to show how to do it if you need to.

PaulZC:
Wow - thanks Mike! That code example has been bad for a very long time…

Thanks very much for fixing this.

/mike

Hi! I’m currently working on a project that uses the SparkFun Qwiic IMU. I’m having some trouble getting the IMU to work properly, so I was hoping someone here could help me out. I’ve followed the instructions in the SparkFun Qwiic IMU documentation, but I’m still not getting the results I’m expecting. The IMU seems to be working, but the data I’m getting from it doesn’t seem to be accurate. I’m wondering if anyone else has had this problem and if they were able to fix it. If so, I’d really appreciate any help you could give me.

work project

Hi,

It would be better to ask your question in the IMU Forum - viewforum.php?f=83 - unless you are trying to use it with the Artemis Global Tracker?

Best wishes,

Paul

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):

  1. 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

  2. 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

  3. 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

  4. 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)

For reference I am running apollo3 v2.1.0 per sparkfun AGT hookup guide; is this outdated guidance?

Based on these similar problems that people have made work arounds for?

https://github.com/sparkfunX/Artemis_Gl … /issues/38

https://github.com/sparkfun/Arduino_Apollo3/issues/454

Hi @crpixa (Chase?),

Ah, sorry. I thought this issue was linked to having multiple sensors connected. I didn’t realize that it is actually linked to the sleep/wake functionality.

This is bringing back many memories. It is a long time since I wrote the Global Tracker examples, and also a long time since I had to update the OpenLog Artemis sleep/wake code after we updated the Apollo3 core.

Sleep/wake works on OpenLog Artemis - using version 2.2.1 of the SparkFun Apollo3 core. But the core needs to be patched in a couple of places to make it work correctly. The OpenLog Artemis build instructions are here: https://github.com/sparkfun/OpenLog_Art … _BINARY.md . And the patches are described here: https://github.com/sparkfun/OpenLog_Art … ollo3-core . The SPI patch probably doesn’t apply in your case with the AGT. But the UART Power patch is important - it prevents the badness caused by the Iridium modem pulling the UART RX pin low.

Then there is the sleep/wake code itself. The sleep/wake functions are here: https://github.com/sparkfun/OpenLog_Art … #L250-L567 . Not all of that applies to the AGT, but there are some critical bits, like: https://github.com/sparkfun/OpenLog_Art … r.ino#L291 , https://github.com/sparkfun/OpenLog_Art … #L314-L324 , https://github.com/sparkfun/OpenLog_Art … #L341-L343 , https://github.com/sparkfun/OpenLog_Art … #L525-L527 .

So, where does that leave us with the AGT examples? I can see now that the examples include some of the corrections for Apollo3 2.1+, but not all. For example, in Example16, I have commented the line that disables PERIPH_IOM1 (agtWire). But I have not included the code to properly restore the SCL and SDA pins during wake.

Unfortunately, I have a ton of other work I need to be getting on with. I don’t have time right now to properly update and test the examples. Please accept my apologies, but I am hoping you can work your way through this by updating to 2.2.1, patching the core, and modifying the sleep/ wake code so it matches the OLA?

Please feel free to open an issue for this in the SparkFun AGT GitHub repo: https://github.com/sparkfun/SparkFun_Ar … ker/issues . That way we can track it better. It supersedes the original SparkfunX repo.

Very best wishes - and apologies again for the inconvenience,

Paul

Hi Paul,

Thank you for all of the resources. The project was short on time so I had just added a simple timing relay of a Stemma and trinket for power cycling, not beautiful but functional none-the-less. It does pose some issues such as data loss with failed transmission, etc. I also switched to Ex. 15, as it was easier to work with.

The project is being revisited now, and we are about to re-try implementing the notes you provided, but I wanted to ask if there has been any changes/updates to the examples that address these issues. If not, not a problem. Thanks for your help!

Best,

Chase

Hi Chase,

Nice to hear from you.

There have not been any changes to the Artemis Global Tracker examples - for Apollo3 2.2.1. Sorry about that. But we did make some changes to the OpenLog Artemis recently. The OLA firmware can now be compiled using a GitHub Action (Workflow) - the action patches the core before the code is compiled. Also, a user contributed a Docker file which also patches the core before compiling. You may find one or both of these useful?

https://github.com/sparkfun/OpenLog_Art … ml#L89-L98

https://github.com/sparkfun/OpenLog_Art … Dockerfile

Very best wishes,

Paul