Using MPU6050 with Seeduino XIAO

Hey. So I am trying to use a MPU6050 as a tilt angle sensor with the seeduino XIAO. I found a sketch that works perfectly with the Arduino Uno but for some reason when I upload it to the XIAO it stops outputting angles and acts more like a vibration sensor than anything else. Does anyone know what could be happening? Is there something about SAMD processors that changes how the Wire.h library works?
Here is the example code I've been using:

#include <Wire.h>

//Gyro Variables
float elapsedTime, times, timePrev;        //Variables for time control
int gyro_error=0;                         //We use this variable to only calculate once the gyro data error
float Gyr_rawX, Gyr_rawY, Gyr_rawZ;     //Here we store the raw data read 
float Gyro_angle_x, Gyro_angle_y;         //Here we store the angle value obtained with Gyro data
float Gyro_raw_error_x, Gyro_raw_error_y; //Here we store the initial gyro data error

//Acc Variables
int acc_error=0;                         //We use this variable to only calculate once the Acc data error
float rad_to_deg = 180/3.141592654;      //This value is for pasing from radians to degrees values
float Acc_rawX, Acc_rawY, Acc_rawZ;    //Here we store the raw data read 
float Acc_angle_x, Acc_angle_y;          //Here we store the angle value obtained with Acc data
float Acc_angle_error_x, Acc_angle_error_y; //Here we store the initial Acc data error

float Total_angle_x, Total_angle_y;

void setup() {
  Wire.begin();                           //begin the wire comunication
  
  Wire.beginTransmission(0x68);           //begin, Send the slave adress (in this case 68)              
  Wire.write(0x6B);                       //make the reset (place a 0 into the 6B register)
  Wire.write(0x00);
  Wire.endTransmission(true);             //end the transmission
  //Gyro config
  Wire.beginTransmission(0x68);           //begin, Send the slave adress (in this case 68) 
  Wire.write(0x1B);                       //We want to write to the GYRO_CONFIG register (1B hex)
  Wire.write(0x10);                       //Set the register bits as 00010000 (1000dps full scale)
  Wire.endTransmission(true);             //End the transmission with the gyro
  //Acc config
  Wire.beginTransmission(0x68);           //Start communication with the address found during search.
  Wire.write(0x1C);                       //We want to write to the ACCEL_CONFIG register
  Wire.write(0x10);                       //Set the register bits as 00010000 (+/- 8g full scale range)
  Wire.endTransmission(true); 

  Serial.begin(9600);                     //Remember to set this same baud rate to the serial monitor  
  times = millis();                        //Start counting time in milliseconds


/*Here we calculate the acc data error before we start the loop
 * I make the mean of 200 values, that should be enough*/
  if(acc_error==0)
  {
    for(int a=0; a<200; a++)
    {
      Wire.beginTransmission(0x68);
      Wire.write(0x3B);                       //Ask for the 0x3B register- correspond to AcX
      Wire.endTransmission(false);
      Wire.requestFrom(0x68,6,true); 
      
      Acc_rawX=(Wire.read()<<8|Wire.read())/4096.0 ; //each value needs two registres
      Acc_rawY=(Wire.read()<<8|Wire.read())/4096.0 ;
      Acc_rawZ=(Wire.read()<<8|Wire.read())/4096.0 ;

      
      /*---X---*/
      Acc_angle_error_x = Acc_angle_error_x + ((atan((Acc_rawY)/sqrt(pow((Acc_rawX),2) + pow((Acc_rawZ),2)))*rad_to_deg));
      /*---Y---*/
      Acc_angle_error_y = Acc_angle_error_y + ((atan(-1*(Acc_rawX)/sqrt(pow((Acc_rawY),2) + pow((Acc_rawZ),2)))*rad_to_deg)); 
      
      if(a==199)
      {
        Acc_angle_error_x = Acc_angle_error_x/200;
        Acc_angle_error_y = Acc_angle_error_y/200;
        acc_error=1;
      }
    }
  }//end of acc error calculation   

/*Here we calculate the gyro data error before we start the loop
 * I make the mean of 200 values, that should be enough*/
  if(gyro_error==0)
  {
    for(int i=0; i<200; i++)
    {
      Wire.beginTransmission(0x68);            //begin, Send the slave adress (in this case 68) 
      Wire.write(0x43);                        //First adress of the Gyro data
      Wire.endTransmission(false);
      Wire.requestFrom(0x68,4,true);           //We ask for just 4 registers 
         
      Gyr_rawX=Wire.read()<<8|Wire.read();     //Once again we shif and sum
      Gyr_rawY=Wire.read()<<8|Wire.read();
   
      /*---X---*/
      Gyro_raw_error_x = Gyro_raw_error_x + (Gyr_rawX/32.8); 
      /*---Y---*/
      Gyro_raw_error_y = Gyro_raw_error_y + (Gyr_rawY/32.8);
      if(i==199)
      {
        Gyro_raw_error_x = Gyro_raw_error_x/200;
        Gyro_raw_error_y = Gyro_raw_error_y/200;
        gyro_error=1;
      }
    }
  }//end of gyro error calculation   
}//end of setup void

