Is it possible to use two ICM-20948s with the OpenLog Artemis?

Hello all.

Basically what the title asks. I needed data from another ICM-20948 in my application. I tried connecting a SparkFun ICM-20948 using the Qwiic connector to the OLA. Below is the code that I’m trying to run:

/****************************************************************
 * Testing basic functionality of the ICM 20948 on the OpenLog Artemis.
 * Select "SparkFun Apollo3" \ SparkFun RedBoard Artemis ATP" as the board.
 * 
 * Based on:
 * 
 * Example1_Basics.ino
 * ICM 20948 Arduino Library Demo 
 * Use the default configuration to stream 9-axis IMU data
 * Owen Lyke @ SparkFun Electronics
 * Original Creation Date: April 17 2019
 * 
 * This code is beerware; if you see me (or any other SparkFun employee) at the
 * local, and you've found our code helpful, please buy us a round!
 * 
 * Distributed as-is; no warranty is given.
 ***************************************************************/

// OLA Specifics:
const byte PIN_IMU_POWER = 27; // The Red SparkFun version of the OLA (V10) uses pin 27
const byte PIN_IMU_INT = 37;
const byte PIN_IMU_CHIP_SELECT = 44;
const byte PIN_SPI_SCK = 5;
const byte PIN_SPI_CIPO = 6;
const byte PIN_SPI_COPI = 7;
const byte PIN_QWIIC_SCL = 8;
const byte PIN_QWIIC_SDA = 9;


#include "ICM_20948.h"  // Click here to get the library: http://librarymanager/All#SparkFun_ICM_20948_IMU
#include <Wire.h>

#define USE_SPI       // Uncomment this to use SPI

#define SERIAL_PORT Serial

#define SPI_PORT SPI    // Your desired SPI port.       Used only when "USE_SPI" is defined
#define CS_PIN PIN_IMU_CHIP_SELECT // Which pin you connect CS to. Used only when "USE_SPI" is defined. OLA uses pin 44.

TwoWire qwiic(PIN_QWIIC_SDA, PIN_QWIIC_SCL);

#define WIRE_PORT qwiic  // Your desired Wire port.      Used when "USE_SPI" is not defined
#define AD0_VAL   0     // The value of the last bit of the I2C address. 
                        // On the SparkFun 9DoF IMU breakout the default is 1, and when 
                        // the ADR jumper is closed the value becomes 0

ICM_20948_SPI myICM_SPI;  // If using SPI create an ICM_20948_SPI object
ICM_20948_I2C myICM_I2C;  // Otherwise create an ICM_20948_I2C object


void setup()
{
  // ========= Initialise SPI ========
  SPI_PORT.begin();
  pinMode(PIN_IMU_CHIP_SELECT, OUTPUT);
  digitalWrite(PIN_IMU_CHIP_SELECT, HIGH); //Be sure IMU is deselected
  enableCIPOpullUp(); // Enable CIPO pull-up on the OLA

  //There is a quirk in v2.1 of the Apollo3 mbed core which means that the first SPI transaction will
  //disable the pull-up on CIPO. We need to do a fake transaction and then re-enable the pull-up
  //to work around this...
  #if defined(ARDUINO_ARCH_MBED)
    SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0)); // Do a fake transaction
    SPI.endTransaction();
    enableCIPOpullUp(); // Re-enable the CIPO pull-up
  #endif
  
  // ========= Initialise I2C ========
  WIRE_PORT.begin();
  WIRE_PORT.setClock(400000);
  
  //Reset ICM by power cycling it
  imuPowerOff();

  delay(10);

  imuPowerOn(); // Enable power for the OLA IMU

  delay(100); // Wait for the IMU to power up

  SERIAL_PORT.begin(115200);
  while(!SERIAL_PORT);

  bool initialized = false;
  while( !initialized )
  {
    myICM_SPI.begin( CS_PIN, SPI_PORT ); 
    myICM_I2C.begin( WIRE_PORT, AD0_VAL );

    SERIAL_PORT.print( F("Initialization of the in-built (SPI) sensor returned: ") );
    SERIAL_PORT.println( myICM_SPI.statusString() );
    SERIAL_PORT.print( F("Initialization of the external (I2C) sensor returned: ") );
    SERIAL_PORT.println( myICM_I2C.statusString() );
    if( myICM_SPI.status != ICM_20948_Stat_Ok || myICM_I2C.status != ICM_20948_Stat_Ok ){
      SERIAL_PORT.println( "Trying again..." );
      delay(500);
    }else{
      initialized = true;
    }
  }
}

