NOT FINISHED YET!!! My first try to get a self built fully working Quadrocopter based on an mbed, a self built frame and some other more or less cheap parts.

Dependencies:   mbed MODI2C

Files at this revision

API Documentation at this revision

Comitter:
maetugr
Date:
Mon Jun 10 13:22:46 2013 +0000
Parent:
33:fd98776b6cc7
Child:
35:2a9465fedb99
Commit message:
First version with new ESCs (they are working with the KK Board); some strange kicks in the behaviour of balancing, next step a logger

Changed in this revision

IMU_Filter/IMU_Filter.cpp Show annotated file Show diff for this revision Revisions of this file
IMU_Filter/IMU_Filter.h Show annotated file Show diff for this revision Revisions of this file
Mixer/Mixer.cpp Show annotated file Show diff for this revision Revisions of this file
PC/PC.cpp Show annotated file Show diff for this revision Revisions of this file
PC/PC.h Show annotated file Show diff for this revision Revisions of this file
RC/RC_Channel.cpp Show annotated file Show diff for this revision Revisions of this file
RC/RC_Channel.h Show annotated file Show diff for this revision Revisions of this file
Sensors/Alt/BMP085.cpp Show annotated file Show diff for this revision Revisions of this file
Sensors/Alt/BMP085.h Show annotated file Show diff for this revision Revisions of this file
Sensors/Comp/HMC5883.cpp Show annotated file Show diff for this revision Revisions of this file
Sensors/Comp/HMC5883.h Show annotated file Show diff for this revision Revisions of this file
Sensors/Gyro/L3G4200D.h Show annotated file Show diff for this revision Revisions of this file
Sensors/I2C_Sensor.cpp Show annotated file Show diff for this revision Revisions of this file
Sensors/I2C_Sensor.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IMU_Filter/IMU_Filter.cpp	Mon Jun 10 13:22:46 2013 +0000
@@ -0,0 +1,130 @@
+#include "IMU_Filter.h"
+
+// MARG
+#define PI 3.1415926535897932384626433832795
+#define Kp 2.0f         // proportional gain governs rate of convergence to accelerometer/magnetometer
+#define Ki 0.005f       // integral gain governs rate of convergence of gyroscope biases
+
+IMU_Filter::IMU_Filter()
+{
+    for(int i=0; i<3; i++)
+        angle[i]=0;
+    
+    // MARG
+    q0 = 1; q1 = 0; q2 = 0; q3 = 0;
+    exInt = 0; eyInt = 0; ezInt = 0;
+}
+
+void IMU_Filter::compute(float dt, const float * Gyro_data, const float * Acc_data)
+{
+    // calculate angles for each sensor
+    for(int i = 0; i < 3; i++)
+        d_Gyro_angle[i] = Gyro_data[i] *dt;
+    get_Acc_angle(Acc_data);
+    
+    // Complementary Filter
+    #if 0 // (formula from http://diydrones.com/m/discussion?id=705844%3ATopic%3A669858)
+        angle[0] = (0.999*(angle[0] + d_Gyro_angle[0]))+(0.001*(Acc_angle[0]));
+        angle[1] = (0.999*(angle[1] + d_Gyro_angle[1]))+(0.001*(Acc_angle[1]));// + 3)); // TODO Offset accelerometer einstellen
+        angle[2] += d_Gyro_angle[2]; // gyro only here TODO: Compass + 3D
+    #endif
+    
+    #if 0 // alte berechnung, vielleicht Accelerometer zu stark gewichtet
+        angle[0] += (Acc.angle[0] - angle[0])/50 + d_Gyro_angle[0];
+        angle[1] += (Acc.angle[1] - angle[1])/50 + d_Gyro_angle[1];// TODO Offset accelerometer einstellen
+        //tempangle += (Comp.get_angle() - tempangle)/50 + Gyro.data[2] *dt/15000000.0;
+        angle[2] = Gyro_angle[2]; // gyro only here
+    #endif
+    
+    #if 0 // neuer Test 2 (funktioniert wahrscheinlich nicht, denkfehler)
+        angle[0] += Gyro_angle[0] * 0.98 + Acc.angle[0] * 0.02;
+        angle[1] += Gyro_angle[1] * 0.98 + (Acc.angle[1] + 3) * 0.02; // TODO: Calibrierung Acc
+        angle[2] = Gyro_angle[2]; // gyro only here
+    #endif
+    
+    #if 0 // all gyro only
+        for(int i = 0; i < 3; i++)
+            angle[i] += d_Gyro_angle[i];
+    #endif
+    
+    // MARG
+    #if 1 // (from http://www.x-io.co.uk/open-source-imu-and-ahrs-algorithms/)
+        float radGyro[3];
+        
+        for(int i=0; i<3; i++)  // Radians per second
+            radGyro[i] = Gyro_data[i] * PI / 180;
+        
+        IMUupdate(dt/2, radGyro[0], radGyro[1], radGyro[2], Acc_data[0], Acc_data[1], Acc_data[2]);
+        
+        float rangle[3]; // calculate angles in radians from quternion output
+        rangle[0] = atan2(2*q0*q1 + 2*q2*q3, 1 - 2*(q1*q1 + q2*q2)); // from Wiki
+        rangle[1] = asin(2*q0*q2 - 2*q3*q1);
+        rangle[2] = atan2(2*q0*q3 + 2*q1*q2, 1 - 2*(q2*q2 + q3*q3));
+        
+        for(int i=0; i<3; i++)  // angle in degree
+            angle[i] = rangle[i] * 180 / PI;
+    #endif 
+}
+
+void IMU_Filter::get_Acc_angle(const float * Acc_data)
+{
+    // calculate the angles for roll and pitch (0,1)
+    float R = sqrt(pow((float)Acc_data[0],2) + pow((float)Acc_data[1],2) + pow((float)Acc_data[2],2));
+    float temp[3];
+    
+    temp[0] = -(Rad2Deg * acos(Acc_data[1] / R)-90);
+    temp[1] =   Rad2Deg * acos(Acc_data[0] / R)-90;
+    temp[2] =   Rad2Deg * acos(Acc_data[2] / R);
+    
+    for(int i = 0;i < 3; i++)
+        if (temp[i] > -360 && temp[i] < 360)
+            Acc_angle[i] = temp[i];
+}
+
+// MARG
+void IMU_Filter::IMUupdate(float halfT, float gx, float gy, float gz, float ax, float ay, float az)
+{
+    float norm;
+    float vx, vy, vz;
+    float ex, ey, ez;         
+    
+    // normalise the measurements
+    norm = sqrt(ax*ax + ay*ay + az*az);
+    if(norm == 0.0f) return;   
+    ax = ax / norm;
+    ay = ay / norm;
+    az = az / norm;      
+    
+    // estimated direction of gravity
+    vx = 2*(q1*q3 - q0*q2);
+    vy = 2*(q0*q1 + q2*q3);
+    vz = q0*q0 - q1*q1 - q2*q2 + q3*q3;
+    
+    // error is sum of cross product between reference direction of field and direction measured by sensor
+    ex = (ay*vz - az*vy);
+    ey = (az*vx - ax*vz);
+    ez = (ax*vy - ay*vx);
+    
+    // integral error scaled integral gain
+    exInt = exInt + ex*Ki;
+    eyInt = eyInt + ey*Ki;
+    ezInt = ezInt + ez*Ki;
+    
+    // adjusted gyroscope measurements
+    gx = gx + Kp*ex + exInt;
+    gy = gy + Kp*ey + eyInt;
+    gz = gz + Kp*ez + ezInt;
+    
+    // integrate quaternion rate and normalise
+    q0 = q0 + (-q1*gx - q2*gy - q3*gz)*halfT;
+    q1 = q1 + (q0*gx + q2*gz - q3*gy)*halfT;
+    q2 = q2 + (q0*gy - q1*gz + q3*gx)*halfT;
+    q3 = q3 + (q0*gz + q1*gy - q2*gx)*halfT;  
+    
+    // normalise quaternion
+    norm = sqrt(q0*q0 + q1*q1 + q2*q2 + q3*q3);
+    q0 = q0 / norm;
+    q1 = q1 / norm;
+    q2 = q2 / norm;
+    q3 = q3 / norm;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IMU_Filter/IMU_Filter.h	Mon Jun 10 13:22:46 2013 +0000
@@ -0,0 +1,27 @@
+// by MaEtUgR
+
+#ifndef IMU_FILTER_H
+#define IMU_FILTER_H
+
+#include "mbed.h"
+
+#define Rad2Deg         57.295779513082320876798154814105 // factor between radians and degrees of angle (180/Pi)
+
+class IMU_Filter
+{
+    public:
+        IMU_Filter();
+        void compute(float dt, const float * gyro_data, const float * acc_data);
+        float angle[3];                                 // calculated values of the position [0: x,roll | 1: y,pitch | 2: z,yaw]
+        
+        // MARG
+        float q0, q1, q2, q3;   // quaternion elements representing the estimated orientation
+        float exInt , eyInt , ezInt;  // scaled integral error
+        void IMUupdate(float halfT, float gx, float gy, float gz, float ax, float ay, float az);
+    private:
+        float d_Gyro_angle[3];
+        void get_Acc_angle(const float * Acc_data);
+        float Acc_angle[3];
+};
+
+#endif
\ No newline at end of file
--- a/Mixer/Mixer.cpp	Thu Apr 04 14:25:21 2013 +0000
+++ b/Mixer/Mixer.cpp	Mon Jun 10 13:22:46 2013 +0000
@@ -19,10 +19,10 @@
         {  -1,  0, -1}
     },
     {
-        {  RT,  RT,  1},       // X configuration
-        { -RT,  RT, -1},
-        { -RT, -RT,  1},
-        {  RT, -RT, -1}
+        {  RT, -RT,  1},       // X configuration
+        {  RT,  RT, -1},
+        { -RT,  RT,  1},
+        { -RT, -RT, -1}
     }
     };
     