void loop() {
  timePrev = times;                        // the previous time is stored before the actual time read
  times = millis();                        // actual time read
  elapsedTime = (times - timePrev) / 1000; //divide by 1000 in order to obtain seconds

  //////////////////////////////////////Gyro read/////////////////////////////////////

    Wire.beginTransmission(0x68);            //begin, Send the slave adress (in this case 68) 
    Wire.write(0x43);                        //First adress of the Gyro data
    Wire.endTransmission(false);
    Wire.requestFrom(0x68,4,true);           //We ask for just 4 registers
        
    Gyr_rawX=Wire.read()<<8|Wire.read();     //Once again we shif and sum
    Gyr_rawY=Wire.read()<<8|Wire.read();
    /*Now in order to obtain the gyro data in degrees/seconds we have to divide first
    the raw value by 32.8 because that's the value that the datasheet gives us for a 1000dps range*/
    /*---X---*/
    Gyr_rawX = (Gyr_rawX/32.8) - Gyro_raw_error_x; 
    /*---Y---*/
    Gyr_rawY = (Gyr_rawY/32.8) - Gyro_raw_error_y;
    
    /*Now we integrate the raw value in degrees per seconds in order to obtain the angle
    * If you multiply degrees/seconds by seconds you obtain degrees */
    /*---X---*/
    Gyro_angle_x = Gyr_rawX*elapsedTime;
    /*---X---*/
    Gyro_angle_y = Gyr_rawY*elapsedTime;
  //////////////////////////////////////Acc read/////////////////////////////////////

  Wire.beginTransmission(0x68);     //begin, Send the slave adress (in this case 68) 
  Wire.write(0x3B);                 //Ask for the 0x3B register- correspond to AcX
  Wire.endTransmission(false);      //keep the transmission and next
  Wire.requestFrom(0x68,6,true);    //We ask for next 6 registers starting withj the 3B  
  /*We have asked for the 0x3B register. The IMU will send a brust of register.
  * The amount of register to read is specify in the requestFrom function.
  * In this case we request 6 registers. Each value of acceleration is made out of
  * two 8bits registers, low values and high values. For that we request the 6 of them  
  * and just make then sum of each pair. For that we shift to the left the high values 
  * register (<<) and make an or (|) operation to add the low values.
  If we read the datasheet, for a range of+-8g, we have to divide the raw values by 4096*/    
  Acc_rawX=(Wire.read()<<8|Wire.read())/4096.0 ; //each value needs two registres
  Acc_rawY=(Wire.read()<<8|Wire.read())/4096.0 ;
  Acc_rawZ=(Wire.read()<<8|Wire.read())/4096.0 ; 
 /*Now in order to obtain the Acc angles we use euler formula with acceleration values
 after that we substract the error value found before*/  
 /*---X---*/
 Acc_angle_x = (atan((Acc_rawY)/sqrt(pow((Acc_rawX),2) + pow((Acc_rawZ),2)))*rad_to_deg) - Acc_angle_error_x;
 /*---Y---*/
 Acc_angle_y = (atan(-1*(Acc_rawX)/sqrt(pow((Acc_rawY),2) + pow((Acc_rawZ),2)))*rad_to_deg) - Acc_angle_error_y;    


 //////////////////////////////////////Total angle and filter/////////////////////////////////////
 /*---X axis angle---*/
 Total_angle_x = 0.98 *(Total_angle_x + Gyro_angle_x) + 0.02*Acc_angle_x;
 /*---Y axis angle---*/
 Total_angle_y = 0.98 *(Total_angle_y + Gyro_angle_y) + 0.02*Acc_angle_y;
   
 /*Uncoment the rest of the serial prines
 * I only print the Y angle value for this test */
 //Serial.print("Xº: ");
 //Serial.print(Total_angle_x);
 //Serial.print("   |   ");
 Serial.print("Yº: ");
 Serial.print(Total_angle_y);
 Serial.println(" ");
}

Welcome to the forum.

You are filling the I2C bus to the maximum, I don't know how well the MPU-6050 can deal with that. Could you add a delay ? For example in the for-statement that runs 200 times.