void loop() {
}

void imuPowerOn()
{
  pinMode(PIN_IMU_POWER, OUTPUT);
  digitalWrite(PIN_IMU_POWER, HIGH);
}
void imuPowerOff()
{
  pinMode(PIN_IMU_POWER, OUTPUT);
  digitalWrite(PIN_IMU_POWER, LOW);
}

#if defined(ARDUINO_ARCH_MBED) // updated for v2.1.0 of the Apollo3 core
bool enableCIPOpullUp()
{
  //Add 1K5 pull-up on CIPO
  am_hal_gpio_pincfg_t cipoPinCfg = g_AM_BSP_GPIO_IOM0_MISO;
  cipoPinCfg.ePullup = AM_HAL_GPIO_PIN_PULLUP_1_5K;
  pin_config(PinName(PIN_SPI_CIPO), cipoPinCfg);
  return (true);
}
#else
bool enableCIPOpullUp()
{
  //Add CIPO pull-up
  ap3_err_t retval = AP3_OK;
  am_hal_gpio_pincfg_t cipoPinCfg = AP3_GPIO_DEFAULT_PINCFG;
  cipoPinCfg.uFuncSel = AM_HAL_PIN_6_M0MISO;
  cipoPinCfg.ePullup = AM_HAL_GPIO_PIN_PULLUP_1_5K;
  cipoPinCfg.eDriveStrength = AM_HAL_GPIO_PIN_DRIVESTRENGTH_12MA;
  cipoPinCfg.eGPOutcfg = AM_HAL_GPIO_PIN_OUTCFG_PUSHPULL;
  cipoPinCfg.uIOMnum = AP3_SPI_IOM;
  padMode(MISO, cipoPinCfg, &retval);
  return (retval == AP3_OK);
}
#endif

This is basically the code found in the [OLA_IMU_Basics.ino example sketch. I’m creating two objects for the IMU – the first one using SPI for the in-built IMU and the second one using I2C for the external IMU. However, when I open the serial monitor, this is the output I’m getting:

Initialization of the in-built (SPI) sensor returned: All is well.
Initialization of the external (I2C) sensor returned:  Data Underflow"
Trying again...

I also tried using just uploading the [OLA_IMU_Basics.ino example sketch and just made the following change on line 33:

// #define USE_SPI // Uncomment this to use SPI

Basically I tried using I2C for the built-in IMU instead of SPI. However, I’m still getting the same error:

Initialization of the sensor returned: Data Underflow

Could anybody point out why I2C is not working even for the built-in IMU? And if some changes make it work, would it be possible to make use of two IMUs using something similar to what I described above?](OpenLog_Artemis/Firmware/Test Sketches/OLA_IMU_Basics/OLA_IMU_Basics.ino at main · sparkfun/OpenLog_Artemis · GitHub)](OpenLog_Artemis/Firmware/Test Sketches/OLA_IMU_Basics/OLA_IMU_Basics.ino at main · sparkfun/OpenLog_Artemis · GitHub)

You can simplify your code to test the I2C connection with just the external IMU first. This can be the test code:

#include "ICM_20948.h"
#include <Wire.h>

#define SERIAL_PORT Serial
#define PIN_QWIIC_SCL 8
#define PIN_QWIIC_SDA 9

TwoWire qwiic(PIN_QWIIC_SDA, PIN_QWIIC_SCL);
#define WIRE_PORT qwiic
#define AD0_VAL   0 // Change to 1 if ADR jumper is closed

ICM_20948_I2C myICM; // I2C object

void setup() {
  // Start serial communication
  SERIAL_PORT.begin(115200);
  while (!SERIAL_PORT);

  // Initialize I2C communication
  WIRE_PORT.begin();
  WIRE_PORT.setClock(400000);

  // Power up the IMU (if necessary, might not be needed for external IMU)
  pinMode(PIN_IMU_POWER, OUTPUT);
  digitalWrite(PIN_IMU_POWER, HIGH);
  delay(100);

  // Initialize the IMU
  bool initialized = false;
  while (!initialized) {
    myICM.begin(WIRE_PORT, AD0_VAL);
    SERIAL_PORT.print("Initialization of the external (I2C) sensor returned: ");
    SERIAL_PORT.println(myICM.statusString());
    if (myICM.status != ICM_20948_Stat_Ok) {
      SERIAL_PORT.println("Trying again...");
      delay(500);
    } else {
      initialized = true;
      SERIAL_PORT.println("External IMU initialized successfully.");
    }
  }
}

void loop() {
  // Add code to read sensor data if initialization is successful
}