I2c between Seeeduino XIAO (slave) and raspberry pi 4 (master)

Hi, I succeeded in sending double type data from arduino (Seeeduino XIAO) to raspberry pi 4 by I2C.

So, I tried the next step. I got sensor data from ICM20948 on arduino (Seeeduino XIAO) and calculated Euler angles (Roll, Pitch, Yaw). And I tried to send data (Roll, Pitch, Yaw) to raspberry pi 4, but it failed. I think that "Wire.setClock(400000);" may be causes of failure. This is necessary for I2C between arduino and ICM20948. However, this may be the cause of error in I2C between arduino and raspberry pi 4.

Could you please tell me the solution?

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

#define AD0_VAL 0x69
#define i2c_add 0x08

ICM_20948_I2C myICM;

double num[3] = {0.00, 0.00, 0.00};

void setup() {
  Wire.begin(i2c_add);
  Wire.onRequest(send);
  delay(100);
  Wire.setClock(400000);
  delay(100);
  Serial.begin(115200);
  delay(100);

  bool initialized = false;
  while (!initialized) {
    myICM.begin(Wire, AD0_VAL);
#ifndef QUAT_ANIMATION
    Serial.print(F("Initialization of the sensor returned: "));
    Serial.println(myICM.statusString());
#endif
    if (myICM.status != ICM_20948_Stat_Ok) {
#ifndef QUAT_ANIMATION
      Serial.println(F("Trying again..."));
#endif
      delay(500);
    } else {
      initialized = true;
    }
  }
  bool success = true;  // Use success to show if the DMP configuration was successful

  // Initialize the DMP. initializeDMP is a weak function. You can overwrite it if you want to e.g. to change the sample rate
  success &= (myICM.initializeDMP() == ICM_20948_Stat_Ok);

  // Enable the DMP Game Rotation Vector sensor
  success &= (myICM.enableDMPSensor(INV_ICM20948_SENSOR_GAME_ROTATION_VECTOR) == ICM_20948_Stat_Ok);

  success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Quat6, 0) == ICM_20948_Stat_Ok);  // Set to the maximum

  // Enable the FIFO
  success &= (myICM.enableFIFO() == ICM_20948_Stat_Ok);

  // Enable the DMP
  success &= (myICM.enableDMP() == ICM_20948_Stat_Ok);

  // Reset DMP
  success &= (myICM.resetDMP() == ICM_20948_Stat_Ok);

  // Reset FIFO
  success &= (myICM.resetFIFO() == ICM_20948_Stat_Ok);

  // Check success
  if (success) {
#ifndef QUAT_ANIMATION
    Serial.println(F("DMP enabled!"));
#endif
  } else {
    Serial.println(F("Enable DMP failed!"));
    Serial.println(F("Please check that you have uncommented line 29 (#define ICM_20948_USE_DMP) in ICM_20948_C.h..."));
    while (1)
      ;  // Do nothing more
  }
}


void loop() {
  icm_20948_DMP_data_t data;
  myICM.readDMPdataFromFIFO(&data);

  if ((myICM.status == ICM_20948_Stat_Ok) || (myICM.status == ICM_20948_Stat_FIFOMoreDataAvail))  // Was valid data available?
  {

    if ((data.header & DMP_header_bitmap_Quat6) > 0)  // We have asked for GRV data so we should receive Quat6
    {
      // Scale to +/- 1
      double q1 = ((double)data.Quat6.Data.Q1) / 1073741824.0;  // Convert to double. Divide by 2^30
      double q2 = ((double)data.Quat6.Data.Q2) / 1073741824.0;  // Convert to double. Divide by 2^30
      double q3 = ((double)data.Quat6.Data.Q3) / 1073741824.0;  // Convert to double. Divide by 2^30

      double q0 = sqrt(1.0 - ((q1 * q1) + (q2 * q2) + (q3 * q3)));

      double q2sqr = q2 * q2;

      // roll (x-axis rotation)
      double t0 = +2.0 * (q0 * q1 + q2 * q3);
      double t1 = +1.0 - 2.0 * (q1 * q1 + q2sqr);
      double roll = atan2(t0, t1) * 180.0 / PI;

      // pitch (y-axis rotation)
      double t2 = +2.0 * (q0 * q2 - q3 * q1);
      t2 = t2 > 1.0 ? 1.0 : t2;
      t2 = t2 < -1.0 ? -1.0 : t2;
      double pitch = asin(t2) * 180.0 / PI;

      // yaw (z-axis rotation)
      double t3 = +2.0 * (q0 * q3 + q1 * q2);
      double t4 = +1.0 - 2.0 * (q2sqr + q3 * q3);
      double yaw = atan2(t3, t4) * 180.0 / PI;

      num[0] = roll;
      num[1] = pitch;
      num[2] = yaw;
    }
  }

  Serial.print(F("Roll:"));
  Serial.print(num[0], 2);
  Serial.print(F(" Pitch:"));
  Serial.print(num[1], 2);
  Serial.print(F(" Yaw:"));
  Serial.println(num[2], 2);


  if (myICM.status != ICM_20948_Stat_FIFOMoreDataAvail)  // If more data is available then we should read it right away - and not delay
  {
    delay(10);
  }

  delay(100);
}

