// Last Update: 23-06-2021
// MPU-6050 Accelerometer Calibration 24 positions at 45 degrees. Based on [Ref.1] Chapter 7: "12 parameter calibration". 
  #include 
  #define I2C Wire         //    Teensy
  
  #define MPU_addr (0x68)  // I2C address of the MPU-6050
// Variables used for data-logging
   int i, m;
   int acc[7];          // MPU-6050  
   int Sensitivity;     // LSB/g 
   
   float X[24][4];     // X = input matrix m x p (m=rows=24, p=columns=4)
                       // m=24 defines number of measurements           
                       //X[m][0] = stores measured value acc.X-component
                       //X[m][1] = stores measured value acc.Y-component
                       //X[m][2] = stores measured value acc.Z-component
                       //X[m][3] = 1, this 1 is stored for the Least-Square calculation
 
   float Yx[24];       //True X-component of the m'th gravitational field measurement.              
   float Yy[24];       //True Y-component of the m'th gravitational field measurement
   float Yz[24];       //True Z-component of the m'th gravitational field measurement
   float pi=M_PI;      //pi=3.14159;
   float Check_X_Y[24][6];
   
  // The following variables are used for the calibration analysis: 
   float XT[4][24];            // XT is used for storing transposed input matrix p x m (p=rows=4,m=columns=24)
   float Xh1[4][4];            // auxiliary matrix Xh1=(XT x X)=>p x p=>4 x 4 matrix
   float Xh2[4][4];            // auxiliary matrix Xh2 for temp.copy of Xh1 =>p x p =>4 x 4 matrix
   float Xh3[4][4];            // auxiliary matrix used for inversion check =>p x p =>4 x 4 matrix 
   float Xh4[4][24];           // auxiliary matrix 
 
   float Wx[4];                // vector always 4 components
   float Wy[4];                // vector always 4 components
   float Wz[4];                // vector always 4 components
   float CaliMatrix[3][3];     //Calibration Matrix is a 3 x 3 matrix
   float CaliVec[3];           //shift vector always 3 components
   float Yvec[3];              //Vector stores x.y.z Euler components
   float Mvec[3];              //Vector with raw x,y,z measurements
   float P[3];                 // Residual vector always 3 components
   float Ph[3];                // Residual help-vector always 3 components
   float r[3];                 // Residual vector always 3 components
   float Residu[3];            // Residu x,y,z values
   
  void setup(void) 
 {
   I2C.begin();          // join i2c bus (address optional for master) 
   Serial.begin(9600);   // start serial for output. Make sure you set your Serial Monitor to the same! 
//++++++++++++ Configure MPU-6050 Accel en Gyro +++++++++++++++
   set_MPU_6050_registers();
  
   Serial.println("MPU-6050 Accelerometer Calibration"); 
   Serial.println("");   
 }
 
  void loop()
 {
// 24 steps of data-aquisition. 
//========================== 1: X-45deg Up, Y-Up, Z=0 =====================================    
  Serial.println("1 of 24: Set Cube in X-45deg Up, Y-Up, Z=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m=0; 
  Yx[m]=-0.707107;     
  Yy[m]=-0.707107;     
  Yz[m]= 0;            
  
// Fill Row 1 of input matrix X[m x p] (m=rows=24,p=columns=4)
   read_Raw_Accel(1);
   Check_X_Y[m][0]=X[m][0];
   Check_X_Y[m][1]=X[m][1];
   Check_X_Y[m][2]=X[m][2];
   Check_X_Y[m][3]=Yx[m];
   Check_X_Y[m][4]=Yy[m];
   Check_X_Y[m][5]=Yz[m];
  while (Serial.available())
    {
     Serial.read();  // clear the input buffer
    }
  Serial.println("X_45deg UP, Y_UP, Z=0 is done...");
  Serial.println(); 
    
//=========== 2: X-45deg-Up, Y-Down, Z=0 ======================   
  Serial.println("2 of 24: Set Cube in X-45deg-Up, Y-Down, Z=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= -0.707107;     
  Yy[m]= +0.707107;     
  Yz[m]= 0; 
// Fill Row 2 of input matrix X[m x p] (m=rows=24,p=columns=4)
   read_Raw_Accel(2);
   Check_X_Y[m][0]=X[m][0];
   Check_X_Y[m][1]=X[m][1];
   Check_X_Y[m][2]=X[m][2];
   Check_X_Y[m][3]=Yx[m];
   Check_X_Y[m][4]=Yy[m];
   Check_X_Y[m][5]=Yz[m];
    
   while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("X-45deg-Up, Y-Down, Z=0 is done...");
  Serial.println();   
//============= 3: X-45deg-Down, Y-Up, Z=0 ====================     
  Serial.println("3 of 24: Set Cube in X-45deg-Down, Y-Up, Z=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= +0.707107;     
  Yy[m]= -0.707107;     
  Yz[m]= 0;             
  
  // Fill Row 3 of input matrix X[m x p] (m=rows=24,p=columns=4)
     read_Raw_Accel(3);
     Check_X_Y[m][0]=X[m][0];
     Check_X_Y[m][1]=X[m][1];
     Check_X_Y[m][2]=X[m][2];
     Check_X_Y[m][3]=Yx[m];
     Check_X_Y[m][4]=Yy[m];
     Check_X_Y[m][5]=Yz[m];
    
    while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("X-45deg-Down, Y-Up, Z=0 is done...");
  Serial.println();         
//============= 4: X-45deg-Down, Y-Down, Z=0 ==================    
  Serial.println("4 of 24: Set Cube in X-45deg-Down, Y-Down, Z=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= +0.707107;   
  Yy[m]= +0.707107;    
  Yz[m]= 0;    
  // Fill Row 4 of input matrix X[m x p] (m=rows=24,p=columns=4)
     read_Raw_Accel(4);
     Check_X_Y[m][0]=X[m][0];
     Check_X_Y[m][1]=X[m][1];
     Check_X_Y[m][2]=X[m][2];
     Check_X_Y[m][3]=Yx[m];
     Check_X_Y[m][4]=Yy[m];
     Check_X_Y[m][5]=Yz[m];
     
  while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("X-45deg-Down, Y-Down, Z=0 is done...");
  Serial.println();
     
//================ 5: Y-45deg-Up, X-Up, Z=0 ===================       
 Serial.println("5 of 24: Set Cube in Y-45deg-Up, X-Up, Z=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]=-0.707107;
  Yy[m]=-0.707107;
  Yz[m]= 0;    
 
  // Fill Row 5 of input matrix X[m x p] (m=rows=24,p=columns=4)
     read_Raw_Accel(5);
     Check_X_Y[m][0]=X[m][0];
     Check_X_Y[m][1]=X[m][1];
     Check_X_Y[m][2]=X[m][2];
     Check_X_Y[m][3]=Yx[m];
     Check_X_Y[m][4]=Yy[m];
     Check_X_Y[m][5]=Yz[m];
     
  while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("Y-45deg-Up, X-Up, Z=0 is done..."); 
  Serial.println();
//============== 6: Y-45deg-Up, X-Down, Z=0 ==================
  Serial.println("6 of 24: Set Cube in Y-45deg-Up, X-Down, Z=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= +0.707107;  
  Yy[m]= -0.707107;   
  Yz[m]= 0;  
  // Fill Row 6 of input matrix X[m x p] (m=rows=24,p=columns=4)
     read_Raw_Accel(6);
     Check_X_Y[m][0]=X[m][0];
     Check_X_Y[m][1]=X[m][1];
     Check_X_Y[m][2]=X[m][2];
     Check_X_Y[m][3]=Yx[m];
     Check_X_Y[m][4]=Yy[m];
     Check_X_Y[m][5]=Yz[m];
     
  while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("Y-45deg-Up, X-Down, Z=0 is done..."); 
  Serial.println();
//============== 7: Y_45deg Down, X_Up, Z=0 ==================    
  Serial.println("7 of 24: Set Cube in Y_45deg Down, X_Up, Z=0  position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= -0.707107;     
  Yy[m]= +0.707107;     
  Yz[m]= 0;     
 
 // Fill Row 7 of input matrix X[m x p] (m=rows=24,p=columns=4)
    read_Raw_Accel(7);  
    Check_X_Y[m][0]=X[m][0];
    Check_X_Y[m][1]=X[m][1];
    Check_X_Y[m][2]=X[m][2];
    Check_X_Y[m][3]=Yx[m];
    Check_X_Y[m][4]=Yy[m];
    Check_X_Y[m][5]=Yz[m];
     
   while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
   Serial.println("Y_45deg Down, X_Up, Z=0 is done...");
   Serial.println(); 
    
//============= 8: Y-45deg-Down, X-Down, Z=0 ==================   
  Serial.println("8 of 24: Set Cube in Y-45deg-Down, X-Down, Z=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= +0.707107;
  Yy[m]= +0.707107;
  Yz[m]= 0;     
 
 // Fill Row 8 of input matrix X[m x p] (m=rows=24,p=columns=4)
     read_Raw_Accel(8);  
     Check_X_Y[m][0]=X[m][0];
     Check_X_Y[m][1]=X[m][1];
     Check_X_Y[m][2]=X[m][2];
     Check_X_Y[m][3]=Yx[m];
     Check_X_Y[m][4]=Yy[m];
     Check_X_Y[m][5]=Yz[m];
     
  while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("Y-45deg-Down, X-Down, Z=0 is done...");
  Serial.println(); 
  
//============ 9: X-45deg-Up, Z-Up, Y=0 =======================     
       Serial.println("9 of 24: Set Cube in X-45deg-Up, Z-Up, Y=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= -0.707107;
  Yy[m]= 0;     
  Yz[m]= -0.707107; 
 // Fill Row 9 of input matrix X[m x p] (m=rows=24,p=columns=4)
     read_Raw_Accel(9); 
     Check_X_Y[m][0]=X[m][0];
     Check_X_Y[m][1]=X[m][1];
     Check_X_Y[m][2]=X[m][2];
     Check_X_Y[m][3]=Yx[m];
     Check_X_Y[m][4]=Yy[m];
     Check_X_Y[m][5]=Yz[m];
     
  while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("X-45deg-Up, Z-Up, Y=0 is done...");
  Serial.println();         
//============= 10: X-45deg-Up, Z-Down, Y=0 ==================    
  Serial.println("10 of 24: Set Cube in X-45deg-Up, Z-Down, Y=0  position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= -0.707107;
  Yy[m]= 0;     
  Yz[m]= +0.707107; 
 // Fill Row 10 of input matrix X[m x p] (m=rows=24,p=columns=4)
     read_Raw_Accel(10); 
     Check_X_Y[m][0]=X[m][0];
     Check_X_Y[m][1]=X[m][1];
     Check_X_Y[m][2]=X[m][2];
     Check_X_Y[m][3]=Yx[m];
     Check_X_Y[m][4]=Yy[m];
     Check_X_Y[m][5]=Yz[m];
     
  while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("X-45deg-Up, Z-Down, Y=0 is done...");
  Serial.println();
     
//============= 11: X-45deg-Down, Z-Up, Y=0 ===================       
  Serial.println("11 of 24: Set Cube in X-45deg-Down, Z-Up, Y=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= +0.707107;
  Yy[m]= 0;     
  Yz[m]= -0.707107;
// Fill Row 11 of input matrix X[m x p] (m=rows=24,p=columns=4)
     read_Raw_Accel(11); 
     Check_X_Y[m][0]=X[m][0];
     Check_X_Y[m][1]=X[m][1];
     Check_X_Y[m][2]=X[m][2];
     Check_X_Y[m][3]=Yx[m];
     Check_X_Y[m][4]=Yy[m];
     Check_X_Y[m][5]=Yz[m];
     
  while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("X-45deg-Down, Z-Up, Y=0 is done..."); 
  Serial.println();
//============= 12: X-45deg-Down, Z-Down, Y=0 =================
  Serial.println("12 of 24: Set Cube in X-45deg-Down, Z-Down, Y=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= +0.707107;  
  Yy[m]= 0;     
  Yz[m]= +0.707107; 
 // Fill Row 12 of input matrix X[m x p] (m=rows=24,p=columns=4)
     read_Raw_Accel(12); 
     Check_X_Y[m][0]=X[m][0];
     Check_X_Y[m][1]=X[m][1];
     Check_X_Y[m][2]=X[m][2];
     Check_X_Y[m][3]=Yx[m];
     Check_X_Y[m][4]=Yy[m];
     Check_X_Y[m][5]=Yz[m];
     
  while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("X-45deg-Down, Z-Down, Y=0 is done..."); 
  Serial.println();
//============= 13: Z-45deg-Up, X-Up, Y=0 =====================   
  Serial.println("13 of 24: Set Cube in Z-45deg-Up, X-Up, Y=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= -0.707107;
  Yy[m]= 0;     
  Yz[m]=-0.707107; 
 // Fill Row 13 of input matrix X[m x p] (m=rows=24,p=columns=4)
     read_Raw_Accel(13); 
     Check_X_Y[m][0]=X[m][0];
     Check_X_Y[m][1]=X[m][1];
     Check_X_Y[m][2]=X[m][2];
     Check_X_Y[m][3]=Yx[m];
     Check_X_Y[m][4]=Yy[m];
     Check_X_Y[m][5]=Yz[m];
     
  while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("Z-45deg-Up, X-Up, Y=0 is done...");
  Serial.println();   
//============= 14: Z-45deg-Up, X-Down, Y=0 ===================     
  Serial.println("14 of 24: Set Cube in Z-45deg-Up, X-Down, Y=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= +0.707107; 
  Yy[m]= 0;     
  Yz[m]= -0.707107; 
 // Fill Row 14 of input matrix X[m x p] (m=rows=24,p=columns=4)
     read_Raw_Accel(14);   
     Check_X_Y[m][0]=X[m][0];
     Check_X_Y[m][1]=X[m][1];
     Check_X_Y[m][2]=X[m][2];
     Check_X_Y[m][3]=Yx[m];
     Check_X_Y[m][4]=Yy[m];
     Check_X_Y[m][5]=Yz[m];
  while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("Z-45deg-Up, X-Down, Y=0 is done...");
  Serial.println();         
//============ 15: Z-45deg-Down, X-Up, Y=0 ====================    
  Serial.println("15 of 24: Set Cube in Z-45deg-Down, X-Up, Y=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= -0.707107; 
  Yy[m]= 0;     
  Yz[m]= +0.707107; 
 // Fill Row 15 of input matrix X[m x p] (m=rows=24,p=columns=4)
     read_Raw_Accel(15); 
     Check_X_Y[m][0]=X[m][0];
     Check_X_Y[m][1]=X[m][1];
     Check_X_Y[m][2]=X[m][2];
     Check_X_Y[m][3]=Yx[m];
     Check_X_Y[m][4]=Yy[m];
     Check_X_Y[m][5]=Yz[m];
     
  while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("Z-45deg-Down, X-Up, Y=0 is done...");
  Serial.println();
     
//========== 16: Z-45deg-Down, X-Down, Y=0 ===================       
  Serial.println("16 of 24: Set Cube in Z-45deg-Down, X-Down, Y=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= +0.707107;     
  Yy[m]= 0;            
  Yz[m]= +0.707107;     
// Fill Row 16 of input matrix X[m x p] (m=rows=24,p=columns=4)
     read_Raw_Accel(16); 
     Check_X_Y[m][0]=X[m][0];
     Check_X_Y[m][1]=X[m][1];
     Check_X_Y[m][2]=X[m][2];
     Check_X_Y[m][3]=Yx[m];
     Check_X_Y[m][4]=Yy[m];
     Check_X_Y[m][5]=Yz[m];
     
  while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("Z-45deg-Down, X-Down, Y=0 is done..."); 
  Serial.println();
//============ 17: Y-45deg-Up, Z-Up, X=0 ======================
  Serial.println("17 of 24: Set Cube in Y-45deg-Up, Z-Up, X=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= 0; 
  Yy[m]= -0.707107;    
  Yz[m]= -0.707107;    
 
 // Fill Row 17 of input matrix X[m x p] (m=rows=24,p=columns=4)
     read_Raw_Accel(17); 
     Check_X_Y[m][0]=X[m][0];
     Check_X_Y[m][1]=X[m][1];
     Check_X_Y[m][2]=X[m][2];
     Check_X_Y[m][3]=Yx[m];
     Check_X_Y[m][4]=Yy[m];
     Check_X_Y[m][5]=Yz[m];
     
  while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("Y-45deg-Up, Z-Up, X=0 is done..."); 
  Serial.println();
//============ 18: Y-45deg-Up, Z-Down, X=0 ====================       
 Serial.println("18 of 24: Set Cube in Y-45deg-Up, Z-Down, X=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= 0;     
  Yy[m]= -0.707107;
  Yz[m]= +0.707107;
 
  // Fill Row 18 of input matrix X[m x p] (m=rows=24,p=columns=4)
     read_Raw_Accel(18); 
     Check_X_Y[m][0]=X[m][0];
     Check_X_Y[m][1]=X[m][1];
     Check_X_Y[m][2]=X[m][2];
     Check_X_Y[m][3]=Yx[m];
     Check_X_Y[m][4]=Yy[m];
     Check_X_Y[m][5]=Yz[m];
     
  while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("Y-45deg-Up, Z-Down, X=0 is done..."); 
  Serial.println();
//============= 19: Y-45deg-Down, Z-Up, X=0 ===================
  Serial.println("19 of 24: Set Cube in Y-45deg-Down, Z-Up, X=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= 0; 
  Yy[m]= +0.707107; 
  Yz[m]= -0.707107; 
   
 // Fill Row 19 of input matrix X[m x p] (m=rows=24,p=columns=4)
     read_Raw_Accel(19);  
     Check_X_Y[m][0]=X[m][0];
     Check_X_Y[m][1]=X[m][1];
     Check_X_Y[m][2]=X[m][2];
     Check_X_Y[m][3]=Yx[m];
     Check_X_Y[m][4]=Yy[m];
     Check_X_Y[m][5]=Yz[m];
     
  while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("Y-45deg-Down, Z-Up, X=0 is done..."); 
  Serial.println();
//=========== 20: Y-45deg-Down, Z-Down, X=0 ==================   
  Serial.println("20 of 24: Set Cube in Y-45deg-Down, Z-Down, X=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= 0;     //-sin(90*pi/180);                    //Euler hoeken in radians
  Yy[m]= +0.707107;     // sin(0*pi/180)*cos(0*pi/180);       //Euler hoeken in radians
  Yz[m]= +0.707107;     // cos(0*pi/180)*cos(0*pi/180);       //Euler hoeken in radians
// Fill Row =20 van input matrix X[m x p] (m=rows=24,p=columns=4)
   read_Raw_Accel(20);  
   Check_X_Y[m][0]=X[m][0];
   Check_X_Y[m][1]=X[m][1];
   Check_X_Y[m][2]=X[m][2];
   Check_X_Y[m][3]=Yx[m];
   Check_X_Y[m][4]=Yy[m];
   Check_X_Y[m][5]=Yz[m];
   
  while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("Y-45deg-Down, Z-Down, X=0 is done...");
  Serial.println();   
//============ 21: Z-45deg-Up, Y-Up, X=0 =====================     
  Serial.println("21 of 24: Set Cube in Z-45deg-Up, Y-Up, X=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= 0;     
  Yy[m]= -0.707107; 
  Yz[m]= -0.707107; 
 // Fill Row 21 of input matrix X[m x p] (m=rows=24,p=columns=4)
   read_Raw_Accel(21); 
   Check_X_Y[m][0]=X[m][0];
   Check_X_Y[m][1]=X[m][1];
   Check_X_Y[m][2]=X[m][2];
   Check_X_Y[m][3]=Yx[m];
   Check_X_Y[m][4]=Yy[m];
   Check_X_Y[m][5]=Yz[m];
   
  while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("Z-45deg-Up, Y-Up, X=0 is done...");
  Serial.println();         
//=============== 22: Z-45deg-Up, Y-Down, X=0 =================    
  Serial.println("22 of 24: Set Cube in Z-45deg-Up, Y-Down, X=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= 0;       
  Yy[m]= +0.707107; 
  Yz[m]= -0.707107; 
// Fill Row 22 of input matrix X[m x p] (m=rows=24,p=columns=4)
   read_Raw_Accel(22);  
   Check_X_Y[m][0]=X[m][0];
   Check_X_Y[m][1]=X[m][1];
   Check_X_Y[m][2]=X[m][2];
   Check_X_Y[m][3]=Yx[m];
   Check_X_Y[m][4]=Yy[m];
   Check_X_Y[m][5]=Yz[m];
   
  while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("Z-45deg-Up, Y-Down, X=0 is done...");
  Serial.println();
     
//============== 23: Z-45deg-Down, Y-Up, X=0 ==================       
 Serial.println("23 of 24: Set Cube in Z-45deg-Down, Y-Up, X=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= 0;      
  Yy[m]= -0.707107;
  Yz[m]= +0.707107;
 
 // Fill Row 23 of input matrix X[m x p] (m=rows=24,p=columns=4)
   read_Raw_Accel(23);  
   Check_X_Y[m][0]=X[m][0];
   Check_X_Y[m][1]=X[m][1];
   Check_X_Y[m][2]=X[m][2];
   Check_X_Y[m][3]=Yx[m];
   Check_X_Y[m][4]=Yy[m];
   Check_X_Y[m][5]=Yz[m];
   
  while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("Z-45deg-Down, Y-Up, X=0 is done..."); 
  Serial.println();
//============ 24: Z-45deg-Down, Y-Down, X=0 ==================
Serial.println("24 of 24: Set Cube in Z-45deg-Down, Y-Down, X=0 position and Type any key when ready..."); 
  while (!Serial.available()){}  // wait for a character
  m++;
  Yx[m]= 0;       
  Yy[m]= +0.707107; 
  Yz[m]= +0.707107; 
 // Fill Row 24 of input matrix X[m x p] (m=rows=24,p=columns=4)
   read_Raw_Accel(24);  
   Check_X_Y[m][0]=X[m][0];
   Check_X_Y[m][1]=X[m][1];
   Check_X_Y[m][2]=X[m][2];
   Check_X_Y[m][3]=Yx[m];
   Check_X_Y[m][4]=Yy[m];
   Check_X_Y[m][5]=Yz[m];
   
  while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
  Serial.println("Z-45deg-Down, Y-Down, X=0 is done..."); 
  Serial.println();
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//==================== All 24 Cube positions are recorded ===============================
    Serial.println();
    Serial.println("All 24 Cube positions done. Recorded Data are printed for consistency check"); 
    Serial.println();
    MatrixMathPrint((float*)Check_X_Y,24,6,"Check the consistency of input data.");
    
    Serial.println();
    Serial.println("Type any key to Start Calibration..."); 
    while (!Serial.available()){}  // wait for a character
    while (Serial.available())
    {
      Serial.read();  // clear the input buffer
    }
//======================== Start of Calibration Analysis ================================    
    
    MatrixMathTranspose((float*)X,24,4,(float*)XT);   //XT is transposed Data Matrix X
    // MatrixMathPrint((float*)XT,4,24,"XT is the transposed Data matrix X");
        
    MatrixMathMultiply((float*)XT,(float*)X,4,24,4,(float*)Xh1); //calculate (XT x X) and store in Xh1
   // MatrixMathPrint((float*)Xh1,4,4,"XT x X");
    MatrixMathCopy((float*)Xh1,4,4,(float*)Xh2);     //create a temp.copy Xh2 of matrix Xh1=(XT x X)
    Serial.println();
    MatrixMathInvert((float*)Xh1,4);                //calculate  inverse van matrix Xh1=(XT x X)
    // MatrixMathPrint((float*)Xh1,4,4,"Xh1 = (XT x X)**-1 inverted matrix");
   
    // Check the matrix inversion using the temp.copy Xh2 and store the resut in Xh3
    MatrixMathMultiply((float*)Xh2,(float*)Xh1,4,4,4,(float*)Xh3); 
    MatrixMathPrint((float*)Xh3,4,4,"Matrix inversion Check");
    MatrixMathMultiply((float*)Xh1,(float*)XT,4,4,24,(float*)Xh4); //calculate ((XT x X)**-1) x XT and store result in Xh4
    //MatrixMathPrint((float*)Xh4,4,6,"Xh4=((XT x X)**-1) x XT");
    MatrixMathMultiply((float*)Xh4,(float*)Yx,4,24,1,(float*)Wx); //calculate Wx-Row
    // MatrixMathPrint((float*)Wx,4,1, "Wx-Row");
    MatrixMathMultiply((float*)Xh4,(float*)Yy,4,24,1,(float*)Wy); //calculate Wy-Row
    // MatrixMathPrint((float*)Wy,4,1, "Wy-Row");
    MatrixMathMultiply((float*)Xh4,(float*)Yz,4,24,1,(float*)Wz); //calculate Wz-Row
    // MatrixMathPrint((float*)Wz,4,1, "Wz-Row");
    for (i=0;i < 3;i++)
    {
    CaliMatrix[0][i]=Wx[i];
    }
    for (i=0;i < 3;i++)
    {
    CaliMatrix[1][i]=Wy[i];
    }
    for (i=0;i < 3;i++)
    {
    CaliMatrix[2][i]=Wz[i];
    }
    MatrixMathPrint((float*)CaliMatrix,3,3,"The Calibration Matrix");
    
    CaliVec[0]=Wx[3];
    CaliVec[1]=Wy[3];
    CaliVec[2]=Wz[3];
    MatrixMathPrint((float*)CaliVec,3,1, "The Calibration shift Vector");
    //Quality check of the results.
    
    Serial.println();
    Serial.println("Calculation of the x,y,z residu components as Quality Check");
    Residu[0]=0;
    Residu[1]=0;
    Residu[2]=0;
    
    for (i=0;i < 24;i++)
    {
    Mvec[0]=X[i][0];
    Mvec[1]=X[i][1];
    Mvec[2]=X[i][2];
    MatrixMathMultiply((float*)CaliMatrix,(float*)Mvec,3,3,1,(float*)Ph);
   
    r[0]=Yx[i]-Ph[0]-CaliVec[0];
    r[1]=Yy[i]-Ph[1]-CaliVec[1];
    r[2]=Yz[i]-Ph[2]-CaliVec[2];
     
    Residu[0]=Residu[0]+r[0]*r[0];
    Residu[1]=Residu[1]+r[1]*r[1];
    Residu[2]=Residu[2]+r[2]*r[2];
    }
    
    MatrixMathPrint((float*)Residu,3,1, "Square of residu-vector x,y,z as quality measure of calibration");
    Serial.println();
    Serial.println();
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//++++++++++++++++ Start Supporting Functions +++++++++++++++++++++++++++++++
  void read_Raw_Accel(int Row)
{
  delay(500);   // only read every 0,5 seconds 
  int  xsum=0;
  int  ysum=0;
  int  zsum=0;
  float Gx,Gy,Gz;
  int j;
  for (j=0; j < 50 ; j++)
  {
  readAccel(acc);    // read the raw globals x/y/z    
  xsum=xsum+acc[0];
  ysum=ysum+acc[1];
  zsum=zsum+acc[2];
  delay(5);
  }
 
  Gx=(xsum/j) ; //average raw value in bitvalues
  Gy=(ysum/j) ; //average raw value in bitvalues
  Gz=(zsum/j) ; //average raw value in bitvalues
  
  //Convert the accelerometer value to G's. 
  //Sensitivity=4096  LSB/g bij +/-8g full Scale range
  // X[m][p] = input matrix m x p (m=rows=24,p=columns=4)
  
  Row=Row-1;
  X[Row][0] = (Gx / Sensitivity);  //raw averaged value in g 
  X[Row][1] = (Gy / Sensitivity);  //raw averaged value in g
  X[Row][2] = (Gz / Sensitivity);  //raw averaged value in g 
  X[Row][3] = 1;
   Serial.print("Yx = "); 
   Serial.print(Yx[Row],4); 
   Serial.print(",  ");
   Serial.print("Yy = "); 
   Serial.print(Yy[Row],4); 
   Serial.print(",  ");   
   Serial.print("Yz = "); 
   Serial.print(Yz[Row],4); 
   Serial.println("  ");
   
   Serial.print("X-Column 1: "); 
   Serial.print(X[Row][0],2); 
   Serial.print(",  ");
   Serial.print("X-Column 2: "); 
   Serial.print(X[Row][1],2 ); 
   Serial.print(",  ");   
   Serial.print("X-Column 3: "); 
   Serial.print(X[Row][2],2 ); 
   Serial.print(",  ");
   Serial.print("X-Column 4: "); 
   Serial.println(X[Row][3]);
}
//===================================================================================
   void readAccel(int * result)
 { 
// Note: Bij DUE is int 4 byes and short is 2 bytes  
// Read from MPU-6050 Accelerometer, process 6 values, dicard remaining 8
   int regAddress = 0x3B;    //first axis-acceleration-data register on the ADXL345
   byte howManyBytesToRead = 14;   //uint8_t means: give me an unsigned int of exactly 8 bits.  
   byte _buff[14]; 
   short readings[7];  //short is 16bits=2bytes
   readFrom(MPU_addr, regAddress, howManyBytesToRead, _buff); //read the acceleration data from the sensor 
// MPU6050 reading 
  readings[0] = ((_buff[0]  <  <  8) | _buff[1]);   // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
  readings[1] = ((_buff[2]  <  <  8) | _buff[3]);   // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  readings[2] = ((_buff[4]  <  <  8) | _buff[5]);   // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  result[0] = -(int)readings[0];    //bit-values, minus to get the g-sign right
  result[1] = -(int)readings[1];    //bit-values, minus to get the g-sign right
  result[2] = -(int)readings[2];    //bit-values, minus to get the g-sign right
 } 
//===================================================================================
 
 // Reads num bytes starting from address register on MPU_addr in to _buff array 
   void readFrom(int DEVICE, byte address, int num, byte _buff[]) { 
   I2C.beginTransmission(DEVICE); // start transmission to device  
   I2C.write(address);             // sends address to read from 
   I2C.endTransmission();         // end transmission 
 
   I2C.beginTransmission(DEVICE); // start transmission to device 
   I2C.requestFrom(DEVICE, num);    // request 6 bytes from device 
 
   int i = 0; 
   while(I2C.available())         // device may send less than requested (abnormal) 
   {  
     _buff[i] = I2C.read();    // receive a byte 
     i++; 
   } 
   I2C.endTransmission();         // end transmission 
 } 
//*******************************************************************************
//********* Various Matrix Functions **************************
//***************** Matrix Printing  ************************************
// Uses tabs to separate numbers under assumption printed float width won't cause problems
// void MatrixMath::Print(float* A, int m, int n, String label){
  void MatrixMathPrint(float* A, int m, int n, String label){
 // A = input matrix (m x p)
 // m = number of rows in A
 // p = number of columns in A 
    
  int i,j;
  Serial.println();
  Serial.println(label);
  for (i=0; i < m; i++){
    for (j=0;j < n;j++){
      Serial.print(A[n*i+j],6);
      Serial.print("\t");
    }
    Serial.println();
  }
}
//*********** Matrix Copy **********************************************************
void MatrixMathCopy(float* A, int n, int m, float* B)
{
  int i, j, k;
  for (i=0;i < m;i++)
    for(j=0;j < n;j++)
    {
      B[n*i+j] = A[n*i+j];
    }
}
//************* Matrix Addition ***********************************
void MatrixMathAdd(float* A, float* B, int m, int n, float* C)
{
  // A = input matrix (m x n)
  // B = input matrix (m x n)
  // m = number of rows in A = number of rows in B
  // n = number of columns in A = number of columns in B
  // C = output matrix = A+B (m x n)
  int i, j;
  for (i=0;i < m;i++)
    for(j=0;j < n;j++)
      C[n*i+j]=A[n*i+j]+B[n*i+j];
}
//************* Matrix Subtraction ***********************************
void MatrixMathSubtract(float* A, float* B, int m, int n, float* C)
{
  // A = input matrix (m x n)
  // B = input matrix (m x n)
  // m = number of rows in A = number of rows in B
  // n = number of columns in A = number of columns in B
  // C = output matrix = A-B (m x n)
  int i, j;
  for (i=0;i < m;i++)
    for(j=0;j < n;j++)
      C[n*i+j]=A[n*i+j]-B[n*i+j];
}
//******* Matrix Multiplication C=AxB ********************************
void MatrixMathMultiply(float* A, float* B, int m, int p, int n, float* C)
{
  // A = input matrix (m x p)
  // B = input matrix (p x n)
  // m = number of rows in A
  // p = number of columns in A = number of rows in B
  // n = number of columns in B
  // C = output matrix = A*B (m x n)
  int i, j, k;
  for (i=0;i < m;i++)
    for(j=0;j < n;j++)
    {
      C[n*i+j]=0;
      for (k=0;k < p;k++)
        C[n*i+j]= C[n*i+j]+A[p*i+k]*B[n*k+j];
    }
}
//*********** Matrix Transpose ****************************************
void MatrixMathTranspose(float* A, int m, int n, float* C)
{
  // A = input matrix (m x n)
  // m = number of rows in A
  // n = number of columns in A
  // C = output matrix = the transpose of A (n x m)
  int i, j;
  for (i=0;i < m;i++)
    for(j=0;j < n;j++)
      C[m*j+i]=A[n*i+j];
}
//************* Matrix Inversion *****************************************
// * This function inverts a matrix based on the Gauss Jordan method.
// * Specifically, it uses partial pivoting to improve numeric stability.
// * The algorithm is drawn from those presented in 
//   NUMERICAL RECIPES: The Art of Scientific Computing.
// * The function returns 1 on success, 0 on failure.
// * NOTE: The argument is ALSO the result matrix, meaning the input matrix is REPLACED
int MatrixMathInvert(float* A, int n)
{
  // A = input matrix AND result matrix
  // n = number of rows = number of columns in A (n x n)
  int pivrow;   // keeps track of current pivot row
  int k,i,j;    // k: overall index along diagonal; i: row index; j: col index
  int pivrows[n]; // keeps track of rows swaps to undo at end
  float tmp;    // used for finding max value and making column swaps
  for (k = 0; k  <  n; k++)
  {
    // find pivot row, the row with biggest entry in current column
    tmp = 0;
    for (i = k; i  <  n; i++)
    {
      if (abs(A[i*n+k]) >= tmp) // 'Avoid using other functions inside abs()?'
      {
        tmp = abs(A[i*n+k]);
        pivrow = i;
      }
    }
    // check for singular matrix
    if (A[pivrow*n+k] == 0.0f)
    {
      Serial.println("Inversion failed due to singular matrix");
      return 0;
    }
    // Execute pivot (row swap) if needed
    if (pivrow != k)
    {
      // swap row k with pivrow
      for (j = 0; j  <  n; j++)
      {
        tmp = A[k*n+j];
        A[k*n+j] = A[pivrow*n+j];
        A[pivrow*n+j] = tmp;
      }
    }
    pivrows[k] = pivrow;  // record row swap (even if no swap happened)
    tmp = 1.0f/A[k*n+k];  // invert pivot element
    A[k*n+k] = 1.0f;    // This element of input matrix becomes result matrix
    // Perform row reduction (divide every element by pivot)
    for (j = 0; j  <  n; j++)
    {
      A[k*n+j] = A[k*n+j]*tmp;
    }
    // Now eliminate all other entries in this column
    for (i = 0; i  <  n; i++)
    {
      if (i != k)
      {
        tmp = A[i*n+k];
        A[i*n+k] = 0.0f;  // The other place where in matrix becomes result mat
        for (j = 0; j  <  n; j++)
        {
          A[i*n+j] = A[i*n+j] - A[k*n+j]*tmp;
        }
      }
    }
  }
  // Done, now need to undo pivot row swaps by doing column swaps in reverse order
  for (k = n-1; k >= 0; k--)
  {
    if (pivrows[k] != k)
    {
      for (i = 0; i  <  n; i++)
      {
        tmp = A[i*n+k];
        A[i*n+k] = A[i*n+pivrows[k]];
        A[i*n+pivrows[k]] = tmp;
      }
    }
  }
  return 1;
}
//**** End Sec2 Calib.Utility 1: Accelerometer Calibration ****