Can you give a link to the MPU-6050 module and can you show a photo with the wiring ?
Do you have the SCL and SDA wires next to each other in a flat ribbon cable ? (don't do that).

You could check the return value of Wire.endTransmission() and Wire.requestFrom() to check if you are still communicating with the sensor.

And yeah, since a year or so, there are problems with the SAMD and the I2C bus. It is yet unclear for me how and what is going on. Your XIAO has its own build environment which is a older copy of the Arduino build environment.

Your 'timePrev' and 'times' and 'elapsedTime' do not work. I don't know if that is the problem, but you should fix that. Always use "unsigned long" when dealing with a millis() value. The loop() has not much to do, I doubt if using the milliseconds is accurate.

You print something in the loop(), every time the loop() runs. That will fill the TX buffer of the Serial library. Once it is filled, your sketch will slow down by hundred times or more.
You also have a baudrate of 9600, that is so slow, even the dinosaurs used a higher baudrate.

Hi Koepel, first thank you so much for such a detailed reply I really appreciate it :). I tried a 100ms and a 250ms delay in the for loop to see what would happen and it made the readings much better. It seem to at least be trying to track the angle, but it is still very noisy and not as accurate as it was on the UNO.

 for(int i=0; i<200; i++)
    {
      Wire.beginTransmission(0x68);            //begin, Send the slave adress (in this case 68) 
      Wire.write(0x43);                        //First adress of the Gyro data
      Wire.endTransmission(false);
      Wire.requestFrom(0x68,4,true);           //We ask for just 4 registers 
         
      Gyr_rawX=Wire.read()<<8|Wire.read();     //Once again we shif and sum
      Gyr_rawY=Wire.read()<<8|Wire.read();
   
      /*---X---*/
      Gyro_raw_error_x = Gyro_raw_error_x + (Gyr_rawX/32.8); 
      /*---Y---*/
      Gyro_raw_error_y = Gyro_raw_error_y + (Gyr_rawY/32.8);
      if(i==199)
      {
        Gyro_raw_error_x = Gyro_raw_error_x/200;
        Gyro_raw_error_y = Gyro_raw_error_y/200;
        gyro_error=1;
      }
      delay(100);
    }

Here is a link to the module I am using


VCC is going to the 5V pin, GND to GND, SCL to A5(SCL), SDA to A4(SDA), AD0 to GND, INT to A2(Currently that does nothing, I will use that later when I start adding more sensors).

I'm not sure how to check the Wire.endTransmission() value, I have heard something about SAMD processors always returning a value of True, but I don't know if that would mess with this code because I'm specifying it's value in the brackets Wire.endTransmission(false/true), what do you think?

Oh thank you I changed the time values to unsigned long.

Lmao okay, I increased the baudrate to 19200. Honestly I always just use 9600 because I don't understand what it is and it usually doesn't matter. The tx buffer thing is good to know, but I need to print the values so I can watch the serial monitor/plotter and check if the angles it outputs look right.

I made a mistake. When the Serial port is actually a USB-serial-port, then the baudrate is ignored and the data travels as fast as the USB interface (the USB hardware and USB software in the SAMD processor) can do.

O no :scream: SDA and SCL are next to each other in a flat ribbon cable. Please split all the wires.

The data should be the same as with the Uno. Perhaps once in a while the I2C communication goes bad and wrong data is used.

To release the pressure on the I2C bus for some chips, just 1ms delay after a Wire.endTransmission() should be enough. Most chips don't mind constant I2C activity and some need a lot of time, but then it is in the datasheet.

When using the MPU-6050 module with 3.3V signals, powering VCC with 5V is okay since the module has a voltage regulator :+1:

Say that 100 samples per second is okay, then you could add a delay of 5ms to 10ms at the end of the Arduino loop().

You can read in the reference that Wire.endTransmission() returns an error code (zero is okay, not zero is not okay) and Wire.requestFrom() returns the number of bytes that are received.

The "false" with Wire.endTransmission(false) makes a repeated start for the next Wire.requestFrom(). That is normal for most I2C devices, but the MPU-6050 does not care much about it. The Wire.requestFrom(true) is to tell that the I2C session has finished. So that is all okay.

Ahaha okay I separated the SDA and SCL cables as much as possible. I added 1ms delays after each Wire.endTransmission() and added a 10ms delay as the end of the void loop().

I'm not sure if it's the best way to do it but I added this if else statement to make sure the Wire.endTransmission(false) was returning 0 and it is.

if (Wire.endTransmission(false) != 0){
      Serial.println("Error not working");
    }
    else{
      Serial.println("All good");
    }

As for checking the Wire.requestFrom(); I tried using Wire.peek() and it returns 255 but I dont actually know if that is what you meant for me to look for.

 Wire.requestFrom(0x68,4,true);           //We ask for 4 registers  
    Serial.println (Wire.peek()); 

I also tried this:

Wire.requestFrom(0x68,4,true);           //We ask for just 4 registers  
    Serial.println (Wire.requestFrom(0x68,4,true)); 

and that returns a value of 4. Are either of those the right way to look for the number of bytes received? Sorry I don't know much about I2C communication.

As another update. The original code I posted works well with an arduino nano V3.0 but outputs similar results to the seeduino XIAO when I try and use it on an Arduino Nano 33 BLE. I don't know if that is helpful, but maybe there's an important difference between these boards that I don't know.
Works for: Uno, Nano V3.0
Currently not working for: Seeduino XIAO, Nano 33 BLE

Although we never fixed the original code I posted here. I have found an example sketch that works well with the Seeduino XIAO. XTronical identified it for use with the nano but it does also work with the XIAO. You need to get the MPU6050_light.h library and follow his example here: https://www.xtronical.com/mpu6050/

1 Like

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