@@ -34,5 +34,5 @@
     }
     
     for(int i = 0; i < 4; i++) // make sure no motor stands still
-        Motor_speed[i] = Motor_speed[i] > 50 ? Motor_speed[i] : 50;
+        Motor_speed[i] = Motor_speed[i] > 150 ? Motor_speed[i] : 150;
 }
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PC/PC.cpp	Mon Jun 10 13:22:46 2013 +0000
@@ -0,0 +1,38 @@
+#include "PC.h"
+#include "mbed.h"
+
+PC::PC(PinName tx, PinName rx, int baudrate) : Serial(tx, rx) 
+{
+    baud(baudrate);
+    cls();
+    
+    command[0] = '\0';
+    command_char_count = 0;
+}
+
+
+void PC::cls() 
+{
+    printf("\x1B[2J");
+}
+
+
+void PC::locate(int Spalte, int Zeile) 
+{
+    printf("\x1B[%d;%dH", Zeile + 1, Spalte + 1);
+}
+
+void PC::readcommand(void (*executer)(char*))
+{
+    char input = getc();             // get the character from serial bus
+    if(input == '\r') {                 // if return was pressed, the command must be executed
+        command[command_char_count] = '\0';
+        executer(&command[0]);
+        
+        command_char_count = 0;                 // reset command
+        command[command_char_count] = '\0';
+    } else if (command_char_count < COMMAND_MAX_LENGHT) {
+        command[command_char_count] = input;
+        command_char_count++;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PC/PC.h	Mon Jun 10 13:22:46 2013 +0000
@@ -0,0 +1,20 @@
+#include "mbed.h"
+
+#ifndef PC_H
+#define PC_H
+
+#define COMMAND_MAX_LENGHT 300
+
+class PC : public Serial 
+{
+    public:
+        PC(PinName tx, PinName rx, int baud);
+        void cls();                                                                        // to clear the display
+        void locate(int column, int row);                                                  // to relocate the cursor
+        void readcommand(void (*executer)(char*));                  // to read a char from the pc to the command string
+        
+        char command[COMMAND_MAX_LENGHT];
+    private:
+        int command_char_count;
+};
+#endif
--- a/RC/RC_Channel.cpp	Thu Apr 04 14:25:21 2013 +0000
+++ b/RC/RC_Channel.cpp	Mon Jun 10 13:22:46 2013 +0000
@@ -3,6 +3,7 @@
 
 RC_Channel::RC_Channel(PinName mypin, int index) : myinterrupt(mypin)
 {
+    LocalFileSystem local("local");                                  // If theres no local yet
     RC_Channel::index = index;
     time = -100; // start value to see if there was any value yet
     
--- a/RC/RC_Channel.h	Thu Apr 04 14:25:21 2013 +0000
+++ b/RC/RC_Channel.h	Mon Jun 10 13:22:46 2013 +0000
@@ -10,7 +10,7 @@
         int read(); // read the last measured data
        
     private:
-        int index; // to know which channel of the RC an instance has
+        int index; // to know which channel of the RC an instance has (only for calibrations savings)
         int time; // last measurement data
         float scale; // calibration values
         float offset;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sensors/Alt/BMP085.cpp	Mon Jun 10 13:22:46 2013 +0000
@@ -0,0 +1,57 @@
+#include "BMP085.h"
+
+BMP085::BMP085(PinName sda, PinName scl) : I2C_Sensor(sda, scl, BMP085_I2C_ADDRESS)
+{
+    // initialize BMP085 with settings
+    //writeRegister(0xf4, 0x2e); // TODO: was macht das + register in header!
+    
+}
+
+/*void BMP085::read() 
+    {
+    long P, UTemp, UPressure, X1, X2, X3, B3, B5, B6;
+    unsigned long B4, B7;
+
+    // TODO: writeRegister(0xf4, 0x2e); ?!!!
+    twi_writechar(BMP085_ADRESS, 0xf4, 0x2e);
+    // Wait at least 4.5ms
+    wait(0.005);
+    UTemp = twi_readshort(BMP085_ADRESS, 0xf6);
+    
+    X1 = ((UTemp - AC6) * AC5) >> 15;
+    X2 = (MC << 11) / (X1 + MD);
+    B5 = X1 + X2;
+    Temperature = (float)((B5 + 8) >> 4)/10.0;
+
+    twi_writechar(BMP085_ADRESS, 0xf4, 0x34 + (oss << 6));
+    // Wait at least 4.5ms
+    wait(0.005);
+    UPressure = twi_readlong(BMP085_ADRESS, 0xf6) >> (8 - oss);
+
+    B6 = B5 - 4000;
+    X1 = (B2 * (B6 * B6) >> 12) >> 11;
+    X2 = (AC2 * B6) >> 11;
+    X3 = X1 + X2;
+    B3 = ((AC1 * 4 + X3) << oss) >> 2;    
+
+    X1 = (AC3 * B6) >> 13;
+    X2 = (B1 * (B6 * B6) >> 12) >> 16;
+    X3 = ((X1 + X2) + 2) >> 2;
+    B4 = AC4 * (X3 + 32768) >> 15;
+    
+    B7 = (unsigned long)(UPressure - B3) * (50000 >> oss);
+    
+    if (B7 < 0x80000000) 
+        {
+        P = (2 * B7) / B4;
+        }
+    else 
+        {
+        P = 2* (B7 / B4);
+        }
+    X1 = (P >> 8) * (P >> 8);
+    X1 = (X1 * 3038) >> 16;
+    X2 = (-7357 * P) >> 16;    
+    P = P + ((X1 + X2 + 3791) >> 4);
+    Pressure = (float)P / 100.0;
+    }*/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sensors/Alt/BMP085.h	Mon Jun 10 13:22:46 2013 +0000
@@ -0,0 +1,34 @@
+// based on http://mbed.org/users/okini3939/code/BMP085/
+
+#ifndef BMP085_H
+#define BMP085_H
+
+#include "mbed.h"
+#include "I2C_Sensor.h"
+
+#define BMP085_I2C_ADDRESS 0xEE
+
+class BMP085 : public I2C_Sensor
+{           
+    public:
+        BMP085(PinName sda, PinName scl);
+        
+        //virtual void read();
+        
+        void calibrate(int s);
+        
+        float get_height();
+         
+    private:
+        // raw data and function to measure it
+        int raw[3];
+        //void readraw();
+        
+        // calibration parameters and their saving
+        int Min[3];
+        int Max[3];
+        float scale[3];
+        float offset[3];
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sensors/Comp/HMC5883.cpp	Mon Jun 10 13:22:46 2013 +0000
@@ -0,0 +1,81 @@
+#include "HMC5883.h"
+
+HMC5883::HMC5883(PinName sda, PinName scl) : I2C_Sensor(sda, scl, HMC5883_I2C_ADDRESS)
+{   
+    // load calibration values
+    //loadCalibrationValues(scale, 3, "COMPASS_SCALE.txt");
+    //loadCalibrationValues(offset, 3, "COMPASS_OFFSET.txt");
+    
+    // initialize HMC5883
+    writeRegister(HMC5883_CONF_REG_A, 0x78);                // 8 samples, 75Hz output, normal mode
+    //writeRegister(HMC5883_CONF_REG_A, 0x19);              // 8 samples, 75Hz output, test mode! (should get constant values from measurement, see datasheet)
+    writeRegister(HMC5883_CONF_REG_B, 0x20);                // Gain for +- 1.3 gauss (earth compass ~0.6 gauss)
+    writeRegister(HMC5883_MODE_REG, 0x00);                  // continuous measurement-mode
+}
+
+void HMC5883::read()
+{
+    readraw();
+    for(int i = 0; i < 3; i++)
+        data[i] = scale[i] * (float)(raw[i]) + offset[i];
+}
+
+void HMC5883::calibrate(int s)
+{
+    int Min[3];                                             // values for achieved maximum and minimum amplitude in calibrating environment
+    int Max[3];
+    
+    Timer calibrate_timer;                                  // timer to know when calibration is finished
+    calibrate_timer.start();
+    
+    while(calibrate_timer.read() < s)                       // take measurements for s seconds
+    {
+        readraw();
+        for(int i = 0; i < 3; i++) {
+            Min[i] = Min[i] < raw[i] ? Min[i] : raw[i];      // after each measurement check if there's a new minimum or maximum
+            Max[i] = Max[i] > raw[i] ? Max[i] : raw[i];
+        }
+    }
+    
+    for(int i = 0; i < 3; i++) {
+        scale[i]= 2000 / (float)(Max[i]-Min[i]);            // calculate scale and offset out of the measured maxima and minima
+        offset[i]= 1000 - (float)(Max[i]) * scale[i];       // the lower bound is -1000, the higher one 1000
+    }
+    
+    saveCalibrationValues(scale, 3, "COMPASS_SCALE.txt");   // save new scale and offset values to flash
+    saveCalibrationValues(offset, 3, "COMPASS_OFFSET.txt");
+}
+
+void HMC5883::readraw()
+{
+    char buffer[6];                                         // 8-Bit pieces of axis data
+    
+    readMultiRegister(HMC5883_DATA_OUT_X_MSB, buffer, 6);   // read axis registers using I2C
+    
+    raw[0] = (short) (buffer[0] << 8 | buffer[1]);          // join 8-Bit pieces to 16-bit short integers
+    raw[1] = (short) (buffer[4] << 8 | buffer[5]);          // X, Z and Y (yes, order is stupid like this, see datasheet)
+    raw[2] = (short) (buffer[2] << 8 | buffer[3]);
+}
+
+float HMC5883::get_angle()
+{
+    #define RAD2DEG     57.295779513082320876798154814105
+
+    float Heading;
+    
+    Heading = RAD2DEG * atan2(data[0],data[1]);
+    Heading += 1.367;                   // correction of the angle between geographical and magnetical north direction, called declination
+                                        // if you need an east-declination += DecAngle, if you need west-declination -= DecAngle
+                                        // for me in Switzerland, Bern it's ca. 1.367 degree east
+                                        // see:     http://magnetic-declination.com/
+                                        // for me:  http://www.swisstopo.admin.ch/internet/swisstopo/de/home/apps/calc/declination.html
+    if(Heading < 0)  
+        Heading += 360;                 // minimum 0 degree
+        
+    if(Heading > 360)   
+        Heading -= 360;                 // maximum 360 degree
+
+    return Heading;
+}
+    
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sensors/Comp/HMC5883.h	Mon Jun 10 13:22:46 2013 +0000
@@ -0,0 +1,32 @@
+// based on http://mbed.org/users/BlazeX/code/HMC5883/
+
+#ifndef HMC5883_H
+#define HMC5883_H
+
+#include "mbed.h"
+#include "I2C_Sensor.h"
+
+#define HMC5883_I2C_ADDRESS     0x3C
+
+#define HMC5883_CONF_REG_A      0x00
+#define HMC5883_CONF_REG_B      0x01
+#define HMC5883_MODE_REG        0x02
+#define HMC5883_DATA_OUT_X_MSB  0x03
+
+class HMC5883 : public I2C_Sensor
+{           
+    public:
+        HMC5883(PinName sda, PinName scl);
+        
+        virtual void read();            // read all axis from register to array data
+        void calibrate(int s);
+        float get_angle();
+         
+    private:
+        virtual void readraw();                 // function to get raw data
+        
+        float scale[3];                 // calibration parameters
+        float offset[3];
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sensors/Gyro/L3G4200D.h	Mon Jun 10 13:22:46 2013 +0000
@@ -0,0 +1,56 @@
+// based on http://mbed.org/users/shimniok/code/L3G4200D/
+
+#ifndef L3G4200D_H
+#define L3G4200D_H
+
+#include "mbed.h"
+#include "I2C_Sensor.h"
+
+#define L3G4200D_I2C_ADDRESS    0xD0
+
+// register addresses
+#define L3G4200D_WHO_AM_I       0x0F
+
+#define L3G4200D_CTRL_REG1      0x20
+#define L3G4200D_CTRL_REG2      0x21
+#define L3G4200D_CTRL_REG3      0x22
+#define L3G4200D_CTRL_REG4      0x23
+#define L3G4200D_CTRL_REG5      0x24
+#define L3G4200D_REFERENCE      0x25
+#define L3G4200D_OUT_TEMP       0x26
+#define L3G4200D_STATUS_REG     0x27
+
+#define L3G4200D_OUT_X_L        0x28
+#define L3G4200D_OUT_X_H        0x29
+#define L3G4200D_OUT_Y_L        0x2A
+#define L3G4200D_OUT_Y_H        0x2B
+#define L3G4200D_OUT_Z_L        0x2C
+#define L3G4200D_OUT_Z_H        0x2D
+
+#define L3G4200D_FIFO_CTRL_REG  0x2E
+#define L3G4200D_FIFO_SRC_REG   0x2F
+
+#define L3G4200D_INT1_CFG       0x30
+#define L3G4200D_INT1_SRC       0x31
+#define L3G4200D_INT1_THS_XH    0x32
+#define L3G4200D_INT1_THS_XL    0x33
+#define L3G4200D_INT1_THS_YH    0x34
+#define L3G4200D_INT1_THS_YL    0x35
+#define L3G4200D_INT1_THS_ZH    0x36
+#define L3G4200D_INT1_THS_ZL    0x37
+#define L3G4200D_INT1_DURATION  0x38
+
+class L3G4200D : public I2C_Sensor
+{
+    public:            
+        L3G4200D(PinName sda, PinName scl);                 // constructor, uses I2C_Sensor class
+        virtual void read();                                // read all axis from register to array data
+        float offset[3];                                    // offset that's subtracted from every measurement
+        void calibrate(int times, float separation_time);   // calibration from 'times' measurements with 'separation_time' time between (get an offset while not moving)
+        int readTemp();                                     // read temperature from sensor
+        
+    private:
+        virtual void readraw();
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sensors/I2C_Sensor.cpp	Mon Jun 10 13:22:46 2013 +0000
@@ -0,0 +1,54 @@
+#include "I2C_Sensor.h"
+
+// calculate the 8-Bit write/read I2C-Address from the 7-Bit adress of the device
+#define GET_I2C_WRITE_ADDRESS(ADR)  (ADR << 1&0xFE) // ADR & 1111 1110
+#define GET_I2C_READ_ADDRESS(ADR)   (ADR << 1|0x01) // ADR | 0000 0001
+
+I2C_Sensor::I2C_Sensor(PinName sda, PinName scl, char i2c_address) :  i2c(sda, scl), local("local")
+{
+    I2C_Sensor::i2c_address = i2c_address;
+    i2c.frequency(400000); // standard speed
+    //i2c.frequency(1500000); // ultrafast!
+}
+
+void I2C_Sensor::saveCalibrationValues(float values[], int size, char * filename)
+{
+    FILE *fp = fopen(strcat("/local/", filename), "w");
+    for(int i = 0; i < size; i++)
+        fprintf(fp, "%f\r\n", values[i]);
+    fclose(fp);
+}
+
+void I2C_Sensor::loadCalibrationValues(float values[], int size, char * filename)
+{
+    FILE *fp = fopen(strcat("/local/", filename), "r");
+    for(int i = 0; i < size; i++)
+        fscanf(fp, "%f", &values[i]);
+    fclose(fp);
+}
+
+//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+// ATTENTION!!! the I2C option "repeated" = true is important because otherwise interrupts while bus communications cause crashes (see http://www.i2c-bus.org/repeated-start-condition/)
+//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+char I2C_Sensor::readRegister(char reg)
+{
+    char value = 0;
+    
+    i2c.write(i2c_address, &reg, 1, true);
+    i2c.read(i2c_address, &value, 1, true);
+
+    return value;
+}
+
+void I2C_Sensor::writeRegister(char reg, char data)
+{
+    char buffer[2] = {reg, data};
+    i2c.write(i2c_address, buffer, 2, true);
+}
+
+void I2C_Sensor::readMultiRegister(char reg, char* output, int size)
+{
+    i2c.write (i2c_address, &reg, 1, true); // tell register address of the MSB get the sensor to do slave-transmit subaddress updating.
+    i2c.read  (i2c_address, output, size, true); // tell it where to store the data read
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sensors/I2C_Sensor.h	Mon Jun 10 13:22:46 2013 +0000
@@ -0,0 +1,38 @@
+// by MaEtUgR
+
+#ifndef I2C_Sensor_H
+#define I2C_Sensor_H
+
+#include "mbed.h"
+
+class I2C_Sensor
+{           
+    public:
+        I2C_Sensor(PinName sda, PinName scl, char address);
+        
+        float data[3];                  // where the measured data is saved
+        virtual void read() = 0;        // read all axis from register to array data
+        //TODO: virtual void calibrate() = 0;   // calibrate the sensor and if desired write calibration values to a file
+        
+    protected:
+        // Calibration
+        void saveCalibrationValues(float values[], int size, char * filename);
+        void loadCalibrationValues(float values[], int size, char * filename);
+    
+        // I2C functions
+        char readRegister(char reg);
+        void writeRegister(char reg, char data);
+        void readMultiRegister(char reg, char* output, int size);
+        
+        // raw data and function to measure it
+        int raw[3];
+        virtual void readraw() = 0;
+        
+    private:
+        I2C i2c;            // I2C-Bus
+        char i2c_address;   // address
+        
+        LocalFileSystem local; // file access to save calibration values
+};
+
+#endif
--- a/main.cpp	Thu Apr 04 14:25:21 2013 +0000
+++ b/main.cpp	Mon Jun 10 13:22:46 2013 +0000
@@ -16,16 +16,35 @@
 #define RC_SENSITIVITY  30                                  // maximal angle from horizontal that the PID is aming for
 #define YAWSPEED        2                                   // maximal speed of yaw rotation in degree per Rate
 
-float P = 1.1;                                   // PID values
-float I = 0.3;
-float D = 0.8;
+// RC
+#define AILERON         0
+#define ELEVATOR        1
+#define RUDDER          2
+#define THROTTLE        3
+// Axes
+#define ROLL            0
+#define PITCH           1
+#define YAW             2
 
 #define PC_CONNECTED // decoment if you want to debug per USB/Bluetooth and your PC
 
+// Global variables
+bool    armed = false;                  // this variable is for security (when false no motor rotates any more)
+float   dt = 0;
+float   time_for_dt = 0;
+float   dt_read_sensors = 0;
+float   time_read_sensors = 0;
+float   controller_value[] = {0,0,0};   // The calculated answer form the Controller
+float   RC_angle[] = {0,0,0};           // Angle of the RC Sticks, to steer the QC
+
+float P = 1.0;                          // PID values
+float I = 0;
+float D = 0;
+
 Timer GlobalTimer;                      // global time to calculate processing speed
-Ticker Dutycycler;                      // timecontrolled interrupt to get data form IMU and RC
+Ticker Dutycycler;                      // timecontrolled interrupt for exact timed control loop
 
-// initialisation of hardware (see includes for more info)
+// Initialisation of hardware (see includes for more info)
 LED         LEDs;
 #ifdef PC_CONNECTED
     //PC          pc(USBTX, USBRX, 115200);    // USB
@@ -35,22 +54,11 @@
 ADXL345     Acc(p28, p27);
 HMC5883     Comp(p28, p27);
 BMP085_old      Alt(p28, p27);
-RC_Channel  RC[] = {RC_Channel(p11,1), RC_Channel(p12,2), RC_Channel(p13,4), RC_Channel(p14,3)};    // no p19/p20 !
-Servo_PWM   ESC[] = {Servo_PWM(p21,PPM_FREQU), Servo_PWM(p22,PPM_FREQU), Servo_PWM(p23,PPM_FREQU), Servo_PWM(p24,PPM_FREQU)}; // p21 - p26 only because PWM needed!
-IMU_Filter  IMU;    // don't write () after constructor for no arguments!
-Mixer       MIX(1); // 1 for X-Formation 
-
-// 0:X:Roll 1:Y:Pitch 2:Z:Yaw
-PID     Controller[] = {PID(P, I, D, 1000), PID(P, I, D, 1000), PID(0.5, 0.01, 0, 1000)};
-
-// global variables
-bool    armed = false;          // this variable is for security (when false no motor rotates any more)
-float   dt = 0;
-float   time_for_dt = 0;
-float   dt_read_sensors = 0;
-float   time_read_sensors = 0;
-float   controller_value[] = {0,0,0};   // The calculated answer form the Controller
-float   RC_angle[] = {0,0,0};  // Angle of the RC Sticks, to steer the QC
+RC_Channel  RC[] = {RC_Channel(p5,1), RC_Channel(p6,2), RC_Channel(p8,4), RC_Channel(p7,3)};                                // no p19/p20 !
+Servo_PWM   ESC[] = {Servo_PWM(p21,PPM_FREQU), Servo_PWM(p22,PPM_FREQU), Servo_PWM(p23,PPM_FREQU), Servo_PWM(p24,PPM_FREQU)};   // p21 - p26 only because PWM needed!
+IMU_Filter  IMU;    // (don't write () after constructor for no arguments!)
+Mixer       MIX(1); // 0 for +-Formation, 1 for X-Formation 
+PID     Controller[] = {PID(P, I, D, 1000), PID(P, I, D, 1000), PID(0.5, 0, 0, 1000)}; // 0:X:Roll 1:Y:Pitch 2:Z:Yaw
 
 void dutycycle() // method which is called by the Ticker Dutycycler every RATE seconds
 {
@@ -58,9 +66,9 @@
     
     // read data from sensors // ATTENTION! the I2C option repeated true is important because otherwise interrupts while bus communications cause crashes
     Gyro.read();
-    Acc.read(); // TODO: nicht jeder Sensor immer? höhe nicht so wichtig
-    //Comp.read();
-    //Alt.Update(); TODO braucht zu lange zum auslesen!
+    Acc.read();
+    //Comp.read(); // TODO: not every loop every sensor? altitude not that important
+    //Alt.Update(); // TODO needs very long to read because of waits
     
     dt_read_sensors = GlobalTimer.read() - time_read_sensors; // stop time measure for sensors
     
@@ -71,13 +79,14 @@
     IMU.compute(dt, Gyro.data, Acc.data);
     
     // Arming / disarming
-    if(RC[3].read() < 20 && RC[2].read() > 850) {
+    if(RC[THROTTLE].read() < 20 && RC[RUDDER].read() > 850) {
         armed = true;
     }
-    if((RC[3].read() < 30 && RC[2].read() < 30) || RC[2].read() < -10 || RC[3].read() < -10 || RC[1].read() < -10 || RC[0].read() < -10) {
+    if((RC[THROTTLE].read() < 30 && RC[RUDDER].read() < 30) || RC[RUDDER].read() < -10 || RC[THROTTLE].read() < -10 || RC[ELEVATOR].read() < -10 || RC[AILERON].read() < -10) {
         armed = false;
     }
     
+    // RC Angle
     for(int i=0;i<2;i++) {    // calculate new angle we want the QC to have
         RC_angle[i] = (RC[i].read()-500)*RC_SENSITIVITY/500.0;
         if (RC_angle[i] < -RC_SENSITIVITY-2)
@@ -85,6 +94,7 @@
     }
     //RC_angle[2] += (RC[3].read()-500)*YAWSPEED/500;  // for yaw angle is integrated
     
+    // PID controlling
     for(int i=0;i<3;i++) {
         Controller[i].setIntegrate(armed); // only integrate in controller when armed, so the value is not totally odd from not flying
         controller_value[i] = Controller[i].compute(RC_angle[i], IMU.angle[i]); // give the controller the actual angle and get his advice to correct
@@ -93,15 +103,7 @@
     
     if (armed) // for SECURITY!
     {       
-            // RC controlling
-            /*for(int i=0;i<3;i++)
-                AnglePosition[i] -= (RC[i].read()-500)*2/500.0;*/
-            /*virt_angle[0] = IMU.angle[0] + (RC[0].read()-500)*MAXPITCH/500.0; // TODO: zuerst RC calibration
-            virt_angle[1] = IMU.angle[1] + (RC[1].read()-500)*MAXPITCH/500.0;
-            yawposition += (RC[3].read()-500)*YAWSPEED/500;
-            virt_angle[2] = IMU.angle[2] + yawposition;*/
-
-            MIX.compute(RC[3].read(), controller_value); // let the Mixer compute motorspeeds based on throttle and controller output
+            MIX.compute(RC[THROTTLE].read(), controller_value); // let the Mixer compute motorspeeds based on throttle and controller output
             
             for(int i=0;i<4;i++)   // Set new motorspeeds
                 ESC[i] = (int)MIX.Motor_speed[i];
@@ -145,6 +147,7 @@
         if (pc.readable())  // Get Serial input (polled because interrupts disturb I2C)
             pc.readcommand(&commandexecuter);
         //pc.printf("%f %f %f %f %f %f\r\n", IMU.angle[0], IMU.angle[1], IMU.angle[2], controller_value[0], controller_value[1], controller_value[2]); // For live plot in MATLAB of IMU
+        //pc.printf("%f,%f,%f\r\n", IMU.angle[0], IMU.angle[1], IMU.angle[2]);
         #if 1 //pc.cls();
             pc.locate(20,0); // PC output
             pc.printf("dt:%3.5fs   dt_sensors:%3.5fs    Altitude:%6.1fm   ", dt, dt_read_sensors, Alt.CalcAltitude(Alt.Pressure));