void send() {
  Wire.write((byte*)&num, sizeof(num));
}

I doubt. Please show me the part of the datasheet where that fast clock speed is required.

This may be true but the root cause of this may be the hardware. How do you make the connection between the boards? What pullups do you use? How long are the wires?

Additionally your Xiao is used as a master and a slave. Although the I2C theoretically defines such a usage (called multimaster) most Wire libraries for the Arduino platforms don't support it and I've never seen a project were it ran reliably. I also don't know if the Raspberry Pi supports it.

Why do you want to use I2C for the communication to the Raspberry Pi? Why is USB ruled out?

1 Like

Thanks for your answer.

If I comment out Wire.setClock(400000); in the code for getting sensor data from the ICM20948 to the Seeeduino XIAO, "Trying again..." is displayed on the serial monitor and sensor data cannot be acquired.

Arduino and raspberry pi 4 are connected by 3 Wires: SCL, SDA and GND. The wires are approximately 10 cm (4inch) long.

Arduino is connected to the laptop via a USB port. So the Seeeduino XIAO is powered from there.
The sensor data is acquired with the arduinoIDE code installed on the laptop.

The reason I want to i2c arduino and raspberry pi 4 is just to send data from arduino to raspberry pi 4.

If I could write the code to get the sensor data in Python or C on the raspberry pi 4, I wouldn't need the arduino. However, given the possibility of bugs, it would be too difficult for me to write new code.

What status is printed?

Which library are you using? The only one available in the IDE library manager with that include name (Sparkfun) doesn't match your parameter choice for the begin() method (AD0_VAL is boolean).

So you don't have pullups on the signals...

Of course but why do you think a multimaster I2C is the best option for that? If you connect the XIAO by USB to the Raspberry Pi you can use simple serial communication for that and avoid the error-prone multimaster-I2C.

Thanks again for the answers.

After this, raspberry pi 4 needs to do serial communication for another device (3-axis gimbal with motor), so it is impossible to do serial communication between raspberry pi 4 and arduino. However, you are right, a multimaster I2C is not the best way. After discussing with my assistant professor and considering your advice, I decided to use SPI communication between arduino and ICM20948 and I2C communication between arduino and raspberry pi 4.

Thank you very much for your help.

P.S.

By using both SPI and I2C methods, I finally succeeded in recording the data acquired from the ICM20948 to raspberry pi 4 in real time! Thank you!

That's not true. You can have almost any number of serial devices connected to the Raspberry Pi by USB.

You should tell us if you have requirements given by your professor which don't make sense in real life. We usually don't expect academic task with no practical use value.

correct - I have connected a number of serial devices to a RPi by USB including Arduinos, ESP32s, etc
each USB serial connection creates a serial port which can opened by a program (C++, Java, Python, etc) and data read and written

Thanks for letting me know. I didn't know that.

So, you mean that communication using USB is practical, and that communication using SPI may cause some problems in practical situations? Or no problem, but communication using USB is better than communication using SPI.

Thanks for letting me know that.

It's much more error-prone, especially for inexperienced users. An SPI bus is very limited in length (it's designed as an in-PCB bus) and tracking down problems requires a well-equipped electronics lab (oscilloscope, logic analyzer, etc.). So the communication by USB is by far more easy to establish and program.

Thanks for your advice.

Now, Raspberry Pi 4 and arduino board (Seeeduino XIAO) are connected via I2C, and arduino board (Seeeduino XIAO) and ICM20948 are connected via SPI. Arduino board's Type-C port is used for serial communication with a notebook PC.
In this situation, it seems impossible to communicate with Raspberry pi and arduino board via serial.

if you plug the Arduino USB into a RPi USB port you can in effect get two data streams

  1. debug information which usually goes to the arduino serial monitor
  2. sensor or other data used by the PRi for control actions etc

you need some means to identify each stream, e.g.

  1. start degbug text line with a > character - such text is directed to the RPi console
  2. text which starts without the > is identified as sensor data

text which would have appeared on the Arduino Serial Monitor appears on the RPi console

Does this mean that I have to install arduinoIDE on my RPi?
Now, I am using arduinoIDE installed on my laptop to process Seeeduino XIAO (arduino board).

you can or connect the device to the laptop USB for prrogramming and then plug it into the RPi USB for running

Thanks for your advice.

Given the possibility of some bugs, I consider the way you have given us.

As stated by @pylon in post 11 implementing SPI communication between two microcontrollers is none trival -- I would say the same regarding I2C communication which involves complex master/slave programming and potential problems with the physical wiring.
Serial is much simpler in particular with systems such as the RPi which enable multiple USB serial interconnections supported by serial library functions in langugaes such as C++, Python, java, etc (on RPi microcontrollers I tend to use Java)

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.