ZaneL / Teensy-ICM-20948

Arduino library for the ICM-20948 motion tracking sensor -- with DMP support
62 stars 17 forks source link

Using multiple IMUs #4

Open n2d7 opened 3 years ago

n2d7 commented 3 years ago

Hi, Is it possible to use multiple IMUs with this library? If possible, what are the necessary changes that need to be done.

Thanks!

ZaneL commented 3 years ago

Hmmm what happens if you leave the library as it is and just make a different settings structure for each ICM chip, each having a different CS pin? And then declare a different TeensyICM20948 object for each.

TeensyICM20948 icm20948_one; TeensyICM20948 icm20948_two;

TeensyICM20948Settings icmSettings_one = ... TeensyICM20948Settings icmSettings_two = ...

Etc...

ZaneL commented 3 years ago

Something like this, with whatever pins you want to use for CS on each chip. I don't have any hardware to test this though, you'll have to let me know if it works.

#include <Teensy-ICM-20948.h>

TeensyICM20948 icm20948One;
TeensyICM20948 icm20948Two;

TeensyICM20948Settings icmSettingsOne =
{
  .cs_pin = 9,                  // SPI chip select pin
  .mode = 1,                     // 0 = low power mode, 1 = high performance mode
  .enable_gyroscope = false,      // Enables gyroscope output
  .enable_accelerometer = false,  // Enables accelerometer output
  .enable_magnetometer = false,   // Enables magnetometer output
  .enable_quaternion = true,     // Enables quaternion output
  .gyroscope_frequency = 1,      // Max frequency = 225, min frequency = 1
  .accelerometer_frequency = 1,  // Max frequency = 225, min frequency = 1
  .magnetometer_frequency = 1,   // Max frequency = 70, min frequency = 1
  .quaternion_frequency = 225     // Max frequency = 225, min frequency = 50
};

TeensyICM20948Settings icmSettingsTwo =
{
  .cs_pin = 10,                  // SPI chip select pin
  .mode = 1,                     // 0 = low power mode, 1 = high performance mode
  .enable_gyroscope = false,      // Enables gyroscope output
  .enable_accelerometer = false,  // Enables accelerometer output
  .enable_magnetometer = false,   // Enables magnetometer output
  .enable_quaternion = true,     // Enables quaternion output
  .gyroscope_frequency = 1,      // Max frequency = 225, min frequency = 1
  .accelerometer_frequency = 1,  // Max frequency = 225, min frequency = 1
  .magnetometer_frequency = 1,   // Max frequency = 70, min frequency = 1
  .quaternion_frequency = 225     // Max frequency = 225, min frequency = 50
};

void setup()
{
  Serial.begin(115200);
  delay(5000);

  icm20948One.init(icmSettingsOne);
  icm20948Two.init(icmSettingsTwo);
}

void loop()
{
  float quat_w_one, quat_x_one, quat_y_one, quat_z_one;
  float quat_w_two, quat_x_two, quat_y_two, quat_z_two;
  char sensor_string_buff[128];

  // Must call this often in main loop -- updates the sensor values
  icm20948One.task();
  icm20948Two.task();

  if (icm20948One.quatDataIsReady())
  {
    icm20948One.readQuatData(&quat_w_one, &quat_x_one, &quat_y_one, &quat_z_one);

    // Store data in a JSON string and send it over the serial port
    sprintf(sensor_string_buff, "{\"quat_w_one\":%f, \"quat_x_one\":%f, \"quat_y_one\":%f, \"quat_z_one\":%f}", quat_w_one, quat_x_one, quat_y_one, quat_z_one);
    Serial.println(sensor_string_buff);
  }

  if (icm20948Two.quatDataIsReady())
  {
    icm20948Two.readQuatData(&quat_w_two, &quat_x_two, &quat_y_two, &quat_z_two);

    // Store data in a JSON string and send it over the serial port
    sprintf(sensor_string_buff, "{\"quat_w_two\":%f, \"quat_x_two\":%f, \"quat_y_two\":%f, \"quat_z_two\":%f}", quat_w_two, quat_x_two, quat_y_two, quat_z_two);
    Serial.println(sensor_string_buff);
  }
}
n2d7 commented 3 years ago

Sorry for the late reply. That is the first thing I did. However, the initialization of the 2nd device (no matter the order) always get stuck on the line

// Reset icm20948 driver states
  inv_icm20948_reset_states(&icm_device, &icm20948_serif);

in the function void TeensyICM20948::init(TeensyICM20948Settings settings) (I used printlns to see where it got stuck)

ZaneL commented 3 years ago

Oh okay.

static inline void inv_icm20948_reset_states(struct inv_icm20948 * s,
        const struct inv_icm20948_serif * serif)
{
    assert(icm20948_instance == 0);

    memset(s, 0, sizeof(*s));
    s->serif = *serif;
    icm20948_instance = s;
}

You can see there is an assertion to make sure that the driver "instance" hasn't already been initialized. Then up above it there is a comment in the invensense code that says:

/** @brief ICM20948 driver states singleton declaration
 *  Because of Low-level driver limitation only one insance of the driver is allowed
 */
extern struct inv_icm20948 * icm20948_instance;

So it looks like it might not be possible...not sure why though.

n2d7 commented 3 years ago

Thanks!

ZaneL commented 3 years ago

Also looking at the code I wrote I can see some problems. For example up at the top I just made the variables global instead of putting them into the class...


int chipSelectPin = 10;

float gyro_x, gyro_y, gyro_z;
bool gyro_data_ready = false;

float accel_x, accel_y, accel_z;
bool accel_data_ready = false;

float mag_x, mag_y, mag_z;
bool mag_data_ready = false;

float quat_w, quat_x, quat_y, quat_z;
bool quat_data_ready = false;

Even if I fixed that it sounds like it still wouldn't work due to the invensense driver limitations.

n2d7 commented 3 years ago

Also the Invensense variables are global as well. inv_icm20948_t icm_device; I tried moving the global variables to the class, just to see if it works with multiple devices. But sadly couldn't fix all the errors that are coming up yet.

n2d7 commented 3 years ago

It works when I fix the global variables and when the assertion is commented out (checked with your nodejs application for quaternion values). Not sure why the assertion is there in the first place.

DemorianJH commented 3 years ago

It works when I fix the global variables and when the assertion is commented out (checked with your nodejs application for quaternion values). Not sure why the assertion is there in the first place.

If this is working ok, is there any chance of a pull request with your changes? Thanks

n2d7 commented 3 years ago

Sorry, It did not work with further testing. I realized later that values of only 1 IMU gets updated. For the other IMUs, values of the first IMU got mirrored somehow.

DemorianJH commented 3 years ago

There were a few more statics in the c driver which likely caused the mirroring problem. I've converted the c into a large cpp class which is ugly and unmaintainable but appears to be correctly reporting different values for two IMU's on the same MCU (SPI not tested). https://github.com/DemorianJH/ICM20948-Multi-IMU