Der WII-Copter ( Selbstbau-Projekt )

Benutzeravatar
JollyJoker
Beiträge: 139
Registriert: 31.01.2011 22:21:57
Wohnort: MS

#4381 Re: Der WII-Copter ( Selbstbau-Projekt )

Beitrag von JollyJoker »

calli hat geschrieben:Wie macht sich das bemerkbar? Damit ich drauf gefasst bin...
Du versuchst immer Heck voran zu fliegen, da Du dann den führenden Prop so gewohnt bist :mrgreen:

Gruß Peer
kleiner Exkurs zum PID tunig des MultiWiicopter: PID und zur GUI: GUI
Remember, it´s the man, not the machine;-)
stiegl_1492
Beiträge: 28
Registriert: 07.04.2011 22:39:07

#4382 Re: Der WII-Copter ( Selbstbau-Projekt )

Beitrag von stiegl_1492 »

Hallo zusammen!

also hab jetzt auf mein Arduino Nano V3 die MultiWiiConf1_preter7 geladen und mit dem GUI getestet.

Bild

So sieht es dann aus nach dem kalibrieren! also kurz getriggert und dann Ausschläge in alle Richtungen und dann is wieder wie vorher!
Wenn ich die Platinen bewege passiert rein gar nichts am GUI
RX/TX Leds blinken wie wild... soll ja so sein wenn kommuniziert wird!

Also hier mein kompletter code!

Code: Alles auswählen

/*
MultiWiiCopter by Alexandre Dubus
www.multiwii.com
March  2011     V1.preter7
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 any later version. see <http://www.gnu.org/licenses/>
*/

/*******************************/
/****CONFIGURABLE PARAMETERS****/
/*******************************/

/* Set the minimum throttle command sent to the ESC (Electronic Speed Controller) */
/* This is the minimum value that allow motors to run at a idle speed  */
#define MINTHROTTLE 1120 // for Super Simple ESCs 10A

/* The type of multicopter */
#define QUADX

//#define YAW_DIRECTION 1 // if you want to reverse the yaw correction direction
#define YAW_DIRECTION -1

#define I2C_SPEED 100000L     //100kHz normal mode, this value must be used for a genuine WMP
//#define I2C_SPEED 400000L   //400kHz fast mode, it works only with some WMP clones

#define PROMINI  //Arduino type
//#define MEGA

//enable internal I2C pull ups
#define INTERNAL_I2C_PULLUPS

//****** advanced users settings   *************


#define FAILSAVE_DELAY     10                     // Guard time for failsafe activation after signal lost. 1 step = 0.1sec - 1sec in example
#define FAILSAVE_OFF_DELAY 200                    // Time for Landing before motors stop in 0.1sec. 1 step = 0.1sec - 20sec in example
#define FAILSAVE_THR0TTLE  (MINTHROTTLE + 200)    // Throttle level used for landing - may be relative to MINTHROTTLE - as in this case


/* The following lines apply only for a pitch/roll tilt stabilization system */
/* It is not compatible with Y6 or HEX6 or HEX6X */
/* Uncomment the first line to activate it */
//#define SERVO_TILT
#define TILT_PITCH_MIN    1020    //servo travel min, don't set it below 1020
#define TILT_PITCH_MAX    2000    //servo travel max, max value=2000
#define TILT_PITCH_MIDDLE 1500    //servo neutral value
#define TILT_PITCH_PROP   10      //servo proportional (tied to angle) ; can be negative to invert movement
#define TILT_ROLL_MIN     1020
#define TILT_ROLL_MAX     2000
#define TILT_ROLL_MIDDLE  1500
#define TILT_ROLL_PROP    10

/* I2C gyroscope */
#define ITG3200

/* I2C accelerometer */
#define ADXL345

/* interleaving delay in micro seconds between 2 readings WMP/NK in a WMP+NK config */
/* if the ACC calibration time is very long (20 or 30s), try to increase this delay up to 4000 */
/* it is relevent only for a conf with NK */
#define INTERLEAVING_DELAY 3000

//for V BAT monitoring
#define VBATSCALE     131 // change this value if readed Battery voltage is different than real voltage
#define VBATLEVEL1_3S 107 // 10,7V
#define VBATLEVEL2_3S 103 // 10,3V
#define VBATLEVEL3_3S 99  // 9.9V

/* when there is an error on I2C bus, we neutralize the values during a short time. expressed in microseconds */
/* it is relevent only for a conf with at least a WMP */
#define NEUTRALIZE_DELAY 100000

/* this is the value for the ESCs when thay are not armed */
/* in some case, this value must be lowered down to 900 for some specific ESCs */
#define MINCOMMAND 1000

/* this is the maximum value for the ESCs at full power */
/* this value can be increased up to 2000 */
#define MAXTHROTTLE 1850

/* This is the speed of the serial interface. 115200 kbit/s is the best option for a USB connection.*/
#define SERIAL_COM_SPEED 115200

/* In order to save space, it's possibile to desactivate the LCD configuration functions */
/* comment this line only if you don't plan to used a LCD */
#define LCD_CONF

/* some radios have not a neutral point centered on 1500. can be changed here */
#define MIDRC 1500

/* experimental */
#define CAM_SERVO_HIGH 2000  // the position of HIGH state servo
#define CAM_SERVO_LOW 1020   // the position of LOW state servo
#define CAM_TIME_HIGH 1000   // the duration of HIGH state servo expressed in ms
#define CAM_TIME_LOW 1000    // the duration of LOW state servo expressed in ms

/* you can change the tricopter servo travel here */
#define TRI_YAW_CONSTRAINT_MIN 1020
#define TRI_YAW_CONSTRAINT_MAX 2000

//****** end of advanced users settings *************

/**************************************/
/****END OF CONFIGURABLE PARAMETERS****/
/**************************************/



ALso wie schon gepostet.. es waren die "richtigen" Sensoren im Programm deklariert!

zur Info: NK ist ein Original (ist zwar aus China aber kam mit original WII Verpackung) und das WMP Board ist ein China Nachbau. und ich verwende ein ARDUINO NANO V3


Danke schon mal für eure Hilfe!

Grüße,
Chris
stiegl_1492
Beiträge: 28
Registriert: 07.04.2011 22:39:07

#4383 Re: Der WII-Copter ( Selbstbau-Projekt )

Beitrag von stiegl_1492 »

und das ist dann das hauptprogramm:
Teil 1

Code: Alles auswählen

#include <EEPROM.h>

#define LEDPIN 13     // will be changed for MEGA in a future version
#define POWERPIN 12   // will be changed for MEGA in a future version
#define V_BATPIN 3    // Analog PIN 3
#define STABLEPIN     // will be defined for MEGA in a future version

#if defined(PROMINI)
  #define LEDPIN_PINMODE             pinMode (13, OUTPUT);
  #define LEDPIN_SWITCH              PINB |= 1<<5;     //switch LEDPIN state (digital PIN 13)
  #define LEDPIN_OFF                 PORTB &= ~(1<<5);
  #define LEDPIN_ON                  PORTB |= (1<<5);
  #define BUZZERPIN_PINMODE          pinMode (8, OUTPUT);
  #define BUZZERPIN_ON               PORTB |= 1;
  #define BUZZERPIN_OFF              PORTB &= ~1;
  #define POWERPIN_PINMODE           pinMode (12, OUTPUT);
  #define POWERPIN_ON                PORTB |= 1<<4;
  #define POWERPIN_OFF               PORTB &= ~(1<<4); //switch OFF WMP, digital PIN 12
  #define I2C_PULLUPS_ENABLE         PORTC |= 1<<4; PORTC |= 1<<5;   // PIN A4&A5 (SDA&SCL)
  #define I2C_PULLUPS_DISABLE        PORTC &= ~(1<<4); PORTC &= ~(1<<5);
  #define PINMODE_LCD                pinMode(0, OUTPUT);
  #define LCDPIN_OFF                 PORTD &= ~1;
  #define LCDPIN_ON                  PORTD |= 1;
  #define DIGITAL_SERVO_TRI_PINMODE  pinMode(3,OUTPUT); //also right servo for BI COPTER
  #define DIGITAL_SERVO_TRI_HIGH     PORTD |= 1<<3;
  #define DIGITAL_SERVO_TRI_LOW      PORTD &= ~(1<<3);
  #define DIGITAL_TILT_PITCH_PINMODE pinMode(A0,OUTPUT);
  #define DIGITAL_TILT_PITCH_HIGH    PORTC |= 1<<0;
  #define DIGITAL_TILT_PITCH_LOW     PORTC &= ~(1<<0);
  #define DIGITAL_TILT_ROLL_PINMODE  pinMode(A1,OUTPUT);
  #define DIGITAL_TILT_ROLL_HIGH     PORTC |= 1<<1;
  #define DIGITAL_TILT_ROLL_LOW      PORTC &= ~(1<<1);
  #define DIGITAL_BI_LEFT_PINMODE    pinMode(11,OUTPUT); 
  #define DIGITAL_BI_LEFT_HIGH       PORTB |= 1<<3;
  #define DIGITAL_BI_LEFT_LOW        PORTB &= ~(1<<3);
  #define PPM_PIN_INTERRUPT          attachInterrupt(0, rxInt, RISING); //PIN 0
  #define MOTOR_ORDER                9,10,11,3,6,5  //for a quad+: rear,right,left,front
  #define DIGITAL_CAM_PINMODE        pinMode(A2,OUTPUT);
  #define DIGITAL_CAM_HIGH           PORTC |= 1<<2;
  #define DIGITAL_CAM_LOW            PORTC &= ~(1<<2);
  //RX PIN assignment inside the port //for PORTD
  #define THROTTLEPIN                2
  #define ROLLPIN                    4
  #define PITCHPIN                   5
  #define YAWPIN                     6
  #define AUX1PIN                    7
  #define AUX2PIN                    7   //unused just for compatibility with MEGA
  #define CAM1PIN                    7   //unused just for compatibility with MEGA
  #define CAM2PIN                    7   //unused just for compatibility with MEGA
  #define ISR_UART                   ISR(USART_UDRE_vect)
#endif

#if defined(BI) || defined(TRI) || defined(SERVO_TILT) || defined(GIMBAL) || defined(FLYING_WING) || defined(CAMTRIG)
  #define SERVO
#endif

#if defined(ADXL345) || defined(BMA020) || defined(BMA180)
  #define I2C_ACC
#endif

#if defined(GIMBAL) || defined(FLYING_WING)
  #define NUMBER_MOTOR 0
#elif defined(BI)
  #define NUMBER_MOTOR 2
#elif defined(TRI)
  #define NUMBER_MOTOR 3
#elif defined(QUADP) || defined(QUADX) || defined(Y4)
  #define NUMBER_MOTOR 4
#elif defined(Y6) || defined(HEX6) || defined(HEX6X)
  #define NUMBER_MOTOR 6
#endif

/*********** RC alias *****************/
#define ROLL       0
#define PITCH      1
#define YAW        2
#define THROTTLE   3
#define AUX1       4
#define AUX2       5
#define CAMPITCH   6
#define CAMROLL    7

static uint32_t previousTime;
static uint32_t neutralizeTime;
static uint32_t rcTime;
static uint32_t currentTime;
static uint16_t cycleTime;          // this is the number in micro second to achieve a full loop, it can differ a little and is taken into account in the PID loop
static uint16_t meanTime = 2000;    // this is the average time of the loop: around 2ms for a WMP config and 6ms for a NK+WMP config
// to be more precise, the calibration is now done is the main loop. Calibrating decreases at each cycle up to 0, then we enter in a normal mode.
// we separate the calibration of ACC and gyro, because number of measure is not always equal.
static uint16_t calibratingA;
static uint16_t calibratingG;
static uint8_t armed = 0;
static int16_t acc_1G = 200;       //this is the 1G measured acceleration (nunchuk)
static int16_t acc_25deg = 85;     //this is the the ACC value measured on x or y axis for a 25deg inclination (nunchuk) = acc_1G * sin(25)
static uint8_t nunchukPresent = 0;
static uint8_t accPresent = 0;     //I2C or ADC acc present
static uint8_t gyroPresent = 0;    //I2C or ADC gyro present (other than WMP)
static uint8_t magPresent = 0;     //I2C or ADC compass present
static uint8_t baroPresent = 0;    //I2C or ADC baro present
static uint8_t accMode = 0;        //if level mode is a activated
static uint8_t magMode = 0;        //if compass heading hold is a activated
static uint8_t baroMode = 0;       //if altitude hold is activated
static int16_t accADC[3];
static int16_t gyroADC[3];
static int16_t magADC[3];
static int16_t heading,magHold;
static int16_t altitudeSmooth = 0;
static uint8_t calibratedACC = 0;
static uint8_t vbat;               //battery voltage in 0.1V steps

// *********************
// I2C general functions
// *********************

// Mask prescaler bits : only 5 bits of TWSR defines the status of each I2C request
#define TW_STATUS_MASK	(1<<TWS7) | (1<<TWS6) | (1<<TWS5) | (1<<TWS4) | (1<<TWS3)
#define TW_STATUS       (TWSR & TW_STATUS_MASK)

void i2c_init(void) {
  #if defined(INTERNAL_I2C_PULLUPS)
    I2C_PULLUPS_ENABLE
  #else
    I2C_PULLUPS_DISABLE
  #endif
  TWSR = 0;        // no prescaler => prescaler = 1
  TWBR = ((16000000L / I2C_SPEED) - 16) / 2; // change the I2C clock rate
  TWCR = 1<<TWEN;  // enable twi module, no interrupt
}

void i2c_rep_start(uint8_t address) {
  TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN) | (1<<TWSTO); // send REAPEAT START condition
  waitTransmissionI2C(); // wait until transmission completed
  checkStatusI2C(); // check value of TWI Status Register
  TWDR = address; // send device address
  TWCR = (1<<TWINT) | (1<<TWEN);
  waitTransmissionI2C(); // wail until transmission completed
  checkStatusI2C(); // check value of TWI Status Register
}


void i2c_write(uint8_t data ) {	
  TWDR = data; // send data to the previously addressed device
  TWCR = (1<<TWINT) | (1<<TWEN);
  waitTransmissionI2C(); // wait until transmission completed
  checkStatusI2C(); // check value of TWI Status Register
}

uint8_t i2c_readAck() {
  TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
  waitTransmissionI2C();
  return TWDR;
}

uint8_t i2c_readNak(void) {
  TWCR = (1<<TWINT) | (1<<TWEN);
  waitTransmissionI2C();
  return TWDR;
}

void waitTransmissionI2C() {
  uint8_t count = 255;
  while (count-->0 && !(TWCR & (1<<TWINT)) );
  if (count<2) { //we are in a blocking state => we don't insist
    TWCR = 0;  //and we force a reset on TWINT register
    neutralizeTime = micros(); //we take a timestamp here to neutralize the value during a short delay after the hard reset
  }
}

void checkStatusI2C() {
  if ( TW_STATUS  == 0xF8) { //TW_NO_INFO : this I2C error status indicates a wrong I2C communication.
    // WMP does not respond anymore => we do a hard reset. I did not find another way to solve it. It takes only 13ms to reset and init to WMP or WMP+NK
    TWCR = 0;
    POWERPIN_OFF //switch OFF WMP
    delay(1);  
    POWERPIN_ON  //switch ON WMP
    delay(10);
    #if defined(ITG3200) || defined(L3G4200D)
    #else
      i2c_WMP_init(0);
    #endif
    neutralizeTime = micros(); //we take a timestamp here to neutralize the WMP or WMP+NK values during a short delay after the hard reset
  }
}


// **************************
// I2C Barometer BOSCH BMP085
// **************************
// I2C adress: 0xEE (8bit)   0x77 (7bit)
// principle:
//  1) read the calibration register (only once at the initialization)
//  2) read uncompensated temperature (not mandatory at every cycle)
//  3) read uncompensated pressure
//  4) raw temp + raw pressure => calculation of the adjusted pressure
//  the following code uses the maximum precision setting (oversampling setting 3)

// sensor registers from the BOSCH BMP085 datasheet
#if defined(BMP085)
static int16_t ac1,ac2,ac3,b1,b2,mb,mc,md;
static uint16_t  ac4,ac5,ac6;

static uint16_t ut; //uncompensated T
static uint32_t up; //uncompensated P
int32_t temperature = 0;
int32_t pressure = 0;
int16_t altitude = 0;
static int16_t altitudeZero;
static int16_t altitudeHold;

void  i2c_Baro_init() {
  delay(10);
  ac1 = i2c_BMP085_readIntRegister(0xAA);
  ac2 = i2c_BMP085_readIntRegister(0xAC);
  ac3 = i2c_BMP085_readIntRegister(0xAE);
  ac4 = i2c_BMP085_readIntRegister(0xB0);
  ac5 = i2c_BMP085_readIntRegister(0xB2);
  ac6 = i2c_BMP085_readIntRegister(0xB4);
  b1  = i2c_BMP085_readIntRegister(0xB6);
  b2  = i2c_BMP085_readIntRegister(0xB8);
  mb  = i2c_BMP085_readIntRegister(0xBA);
  mc  = i2c_BMP085_readIntRegister(0xBC);
  md  = i2c_BMP085_readIntRegister(0xBE);
  
  baroPresent = 1;
  i2c_BMP085_calibrate();
}

// read a 16 bit register
int16_t i2c_BMP085_readIntRegister(unsigned char r) {
  uint8_t msb, lsb;
  
  i2c_rep_start(0xEE + 0);
  i2c_write(r);
  i2c_rep_start(0xEE + 1);//I2C read direction => 1
  msb=i2c_readAck();
  lsb=i2c_readNak();
  return (((int16_t)msb<<8) | ((int16_t)lsb));
}

// read uncompensated temperature value: send command first
void i2c_BMP085_readUT_Command() {
  i2c_rep_start(0xEE + 0);
  i2c_write(0xf4);
  i2c_write(0x2e);
  i2c_rep_start(0xEE + 0);
  i2c_write(0xF6);
}

// read uncompensated temperature value: read result bytes
// the datasheet suggests a delay of 4.5 ms after the send command
uint16_t i2c_BMP085_readUT_Result() {
  uint8_t msb, lsb;
  i2c_rep_start(0xEE + 1);//I2C read direction => 1
  msb=i2c_readAck();
  lsb=i2c_readNak();
  return (((uint16_t)msb<<8) | ((uint16_t)lsb));
}

// read uncompensated pressure value: send command first
void i2c_BMP085_readUP_Command () {
  i2c_rep_start(0xEE + 0);
  i2c_write(0xf4);
  i2c_write(0xf4); //control register value for oversampling setting 3
  i2c_rep_start(0xEE + 0); //I2C write direction => 0
  i2c_write(0xf6);
}

// read uncompensated pressure value: read result bytes
// the datasheet suggests a delay of 25.5 ms (oversampling settings 3) after the send command
uint32_t i2c_BMP085_readUP_Result () {
  uint8_t msb, lsb, xlsb;
  i2c_rep_start(0xEE + 1);//I2C read direction => 1
  msb = i2c_readAck();
  lsb = i2c_readAck();
  xlsb = i2c_readNak();
  return (((uint32_t)msb<<16) | ((uint32_t)lsb<<8) | ((uint32_t)xlsb)) >>5;
}

// deduction of true temperature and pressure from sensor, code is described in the BMP085 specs
void i2c_BMP085_CompensatedSensor() {
  int32_t x1, x2, x3, b3, b5, b6, p;
  uint32_t b4, b7;
  uint16_t a,b;

  //calculate true temperature
  x1 = (int32_t)(ut - ac6) * ac5 >> 15;
  x2 = ((int32_t) mc << 11) / (x1 + md);
  b5 = x1 + x2;
  temperature = (b5 + 8) >> 4;
  //calculate true pressure
  b6 = b5 - 4000;
  x1 = (b2 * (b6 * b6 >> 12)) >> 11; 
  x2 = ac2 * b6 >> 11;
  x3 = x1 + x2;
  b3 = ( ( ((int32_t) ac1 * 4 + x3)<<3 ) + 2) >> 2;
  x1 = ac3 * b6 >> 13;
  x2 = (b1 * (b6 * b6 >> 12)) >> 16;
  x3 = ((x1 + x2) + 2) >> 2;
  b4 = (ac4 * (uint32_t) (x3 + 32768)) >> 15;
  b7 = (up - b3) * (50000 >> 3);
  p = b7 < 0x80000000 ? (b7 * 2) / b4 : (b7 / b4) * 2;
  x1 = (p >> 8) * (p >> 8);
  x1 = (x1 * 3038) >> 16;
  x2 = (-7357 * p) >> 16;
  pressure = p + ((x1 + x2 + 3791) >> 4);
}

//in a whole cycle: we read temperature one time and pressure 5 times
void i2c_Baro_update() {
  static uint32_t t;
  static uint8_t state1 =0,state2 = 0;

  if ( (micros()-t )  < 30000 ) return; //each read is spaced by 30ms
  t = micros();
  
  TWBR = ((16000000L / 400000L) - 16) / 2; // change the I2C clock rate to 400kHz, BMP085 is ok with this speed
  if (state1 == 0) { 
    if (state2 == 0) {
      i2c_BMP085_readUT_Command();
      state2=1;
    } else {
      ut = i2c_BMP085_readUT_Result();
      state2=0;
      state1=5;
    }
  } else {
    if (state2 == 0) {
      i2c_BMP085_readUP_Command();
      state2=1;
    } else {
      up = i2c_BMP085_readUP_Result();
      state2=0;
      state1--;
      i2c_BMP085_CompensatedSensor();
      altitude = (1.0 - pow(float(pressure)/101325.0, 0.190295)) * 443300 - altitudeZero; // altitude in decimeter from starting point
      if ( abs(altitude-altitudeSmooth) < 100 ) //avoid altitude spike
        altitudeSmooth = (altitudeSmooth*7+altitude+4)/8;
    }
  }
}

void i2c_BMP085_calibrate() {
  altitudeZero = 0;
  for (uint8_t i=0;i<7;i++) {
    i2c_Baro_update();
    delay(35);
  }
  altitudeZero = altitude;
}
#endif


// **************************
// I2C Accelerometer ADXL345 
// **************************
// I2C adress: 0x3A (8bit)    0x1D (7bit)
// principle:
//  1) CS PIN must be linked to VCC to select the I2C mode
//  2) SD0 PIN must be linked to VCC to select the right I2C adress
//  3) bit  b00000100 must be set on register 0x2D to read data (only once at the initialization)
//  4) bits b00001011 must be set on register 0x31 to select the data format (only once at the initialization)
#if defined(ADXL345)
static uint8_t rawADC_ADXL345[6];

void i2c_ACC_init () {
  delay(10);
  i2c_rep_start(0x3A+0);      // I2C write direction
  i2c_write(0x2D);            // register 2D Power CTRL
  i2c_write(1<<3);            // Set measure bit 3 on
  i2c_rep_start(0x3A+0);      // I2C write direction 
  i2c_write(0x31);            // DATA_FORMAT register
  i2c_write(0x0B);            // Set bits 3(full range) and 1 0 on (+/- 16g-range)
  i2c_rep_start(0x3A+0);      // I2C write direction 
  i2c_write(0x2C);            // BW_RATE
  i2c_write(8+2+1);           // 200Hz sampling (see table 5 of the spec)

  acc_1G = 250;
  acc_25deg = 106; // = acc_1G * sin(25 deg)
  accPresent = 1;
}

void i2c_ACC_getADC () {
  TWBR = ((16000000L / 400000L) - 16) / 2; // change the I2C clock rate to 400kHz, ADXL435 is ok with this speed
  i2c_rep_start(0x3A);     // I2C write direction
  i2c_write(0x32);         // Start multiple read at reg 0x32 ADX
  i2c_rep_start(0x3A +1);  // I2C read direction => 1
  for(uint8_t i = 0; i < 5; i++) {
    rawADC_ADXL345[i]=i2c_readAck();}
  rawADC_ADXL345[5]= i2c_readNak();

  accADC[ROLL]  = - ((rawADC_ADXL345[3]<<8) | rawADC_ADXL345[2]);
  accADC[PITCH] =   ((rawADC_ADXL345[1]<<8) | rawADC_ADXL345[0]);
  accADC[YAW]   = - ((rawADC_ADXL345[5]<<8) | rawADC_ADXL345[4]);
}
#endif


// **************************
// contribution from opie11 (rc-grooups)
// I2C Accelerometer BMA180
// **************************
// I2C adress: 0x80 (8bit)    0x40 (7bit)
#if defined(BMA180)
static uint8_t rawADC_BMA180[6];

void i2c_ACC_init () {
  delay(10);
  i2c_rep_start(0x80+0);      // I2C write direction 
  i2c_write(0x0D);            // ctrl_reg0
  i2c_write(1<<4);            // Set bit 4 to 1 to enable writing
  i2c_rep_start(0x80+0);       
  i2c_write(0x35);            // 
  i2c_write(3<<1);            // range set to 3.  2730 1G raw data.  With /10 divisor on acc_ADC, more in line with other sensors and works with the GUI
  i2c_rep_start(0x80+0);
  i2c_write(0x20);            // bw_tcs reg: bits 4-7 to set bw
  i2c_write(0<<4);            // bw to 10Hz (low pass filter)

  acc_1G = 273;
  acc_25deg = 113; // = acc_1G * sin(25 deg)
  accPresent = 1;
}

void i2c_ACC_getADC () {
  TWBR = ((16000000L / 400000L) - 16) / 2;  // Optional line.  Sensor is good for it in the spec.
  i2c_rep_start(0x80);     // I2C write direction
  i2c_write(0x02);         // Start multiple read at reg 0x02 acc_x_lsb
  i2c_rep_start(0x80 +1);  // I2C read direction => 1
  for(uint8_t i = 0; i < 5; i++) {
    rawADC_BMA180[i]=i2c_readAck();}
  rawADC_BMA180[5]= i2c_readNak();

  accADC[ROLL]  = - (((rawADC_BMA180[1]<<8) | (rawADC_BMA180[0]))>>2)/10; // opie settings: + ; FFIMU: -
  accADC[PITCH] = - (((rawADC_BMA180[3]<<8) | (rawADC_BMA180[2]))>>2)/10;
  accADC[YAW]   = - (((rawADC_BMA180[5]<<8) | (rawADC_BMA180[4]))>>2)/10;
}
#endif

// **************
// contribution from Point65 and mgros (rc-grooups)
// BMA020 I2C
// **************
// I2C adress: 0x70 (8bit)
#if defined(BMA020)
static uint8_t rawADC_BMA020[6];

void i2c_ACC_init(){
  byte control;
  
  i2c_rep_start(0x70);     // I2C write direction
  i2c_write(0x15);         // 
  i2c_write(0x80);         // Write B10000000 at 0x15 init BMA020

  i2c_rep_start(0x70);     // 
  i2c_write(0x14);         //  
  i2c_write(0x71);         // 
  i2c_rep_start(0x71);     //
  control = i2c_readNak();
 
  control = control >> 5;  //ensure the value of three fist bits of reg 0x14 see BMA020 documentation page 9
  control = control << 2;
  control = control | 0x00; //Range 2G 00
  control = control << 3;
  control = control | 0x00; //Bandwidth 25 Hz 000
 
  i2c_rep_start(0x70);     // I2C write direction
  i2c_write(0x14);         // Start multiple read at reg 0x32 ADX
  i2c_write(control);

  acc_1G = 240;
  acc_25deg = 101; // = acc_1G * sin(25 deg)
  accPresent = 1;
}

void i2c_ACC_getADC(){
  TWBR = ((16000000L / 400000L) - 16) / 2;
  i2c_rep_start(0x70);     // I2C write direction
  i2c_write(0x02);         // Start multiple read at reg 0x32 ADX
  i2c_write(0x71);  
  i2c_rep_start(0x71);  //I2C read direction => 1
  for(uint8_t i = 0; i < 5; i++) {
    rawADC_BMA020[i]=i2c_readAck();}
  rawADC_BMA020[5]= i2c_readNak();

  accADC[ROLL]  =  (((rawADC_BMA020[1])<<8) | ((rawADC_BMA020[0]>>1)<<1))/64;
  accADC[PITCH] =  (((rawADC_BMA020[3])<<8) | ((rawADC_BMA020[2]>>1)<<1))/64;
  accADC[YAW]   = -(((rawADC_BMA020[5])<<8) | ((rawADC_BMA020[4]>>1)<<1))/64;
}
#endif


// **************************
// contribution from Ciskje
// I2C Gyroscope L3G4200D 
// **************************
#if defined(L3G4200D)
static uint8_t rawADC_L3G4200D[6];

void i2c_Gyro_init() {
  delay(100);
  i2c_rep_start(0XD2+0);      // CTRL_REG1
  i2c_write(0x20);            // 400Hz ODR, 20hz filter, run!
  i2c_write(0x8F); 
  i2c_rep_start(0XD2+0);      // CTRL_REG5
  i2c_write(0x24);            // low pass filter enable
  i2c_write(0x02);
  
  gyroPresent = 1;  
}

void i2c_Gyro_getADC () {
  TWBR = ((16000000L / 400000L) - 16) / 2; // change the I2C clock rate to 400kHz
  i2c_rep_start(0XD2);     // I2C write direction
  i2c_write(0x80 | 0x28);  // Start multiple read
  i2c_rep_start(0XD2 +1);  // I2C read direction => 1
  for(uint8_t i = 0; i < 5; i++) {
    rawADC_L3G4200D[i]=i2c_readAck();}
  rawADC_L3G4200D[5]= i2c_readNak();

  gyroADC[ROLL]  =  ((rawADC_L3G4200D[1]<<8) | rawADC_L3G4200D[0]) ;
  gyroADC[PITCH] =  ((rawADC_L3G4200D[3]<<8) | rawADC_L3G4200D[2]) ;
  gyroADC[YAW]   =  -((rawADC_L3G4200D[5]<<8) | rawADC_L3G4200D[4]) ;
}
#endif



// **************************
// I2C Gyroscope ITG3200 
// **************************
// I2C adress: 0xD2 (8bit)   0x69 (7bit)  // for sparkfun breakout board default jumper
// I2C adress: 0xD0 (8bit)   0x68 (7bit)  // for FreeFlight IMU board default jumper
// principle:
// 1) VIO is connected to VDD
// 2) I2C adress is set to 0x69 (AD0 PIN connected to VDD)
// or 2) I2C adress is set to 0x68 (AD0 PIN connected to GND)
// 3) sample rate = 1000Hz ( 1kHz/(div+1) )
#if defined(ITG3200)
static uint8_t rawADC_ITG3200[6];

void i2c_Gyro_init() {
  delay(100);
  i2c_rep_start(0XD0+0);      // I2C write direction 
  i2c_write(0x3E);            // Power Management register
  i2c_write(0x80);            //   reset device
  i2c_write(0x16);            // register DLPF_CFG - low pass filter configuration & sample rate
  i2c_write(0x1D);            //   10Hz Low Pass Filter Bandwidth - Internal Sample Rate 1kHz
  i2c_write(0x3E);            // Power Management register
  i2c_write(0x01);            //   PLL with X Gyro reference
  
  gyroPresent = 1;  
}

void i2c_Gyro_getADC () {
  TWBR = ((16000000L / 400000L) - 16) / 2; // change the I2C clock rate to 400kHz
  i2c_rep_start(0XD0);     // I2C write direction
  i2c_write(0X1D);         // Start multiple read
  i2c_rep_start(0XD0 +1);  // I2C read direction => 1
  for(uint8_t i = 0; i < 5; i++) {
    rawADC_ITG3200[i]=i2c_readAck();}
  rawADC_ITG3200[5]= i2c_readNak();

  gyroADC[ROLL]  = + ( ((rawADC_ITG3200[2]<<8) | rawADC_ITG3200[3])/5/4 );
  gyroADC[PITCH] = - ( ((rawADC_ITG3200[0]<<8) | rawADC_ITG3200[1])/5/4 );
  gyroADC[YAW]   = - ( ((rawADC_ITG3200[4]<<8) | rawADC_ITG3200[5])/5/4 );
}
#endif

// **************************
// I2C Compass HMC5843 
// **************************
// I2C adress: 0x3C (8bit)   0x1E (7bit)

#if defined(HMC5843)
static uint8_t rawADC_HMC5843[6];

void i2c_Mag_init() { 
  delay(100);
  i2c_rep_start(0X3C+0);      // I2C write direction 
  i2c_write(0x02);            // Write to Mode register
  i2c_write(0x00);            //   Continuous-Conversion Mode
  magPresent = 1;  
}

void i2c_Mag_getADC() {
  static uint32_t t;
  if ( (micros()-t )  < 100000 ) return; //each read is spaced by 100ms
  t = micros();
  TWBR = ((16000000L / 400000L) - 16) / 2; // change the I2C clock rate to 400kHz
  i2c_rep_start(0X3C);     // I2C write direction
  i2c_write(0X03);         // Start multiple read
  i2c_rep_start(0X3C +1);  // I2C read direction => 1
  for(uint8_t i = 0; i < 5; i++) {
    rawADC_HMC5843[i]=i2c_readAck();}
  rawADC_HMC5843[5]= i2c_readNak();

  magADC[ROLL]  =   ((rawADC_HMC5843[0]<<8) | rawADC_HMC5843[1]);
  magADC[PITCH] =   ((rawADC_HMC5843[2]<<8) | rawADC_HMC5843[3]);
  magADC[YAW]   = - ((rawADC_HMC5843[4]<<8) | rawADC_HMC5843[5]);
}
#endif

// **************************
// I2C Compass HMC5883 
// **************************
// I2C adress: 0x3C (8bit)   0x1E (7bit)

#if defined(HMC5883)
static uint8_t rawADC_HMC5883[6];

void i2c_Mag_init() { 
  delay(100);
  i2c_rep_start(0X3C+0);      // I2C write direction 
  i2c_write(0x02);            // Write to Mode register
  i2c_write(0x00);            //   Continuous-Conversion Mode
  magPresent = 1;  
}

void i2c_Mag_getADC () {
  static uint32_t t;
  if ( (micros()-t )  < 100000 ) return; //each read is spaced by 100ms
  t = micros();
  TWBR = ((16000000L / 400000L) - 16) / 2; // change the I2C clock rate to 400kHz
  i2c_rep_start(0X3C);     // I2C write direction
  i2c_write(0X03);         // Start multiple read
  i2c_rep_start(0X3C +1);  // I2C read direction => 1
  for(uint8_t i = 0; i < 5; i++) {
    rawADC_HMC5883[i]=i2c_readAck();}
  rawADC_HMC5883[5]= i2c_readNak();

  magADC[ROLL]  =   ((rawADC_HMC5883[0]<<8) | rawADC_HMC5883[1]);
  magADC[PITCH] =   ((rawADC_HMC5883[4]<<8) | rawADC_HMC5883[5]); // there is a swap Z axis<->Y axis with HMC5843
  magADC[YAW]   = - ((rawADC_HMC5883[2]<<8) | rawADC_HMC5883[3]);
}
#endif



// **************************
// I2C Wii Motion Plus 
// **************************
// I2C adress 1: 0xA6 (8bit)    0x53 (7bit)
// I2C adress 2: 0xA4 (8bit)    0x52 (7bit)

static uint8_t rawADC_WMP[6];

void i2c_WMP_init(uint8_t d) {
  delay(d);
  i2c_rep_start(0xA6 + 0);//I2C write direction => 0
  i2c_write(0xF0); 
  i2c_write(0x55); 
  delay(d);
  i2c_rep_start(0xA6 + 0);//I2C write direction => 0
  i2c_write(0xFE); 
  i2c_write(0x05); 
  delay(d);
  if (d>0) {
    uint8_t numberAccRead = 0;
    for(uint8_t i=0;i<100;i++) {
      delay(3);
      if (rawIMU(0) == 0) numberAccRead++; // we detect here is nunchuk extension is available
    }
    if (numberAccRead>25)
      nunchukPresent = 1;
    delay(10);
  }
}

void i2c_WMP_getRawADC() {
  TWBR = ((16000000L / I2C_SPEED) - 16) / 2; // change the I2C clock rate
  i2c_rep_start(0xA4 + 0);//I2C write direction => 0
  i2c_write(0x00);
  i2c_rep_start(0xA4 + 1);//I2C read direction => 1
  for(uint8_t i = 0; i < 5; i++)
    rawADC_WMP[i]=i2c_readAck();
  rawADC_WMP[5]= i2c_readNak();
}

// **************************
// ADC ACC
// **************************
#if defined(ADCACC)
void adc_ACC_init(){
  pinMode(A1,INPUT);
  pinMode(A2,INPUT);
  pinMode(A3,INPUT);
}

void adc_ACC_getRawADC() {
  accADC[ROLL]  =  -analogRead(A1);
  accADC[PITCH] =  -analogRead(A2);
  accADC[YAW]   =  -analogRead(A3);

  acc_1G = 75;
  acc_25deg = 32; // = acc_1G * sin(25 deg)
  accPresent = 1;  
}
#endif

// **************
// gyro+acc IMU
// **************
static int16_t gyroData[3] = {0,0,0};
static int16_t gyroZero[3] = {0,0,0};
static int16_t accZero[3]  = {0,0,0};
static int16_t angle[2];      //absolute angle inclination in multiple of 0.1 degree
static int16_t accSmooth[3];  //projection of smoothed and normalized gravitation force vector on x/y/z axis, as measured by accelerometer


uint8_t rawIMU(uint8_t withACC) { //if the WMP or NK are oriented differently, it can be changed here
  if (withACC) {
    #if defined(I2C_ACC)
      i2c_ACC_getADC();
    #endif
    #if defined(ADCACC)
      adc_ACC_getRawADC();
    #endif  
  }
  #if defined(ITG3200) || defined(L3G4200D)
    i2c_Gyro_getADC();
    return 1;
  #else
    i2c_WMP_getRawADC();
    if ( (rawADC_WMP[5]&0x02) == 0x02 && (rawADC_WMP[5]&0x01) == 0 ) {// motion plus data
      gyroADC[ROLL]   = - ( ((rawADC_WMP[5]>>2)<<8) + rawADC_WMP[2] );
      gyroADC[PITCH]  = - ( ((rawADC_WMP[4]>>2)<<8) + rawADC_WMP[1] );
      gyroADC[YAW]    = - ( ((rawADC_WMP[3]>>2)<<8) + rawADC_WMP[0] );
      return 1;
    } else if ( (rawADC_WMP[5]&0x02) == 0 && (rawADC_WMP[5]&0x01) == 0) { //nunchuk data
      #if defined(I2C_ACC) || defined(ADCACC)
        return 2;
      #else
        accADC[ROLL]  =   ( (rawADC_WMP[3]<<2)        + ((rawADC_WMP[5]>>4)&0x2) );
        accADC[PITCH] = - ( (rawADC_WMP[2]<<2)        + ((rawADC_WMP[5]>>3)&0x2) );
        accADC[YAW]   = - ( ((rawADC_WMP[4]&0xFE)<<2) + ((rawADC_WMP[5]>>5)&0x6) );
        return 0;
      #endif
    } else
      return 2;
  #endif
}

uint8_t updateIMU(uint8_t withACC) {
  static int32_t g[3];
  static int32_t a[3];
  uint8_t axis;
  static int16_t previousGyroADC[3] = {0,0,0};
  uint8_t r;
  r=rawIMU(withACC);
  
  if (currentTime < (neutralizeTime + NEUTRALIZE_DELAY)) {//we neutralize data in case of blocking+hard reset state
    for (axis = 0; axis < 3; axis++) {gyroADC[axis]=0;accADC[axis]=0;}
    accADC[YAW] = acc_1G;
  } else {
    if (r == 1) { //gyro
      if (calibratingG>0) {
        for (axis = 0; axis < 3; axis++) {
          if (calibratingG>1) {
            if (calibratingG == 400) g[axis]=0;
            g[axis] +=gyroADC[axis];
            gyroADC[axis]=0;
          } else {
            gyroZero[axis]=(g[axis]+200)/399;
            blinkLED(10,15,1+3*nunchukPresent);
          }
        }
        calibratingG--;
      }
      #if defined(ITG3200) || defined(L3G4200D)
        gyroADC[ROLL]  = gyroADC[ROLL]  - gyroZero[ROLL];
        gyroADC[PITCH] = gyroADC[PITCH] - gyroZero[PITCH];
        gyroADC[YAW]   = gyroADC[YAW]   - gyroZero[YAW];
      #else
        gyroADC[ROLL]  = gyroADC[ROLL]  - gyroZero[ROLL];
        gyroADC[PITCH] = gyroADC[PITCH] - gyroZero[PITCH];
        gyroADC[YAW]   = gyroADC[YAW]   - gyroZero[YAW];
        gyroADC[ROLL]  = (rawADC_WMP[3]&0x01)     ? gyroADC[ROLL]/5  : gyroADC[ROLL] ;   //the ratio 1/5 is not exactly the IDG600 or ISZ650 specification 
        gyroADC[PITCH] = (rawADC_WMP[4]&0x02)>>1  ? gyroADC[PITCH]/5 : gyroADC[PITCH] ;  //we detect here the slow of fast mode WMP gyros values (see wiibrew for more details)
        gyroADC[YAW]   = (rawADC_WMP[3]&0x02)>>1  ? gyroADC[YAW]/5   : gyroADC[YAW] ;
      #endif
      //anti gyro glitch, limit the variation between two consecutive readings
      for (axis = 0; axis < 3; axis++) {
        gyroADC[axis] = constrain(gyroADC[axis],previousGyroADC[axis]-100,previousGyroADC[axis]+100);
        previousGyroADC[axis] = gyroADC[axis];
      }
    }
    if (r == 0 || ( (accPresent == 1) && (withACC == 1) ) ) { //nunchuk or i2c ACC
      if (calibratingA>0) {
        if (calibratingA>1) {
          for (uint8_t axis = 0; axis < 3; axis++) {
            if (calibratingA == 400) a[axis]=0;
            a[axis] +=accADC[axis];
            accADC[axis]=0;
          }
        } else {
          accZero[ROLL]  = (a[ROLL]+200)/399;
          accZero[PITCH] = (a[PITCH]+200)/399;
          accZero[YAW]   = (a[YAW]+200)/399+acc_1G; // for nunchuk 200=1G
          writeParams(); // write accZero in EEPROM
        }
        calibratingA--;
      } else {
        accADC[ROLL]  =    accADC[ROLL]  - accZero[ROLL] ;
        accADC[PITCH] =    accADC[PITCH] - accZero[PITCH];
        accADC[YAW]   = - (accADC[YAW]   - accZero[YAW]) ;
      }
    }
  }  
  return r;
}

void computeIMU () {
  uint8_t axis;
  static int16_t gyroADCprevious[3] = {0,0,0};
  static int16_t gyroADCp[3] = {0,0,0};
  int16_t gyroADCinter[3];
  static int16_t lastAccADC[3] = {0,0,0};
  static int16_t similarNumberAccData[3];
  static int16_t gyroDeviation[3];
  static uint32_t timeInterleave;
  static int16_t gyroYawSmooth = 0;

  //we separate the 2 situations because reading gyro values with a gyro only setup can be acchieved at a higher rate
  //gyro+nunchuk: we must wait for a quite high delay betwwen 2 reads to get both WM+ and Nunchuk data. It works with 3ms
  //gyro only: the delay to read 2 consecutive values can be reduced to only 0.65ms
  if (nunchukPresent) {
    annexCode();
    while((micros()-timeInterleave)<INTERLEAVING_DELAY) ; //interleaving delay between 2 consecutive reads
    timeInterleave=micros();
    updateIMU(0);
    getEstimatedAttitude(); // computation time must last less than one interleaving delay
    while((micros()-timeInterleave)<INTERLEAVING_DELAY) ; //interleaving delay between 2 consecutive reads
    timeInterleave=micros();
    while(updateIMU(0) != 1) ; // For this interleaving reading, we must have a gyro update at this point (less delay)

    for (axis = 0; axis < 3; axis++) {
      // empirical, we take a weighted value of the current and the previous values
      gyroData[axis] = (gyroADC[axis]*3+gyroADCprevious[axis]+16)/4/8; // /4 is to average 4 values ; /8 is to reduce the sensibility of gyro
      gyroADCprevious[axis] = gyroADC[axis];
    }
  } else {
    #if defined(I2C_ACC) || defined(ADCACC)
      getEstimatedAttitude();
      updateIMU(1); //with I2C or ADC ACC
    #else
      updateIMU(0); //without ACC
    #endif
    for (axis = 0; axis < 3; axis++)
      gyroADCp[axis] =  gyroADC[axis];
    timeInterleave=micros();
    annexCode();
    while((micros()-timeInterleave)<650) ; //empirical, interleaving delay between 2 consecutive reads
    updateIMU(0); //without ACC
    for (axis = 0; axis < 3; axis++) {
      gyroADCinter[axis] =  gyroADC[axis]+gyroADCp[axis];
      // empirical, we take a weighted value of the current and the previous values
      gyroData[axis] = (gyroADCinter[axis]+gyroADCprevious[axis]+12)/3/8; // /3 is to average 3 values ; /8 is to reduce the sensibility of gyro
      gyroADCprevious[axis] = gyroADCinter[axis]/2;
      #if not defined (I2C_ACC) && not defined (ADCACC)
        accADC[axis]=0;
      #endif
    }
  }
  #if defined(TRI)
    gyroData[YAW] = (gyroYawSmooth*2+gyroData[YAW]+1)/3;
    gyroYawSmooth = gyroData[YAW];
  #endif
}


// ************************************
// simplified IMU based on Kalman Filter
// inspired from http://starlino.com/imu_guide.html
// and http://www.starlino.com/imu_kalman_arduino.html
// for angles under 25deg, we use an approximation to speed up the angle calculation
// magnetometer addition for small angles
// ************************************
void getEstimatedAttitude(){
  uint8_t axis;  
  float R, RGyro[3];                 //R obtained from last estimated value and gyro movement;
  static float REst[3] = {0,0,1} ;   // init acc in stable mode
  static float A[2];                 //angles between projection of R on XZ/YZ plane and Z axis (in Radian)
  float wGyro = 300;               // gyro weight/smooting factor
  float invW = 1.0/(1 + 300);
  float gyroFactor;
  static uint8_t small_angle=1;
  static uint16_t tPrevious;
  uint16_t tCurrent,deltaTime;
  float a[2], mag[2], cos_[2];

  tCurrent = micros();
  deltaTime = tCurrent-tPrevious;
  tPrevious = tCurrent;

  #if defined(ITG3200) || defined(L3G4200D)
    gyroFactor = deltaTime/300e6; //empirical
  #else
    gyroFactor = deltaTime/200e6; //empirical, depends on WMP on IDG datasheet, tied of deg/ms sensibility
  #endif
  
  for (axis=0;axis<2;axis++) a[axis] = gyroADC[axis]  * gyroFactor;
  for (axis=0;axis<3;axis++) accSmooth[axis] =(accSmooth[axis]*7+accADC[axis]+4)/8;
  
  if(accSmooth[YAW] > 0 ){ //we want to be sure we are not flying inverted  
    // a very nice trigonometric approximation: under 25deg, the error of this approximation is less than 1 deg:
    //   sin(x) =~= x =~= arcsin(x)
    //   angle_axis = arcsin(ACC_axis/ACC_1G) =~= ACC_axis/ACC_1G
    // the angle calculation is much more faster in this case
    if (accSmooth[ROLL]<acc_25deg && accSmooth[ROLL]>-acc_25deg && accSmooth[PITCH]<acc_25deg && accSmooth[PITCH]>-acc_25deg) {
      for (axis=0;axis<2;axis++) {
        A[axis] +=a[axis];
        A[axis] = ((float)accSmooth[axis]/acc_1G + A[axis]*wGyro)*invW; // =~= sin axis
        #if defined(HMC5843) || defined(HMC5883)
          cos_[axis] = 1-A[axis]*A[axis]/2; // cos(x) =~= 1-x^2/2
        #endif
      } 
      small_angle=1;
    } else {
      //magnitude vector size
      R = sqrt(square(accSmooth[ROLL]) + square(accSmooth[PITCH]) + square(accSmooth[YAW]));
      for (axis=0;axis<2;axis++) {
        if ( acc_1G*3/5 < R && R < acc_1G*7/5 && small_angle == 0 ) //if accel magnitude >1.4G or <0.6G => we neutralize the effect of accelerometers in the angle estimation
          A[axis] = atan2(REst[axis],REst[YAW]);
        A[axis] +=a[axis];
        cos_[axis] = cos(A[axis]);
        RGyro[axis]  = sin(A[axis])  / sqrt( 1.0 + square(cos_[axis])  * square(tan(A[1-axis]))); //reverse calculation of RwGyro from Awz angles
      }
      RGyro[YAW] = sqrt(abs(1.0 - square(RGyro[ROLL]) - square(RGyro[PITCH])));
      for (axis=0;axis<3;axis++)
        REst[axis] = (accADC[axis]/R + wGyro* RGyro[axis])  * invW; //combine Accelerometer and gyro readings
      small_angle=0;
    }
    #if defined(HMC5843) || defined(HMC5883)
      mag[PITCH] = -magADC[PITCH]*cos_[PITCH]+magADC[ROLL]*A[ROLL]*A[PITCH]+magADC[YAW]*cos_[ROLL]*A[PITCH];
      mag[ROLL] = magADC[ROLL]*cos_[ROLL]-magADC[YAW]*A[ROLL];
      heading = degrees(atan2(mag[PITCH],mag[ROLL]));
    #endif
  }
  for (axis=0;axis<2;axis++) angle[axis] = A[axis]*570.29577951; //angle in multiple of 0.1 degree
}


// *************************
// motor and servo functions
// *************************
uint8_t PWM_PIN[6] = {MOTOR_ORDER};
  
static int16_t axisPID[3];
static int16_t motor[6];
static int16_t servo[4] = {1500,1500,1500,1500};
volatile uint8_t atomicServo[4] = {250,250,250,250};

//for HEX Y6 and HEX6/HEX6X flat
volatile uint8_t atomicPWM_PIN5_lowState;
volatile uint8_t atomicPWM_PIN5_highState;
volatile uint8_t atomicPWM_PIN6_lowState;
volatile uint8_t atomicPWM_PIN6_highState;

void writeMotors() { // [1000;2000] => [125;250]
  for(uint8_t i=0;i<min(NUMBER_MOTOR,4);i++)
    analogWrite(PWM_PIN[i], motor[i]>>3);
  #if (NUMBER_MOTOR == 6) && defined(MEGA)
    analogWrite(PWM_PIN[4], motor[4]>>3);
    analogWrite(PWM_PIN[5], motor[5]>>3);
  #endif
  #if (NUMBER_MOTOR == 6) && defined(PROMINI)
    atomicPWM_PIN5_highState = motor[5]/8;
    atomicPWM_PIN5_lowState = 255-atomicPWM_PIN5_highState;
    atomicPWM_PIN6_highState = motor[4]/8;
    atomicPWM_PIN6_lowState = 255-atomicPWM_PIN6_highState;
  #endif
}

void writeAllMotors(int16_t mc) {   // Sends commands to all motors
  for (uint8_t i =0;i<NUMBER_MOTOR;i++)
    motor[i]=mc;
  writeMotors();
}

void initializeMotors() {
  for(uint8_t i=0;i<NUMBER_MOTOR;i++)
    pinMode(PWM_PIN[i],OUTPUT);
  writeAllMotors(1000);
  delay(300);
}

#if defined(SERVO)
void initializeServo() {
  #if defined(TRI)
    DIGITAL_SERVO_TRI_PINMODE
  #endif
  #if defined(SERVO_TILT) || defined(GIMBAL) || defined(FLYING_WING)
    DIGITAL_TILT_ROLL_PINMODE
    DIGITAL_TILT_PITCH_PINMODE
  #endif
  #if defined(CAMTRIG)
    DIGITAL_CAM_PINMODE
  #endif
  #if defined(BI)
    DIGITAL_SERVO_TRI_PINMODE
    DIGITAL_BI_LEFT_PINMODE
  #endif
  TCCR0A = 0; // normal counting mode
  TIMSK0 |= (1<<OCIE0A); // Enable CTC interrupt
}

// ****servo yaw with a 50Hz refresh rate****
// prescaler is set by default to 64 on Timer0
// Duemilanove : 16MHz / 64 => 4 us
// 256 steps = 1 counter cycle = 1024 us
// algorithm strategy:
// pulse high servo 0 -> do nothing for 1000 us -> do nothing for [0 to 1000] us -> pulse down servo 0
// pulse high servo 1 -> do nothing for 1000 us -> do nothing for [0 to 1000] us -> pulse down servo 1
// pulse high servo 2 -> do nothing for 1000 us -> do nothing for [0 to 1000] us -> pulse down servo 2
// pulse high servo 3 -> do nothing for 1000 us -> do nothing for [0 to 1000] us -> pulse down servo 3
// do nothing for 14 x 1000 us
ISR(TIMER0_COMPA_vect) {
  static uint8_t state = 0;
  static uint8_t count;
  if (state == 0) {
    //http://billgrundmann.wordpress.com/2009/03/03/to-use-or-not-use-writedigital/
    #if defined(TRI) || defined (BI)
      DIGITAL_SERVO_TRI_HIGH
    #endif
    OCR0A+= 250; // 1000 us
    state++ ;
  } else if (state == 1) {
    OCR0A+= atomicServo[0]; // 1000 + [0-1020] us
    state++;
  } else if (state == 2) {
    #if defined(TRI) || defined (BI)
      DIGITAL_SERVO_TRI_LOW
    #endif
    #if defined(BI)
      DIGITAL_BI_LEFT_HIGH
    #endif
    #if defined(SERVO_TILT) || defined(GIMBAL) || defined(FLYING_WING)
      DIGITAL_TILT_PITCH_HIGH
    #endif
    OCR0A+= 250; // 1000 us
    state++;
  } else if (state == 3) {
    OCR0A+= atomicServo[1]; // 1000 + [0-1020] us
    state++;
  } else if (state == 4) {
    #if defined(SERVO_TILT) || defined(GIMBAL) || defined(FLYING_WING)
      DIGITAL_TILT_PITCH_LOW
      DIGITAL_TILT_ROLL_HIGH
    #endif
    #if defined(BI)
      DIGITAL_BI_LEFT_LOW
    #endif
    state++;
    OCR0A+= 250; // 1000 us
  } else if (state == 5) {
    OCR0A+= atomicServo[2]; // 1000 + [0-1020] us
    state++;
  } else if (state == 6) {
    #if defined(SERVO_TILT) || defined(GIMBAL) || defined(FLYING_WING)
      DIGITAL_TILT_ROLL_LOW
    #endif
    #if defined(CAMTRIG)
      DIGITAL_CAM_HIGH
    #endif
    state++;
    OCR0A+= 250; // 1000 us
  } else if (state == 7) {
    OCR0A+= atomicServo[3]; // 1000 + [0-1020] us
    state++;
  } else if (state == 8) {
    #if defined(CAMTRIG)
      DIGITAL_CAM_LOW
    #endif
    count = 10; // 12 x 1000 us
    state++;
    OCR0A+= 250; // 1000 us
  } else if (state == 9) {
    if (count > 0) count--;
    else state = 0;
    OCR0A+= 250;
  }
}
#endif

#if (NUMBER_MOTOR == 6) && defined(PROMINI)
void initializeSoftPWM() {
  TCCR0A = 0; // normal counting mode
  TIMSK0 |= (1<<OCIE0A); // Enable CTC interrupt
  TIMSK0 |= (1<<OCIE0B);
}

ISR(TIMER0_COMPA_vect) {
  static uint8_t state = 0;
  if (state == 0) {
    PORTD |= 1<<5; //digital PIN 5 high
    OCR0A+= atomicPWM_PIN5_highState; //250 x 4 microsecons = 1ms
    state = 1;
  } else if (state == 1) {
    OCR0A+= atomicPWM_PIN5_highState;
    state = 2;
  } else if (state == 2) {
    PORTD &= ~(1<<5); //digital PIN 5 low
    OCR0A+= atomicPWM_PIN5_lowState;
    state = 0;
  }
}

ISR(TIMER0_COMPB_vect) { //the same with digital PIN 6 and OCR0B counter
  static uint8_t state = 0;
  if (state == 0) {
    PORTD |= 1<<6;OCR0B+= atomicPWM_PIN6_highState;state = 1;
  } else if (state == 1) {
    OCR0B+= atomicPWM_PIN6_highState;state = 2;
  } else if (state == 2) {
    PORTD &= ~(1<<6);OCR0B+= atomicPWM_PIN6_lowState;state = 0;
  }
}
#endif

// ******************
// rc functions
// ******************
#define MINCHECK 1100
#define MAXCHECK 1900

volatile int16_t failsafeCnt = 0;

static uint8_t pinRcChannel[8] = {ROLLPIN, PITCHPIN, YAWPIN, THROTTLEPIN, AUX1PIN,AUX2PIN,CAM1PIN,CAM2PIN};
volatile uint16_t rcPinValue[8] = {1500,1500,1500,1500,1500,1500,1500,1500}; // interval [1000;2000]
static int16_t rcData[8] ; // interval [1000;2000]
static int16_t rcCommand[4] ; // interval [1000;2000] for THROTTLE and [-500;+500] for ROLL/PITCH/YAW 
static int16_t rcHysteresis[8] ;
static int16_t rcData4Values[8][4];

static uint8_t rcRate8;
static uint8_t rcExpo8;
static float rcFactor1; 
static float rcFactor2;

// ***PPM SUM SIGNAL***
#ifdef SERIAL_SUM_PPM
static uint8_t rcChannel[8] = {SERIAL_SUM_PPM};
#endif
volatile uint16_t rcValue[8] = {1500,1500,1500,1500,1500,1500,1500,1500}; // interval [1000;2000]

// Configure each rc pin for PCINT
void configureReceiver() {
  #ifndef SERIAL_SUM_PPM
    for (uint8_t chan = 0; chan < 8; chan++)
      for (uint8_t a = 0; a < 4; a++)
        rcData4Values[chan][a] = 1500; //we initiate the default value of each channel. If there is no RC receiver connected, we will see those values
    #if defined(PROMINI)
      // PCINT activated only for specific pin inside [D0-D7]  , [D2 D4 D5 D6 D7] for this multicopter
      PORTD   = (1<<2) | (1<<4) | (1<<5) | (1<<6) | (1<<7); //enable internal pull ups on the PINs of PORTD (no high impedence PINs)
      PCMSK2 |= (1<<2) | (1<<4) | (1<<5) | (1<<6) | (1<<7); 
      PCICR   = 1<<2; // PCINT activated only for the port dealing with [D0-D7] PINs
    #endif
    #if defined(MEGA)
      // PCINT activated only for specific pin inside [A8-A15]
      DDRK = 0;  // defined PORTK as a digital port ([A8-A15] are consired as digital PINs and not analogical)
      PORTK   = (1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<4) | (1<<5) | (1<<6) | (1<<7); //enable internal pull ups on the PINs of PORTK
      PCMSK2 |= (1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<4) | (1<<5) | (1<<6) | (1<<7);
      PCICR   = 1<<2; // PCINT activated only for PORTK dealing with [A8-A15] PINs
    #endif
  #else
    PPM_PIN_INTERRUPT
  #endif
}

#ifndef SERIAL_SUM_PPM
ISR(PCINT2_vect) { //this ISR is common to every receiver channel, it is call everytime a change state occurs on a digital pin [D2-D7]
  uint8_t mask;
  uint8_t pin;
  uint16_t cTime,dTime;
  static uint16_t edgeTime[8];
  static uint8_t PCintLast;

 
stiegl_1492
Beiträge: 28
Registriert: 07.04.2011 22:39:07

#4384 Re: Der WII-Copter ( Selbstbau-Projekt )

Beitrag von stiegl_1492 »

und Teil 2

Code: Alles auswählen

 #if defined(PROMINI)
    pin = PIND;             // PIND indicates the state of each PIN for the arduino port dealing with [D0-D7] digital pins (8 bits variable)
  #endif
  #if defined(MEGA)
    pin = PINK;             // PINK indicates the state of each PIN for the arduino port dealing with [A8-A15] digital pins (8 bits variable)
  #endif
  mask = pin ^ PCintLast;   // doing a ^ between the current interruption and the last one indicates wich pin changed
  sei();                    // re enable other interrupts at this point, the rest of this interrupt is not so time critical and can be interrupted safely
  PCintLast = pin;          // we memorize the current state of all PINs [D0-D7]

  cTime = micros();         // micros() return a uint32_t, but it is not usefull to keep the whole bits => we keep only 16 bits
  
  // mask is pins [D0-D7] that have changed // the principle is the same on the MEGA for PORTK and [A8-A15] PINs
  // chan = pin sequence of the port. chan begins at D2 and ends at D7
  if (mask & 1<<2)           //indicates the bit 2 of the arduino port [D0-D7], that is to say digital pin 2, if 1 => this pin has just changed
    if (!(pin & 1<<2)) {     //indicates if the bit 2 of the arduino port [D0-D7] is not at a high state (so that we match here only descending PPM pulse)
      dTime = cTime-edgeTime[2]; if (900<dTime && dTime<2200) rcPinValue[2] = dTime; // just a verification: the value must be in the range [1000;2000] + some margin
    } else edgeTime[2] = cTime;    // if the bit 2 of the arduino port [D0-D7] is at a high state (ascending PPM pulse), we memorize the time
  if (mask & 1<<4)   //same principle for other channels   // avoiding a for() is more than twice faster, and it's important to minimize execution time in ISR
    if (!(pin & 1<<4)) {
      dTime = cTime-edgeTime[4]; if (900<dTime && dTime<2200) rcPinValue[4] = dTime;
    } else edgeTime[4] = cTime;
  if (mask & 1<<5)
    if (!(pin & 1<<5)) {
      dTime = cTime-edgeTime[5]; if (900<dTime && dTime<2200) rcPinValue[5] = dTime;
    } else edgeTime[5] = cTime;
  if (mask & 1<<6)
    if (!(pin & 1<<6)) {
      dTime = cTime-edgeTime[6]; if (900<dTime && dTime<2200) rcPinValue[6] = dTime;
    } else edgeTime[6] = cTime;
  if (mask & 1<<7)
    if (!(pin & 1<<7)) {
      dTime = cTime-edgeTime[7]; if (900<dTime && dTime<2200) rcPinValue[7] = dTime;
    } else edgeTime[7] = cTime;
  #if defined(MEGA)
    if (mask & 1<<0)    
      if (!(pin & 1<<0)) {
        dTime = cTime-edgeTime[0]; if (900<dTime && dTime<2200) rcPinValue[0] = dTime; 
      } else edgeTime[0] = cTime; 
    if (mask & 1<<1)      
      if (!(pin & 1<<1)) {
        dTime = cTime-edgeTime[1]; if (900<dTime && dTime<2200) rcPinValue[1] = dTime; 
      } else edgeTime[1] = cTime;
    if (mask & 1<<3)
      if (!(pin & 1<<3)) {
        dTime = cTime-edgeTime[3]; if (900<dTime && dTime<2200) rcPinValue[3] = dTime;
      } else edgeTime[3] = cTime;
  #endif
  if (mask & 1<<THROTTLEPIN) {    // If pulse present on THROTTLE pin (independent from ardu version), clear FailSafe counter  - added by MIS
    if(failsafeCnt > 20) failsafeCnt -= 20; else failsafeCnt = 0; }
}

#else 
void rxInt() {
  uint16_t now,diff;
  static uint16_t last = 0;
  static uint8_t chan = 0;

  now = micros();
  diff = now - last;
  last = now;
  if(diff>5000) chan = 0;
  else {
    if(900<diff && diff<2200 && chan<8 ) rcValue[chan] = diff;
    chan++;
    if(failsafeCnt > 20) failsafeCnt -= 20; else failsafeCnt = 0;   // clear FailSafe counter - added by MIS
  }
}
#endif

uint16_t readRawRC(uint8_t chan) {
  uint16_t data;
  uint8_t oldSREG;
  oldSREG = SREG;
  cli(); // Let's disable interrupts
  #ifndef SERIAL_SUM_PPM
    data = rcPinValue[pinRcChannel[chan]]; // Let's copy the data Atomically
  #else
    data = rcValue[rcChannel[chan]]; 
  #endif
  SREG = oldSREG;
  sei();// Let's enable the interrupts
  return data; // We return the value correctly copied when the IRQ's where disabled
}
  
void computeRC() {
  static uint8_t rc4ValuesIndex = 0;
  uint8_t chan,a;

  rc4ValuesIndex++;
  for (chan = 0; chan < 8; chan++) {
    rcData4Values[chan][rc4ValuesIndex%4] = readRawRC(chan);
    rcData[chan] = 0;
    for (a = 0; a < 4; a++)
      rcData[chan] += rcData4Values[chan][a];
    rcData[chan]= (rcData[chan]+2)/4;
    if ( rcData[chan] < rcHysteresis[chan] -3)  rcHysteresis[chan] = rcData[chan]+2;
    if ( rcData[chan] > rcHysteresis[chan] +3)  rcHysteresis[chan] = rcData[chan]-2;
  }
}


// ****************
// EEPROM functions
// ****************
static uint8_t P8[3], I8[3], D8[3]; //8 bits is much faster and the code is much shorter
static uint8_t dynP8[3], dynI8[3], dynD8[3]; 
static uint8_t PLEVEL8,ILEVEL8;
static uint8_t rollPitchRate;
static uint8_t yawRate;
static uint8_t dynThrPID;
static uint8_t checkNewConf = 134;
static uint8_t activateAcc8,activateBaro8,activateMag8;
static uint8_t activateCamStab8,activateCamTrig8;

void readEEPROM() {
  uint8_t i,p=1;
  for(i=0;i<3;i++) {P8[i] = EEPROM.read(p++);I8[i] = EEPROM.read(p++);D8[i] = EEPROM.read(p++);}
  PLEVEL8 = EEPROM.read(p++);ILEVEL8 = EEPROM.read(p++);
  rcRate8 = EEPROM.read(p++);rcExpo8 = EEPROM.read(p++);
  rollPitchRate = EEPROM.read(p++);
  yawRate = EEPROM.read(p++);
  dynThrPID = EEPROM.read(p++);
  activateAcc8 = EEPROM.read(p++);activateBaro8 = EEPROM.read(p++);activateMag8 = EEPROM.read(p++);
  activateCamStab8 = EEPROM.read(p++);activateCamTrig8 = EEPROM.read(p++);
  for(i=0;i<3;i++) accZero[i] = (EEPROM.read(p++)&0xff) + (EEPROM.read(p++)<<8);
  //note on the following lines: we do this calcul here because it's a static and redundant result and we don't want to load the critical loop whith it
  rcFactor1 = rcRate8/50.0*rcExpo8/100.0/250000.0;
  rcFactor2 = (100-rcExpo8)*rcRate8/5000.0;
}

void writeParams() {
  uint8_t i,p=1;
  EEPROM.write(0, checkNewConf);
  for(i=0;i<3;i++) {EEPROM.write(p++,P8[i]);  EEPROM.write(p++,I8[i]);  EEPROM.write(p++,D8[i]);}
  EEPROM.write(p++,PLEVEL8);EEPROM.write(p++,ILEVEL8);
  EEPROM.write(p++,rcRate8);EEPROM.write(p++,rcExpo8);
  EEPROM.write(p++,rollPitchRate);
  EEPROM.write(p++,yawRate);
  EEPROM.write(p++,dynThrPID);
  EEPROM.write(p++,activateAcc8);EEPROM.write(p++,activateBaro8);EEPROM.write(p++,activateMag8);
  EEPROM.write(p++,activateCamStab8);EEPROM.write(p++,activateCamTrig8);
  for(i=0;i<3;i++) {EEPROM.write(p++,accZero[i]);EEPROM.write(p++,accZero[i]>>8&0xff);}
  readEEPROM();
  blinkLED(15,20,1);
}

void checkFirstTime() {
  if ( EEPROM.read(0) != checkNewConf ) {
    P8[ROLL] = 40; I8[ROLL] = 30; D8[ROLL] = 15;
    P8[PITCH] = 40; I8[PITCH] = 30; D8[PITCH] = 15;
    P8[YAW]  = 80; I8[YAW]  = 0;  D8[YAW]  = 0;
    PLEVEL8 = 140; ILEVEL8 = 45;
    rcRate8 = 45;
    rcExpo8 = 65;
    rollPitchRate = 0;
    yawRate = 0;
    dynThrPID = 0;
    activateAcc8 = 0;activateBaro8 = 0;activateMag8 = 0;
    activateCamStab8 = 0;activateCamTrig8 = 0;
    writeParams();
  }
}

// *****************************
// LCD & display & monitoring
// *****************************

// 1000000 / 9600  = 104 microseconds at 9600 baud.
// we set it below to take some margin with the running interrupts
#define BITDELAY 102
void LCDprint(uint8_t i) {
  #if defined(LCD_TEXTSTAR)
    Serial.print( i , BYTE);
  #else
    LCDPIN_OFF
    delayMicroseconds(BITDELAY);
    for (uint8_t mask = 0x01; mask; mask <<= 1) {
      if (i & mask) LCDPIN_ON else LCDPIN_OFF // choose bit
      delayMicroseconds(BITDELAY);
    }
    LCDPIN_ON //switch ON digital PIN 0
    delayMicroseconds(BITDELAY);
  #endif
}

void LCDprintChar(const char *s) {
  while (*s) LCDprint(*s++);
}

void initLCD() {
  blinkLED(20,30,1);
  #if defined(LCD_TEXTSTAR)
    // Cat's Whisker Technologies 'TextStar' Module CW-LCD-02
    // http://cats-whisker.com/resources/documents/cw-lcd-02_datasheet.pdf
    // Modified by Luca Brizzi aka gtrick90 @ RCG
    LCDprint(0xFE);LCDprint(0x43);LCDprint(0x02); //cursor blink mode
    LCDprint(0x0c); //clear screen
    LCDprintChar("MultiWii");
    LCDprint(0x0d); // carriage return
    LCDprintChar("CONFIG PARAMS");
    delay(2500);
    LCDprint(0x0c); //clear screen
  #else
    Serial.end();
    //init LCD
    PINMODE_LCD //TX PIN for LCD = Arduino RX PIN (more convenient to connect a servo plug on arduino pro mini)
  #endif
}

void configurationLoop() {
  uint8_t chan,i;
  uint8_t param,paramActive;
  uint8_t val,valActive;
  static char line1[17],line2[17];
  uint8_t LCD=1;
  uint8_t refreshLCD = 1;

  typedef struct {
    char*    paramText;
    uint8_t* var;
    uint8_t  decimal;
    uint8_t  increment;
  } paramStruct;
  
  static paramStruct p[] = {
  {"PITCH&ROLL P", &P8[ROLL],1,1}, {"ROLL   P", &P8[ROLL],1,1},  {"ROLL   I", &I8[ROLL],3,5},  {"ROLL   D", &D8[ROLL],0,1},
  {"PITCH  P", &P8[PITCH],1,1}, {"PITCH  I", &I8[PITCH],3,5}, {"PITCH  D", &D8[PITCH],0,1},
  {"YAW    P", &P8[YAW],1,1},   {"YAW    I", &I8[YAW],3,5},   {"YAW    D", &D8[YAW],0,1},
  {"LEVEL  P", &PLEVEL8,1,1},   {"LEVEL  I", &ILEVEL8,3,5},
  {"RC RATE", &rcRate8,2,2},     {"RC EXPO", &rcExpo8,2,2},
  {"PITCH&ROLL RATE", &rollPitchRate,2,2}, {"YAW RATE", &yawRate,2,2},
  {"THROTTLE PID", &dynThrPID,2,2},
  };

  initLCD();
  param = 0;
  while (LCD == 1) {
    if (refreshLCD == 1) {
      strcpy(line2,"                ");
      strcpy(line1,"                ");
      i=0; char* point = p[param].paramText; while (*point) line1[i++] = *point++;
      uint16_t unit = *p[param].var;
      if (param == 12) {unit *=2;} // RC RATE can go up to 500
      char c1 = '0'+unit/100; char c2 = '0'+unit/10-(unit/100)*10; char c3 = '0'+unit-(unit/10)*10;
      if (p[param].decimal == 0) {line2[6] = c1;  line2[7] = c2;   line2[8] = c3;}
      if (p[param].decimal == 1) {line2[5] = c1;  line2[6] = c2;   line2[7] = '.'; line2[8] = c3;}
      if (p[param].decimal == 2) {line2[5] = c1;  line2[6] = '.';  line2[7] = c2;  line2[8] = c3;}
      if (p[param].decimal == 3) {line2[4] = '0'; line2[5] = '.';  line2[6] = c1;  line2[7] = c2; line2[8] = c3;}

      #if defined(LCD_TEXTSTAR)
        LCDprint(0xFE);LCDprint('L');LCDprint(1);LCDprintChar(line1); //refresh line 1 of LCD
        LCDprint(0xFE);LCDprint('L');LCDprint(2);LCDprintChar(line2); //refresh line 2 of LCD
      #else
        LCDprint(0xFE);LCDprint(128);LCDprintChar(line1);
        LCDprint(0xFE);LCDprint(192);LCDprintChar(line2);
      #endif
      refreshLCD=0;
    }
    for (chan = ROLL; chan < 4; chan++) rcData[chan] = readRawRC(chan);
    //switch config param with pitch
    if (rcData[PITCH] < MINCHECK && paramActive == 0 && param<16) {
      paramActive = 1;refreshLCD=1;blinkLED(10,20,1);
      param++;
    }
    if (rcData[PITCH] > MAXCHECK && paramActive == 0 && param>0) {
      paramActive = 1;refreshLCD=1;blinkLED(10,20,1);
      param--; 
    }
    if (rcData[PITCH] < MAXCHECK && rcData[PITCH] > MINCHECK)  paramActive = 0;
    //+ or - param with low and high roll
    if (rcData[ROLL] < MINCHECK && valActive == 0 && *p[param].var>p[param].increment-1) {
      valActive = 1;refreshLCD=1;blinkLED(10,20,1);
      *p[param].var -= p[param].increment;  //set val -
      if (param == 0) *p[4].var = *p[0].var; //PITCH P
    }
    if (rcData[ROLL] > MAXCHECK && valActive == 0) {
      valActive = 1;refreshLCD=1;blinkLED(10,20,1);
      *p[param].var += p[param].increment;       //set val +
      if (param == 0) *p[4].var = *p[0].var; //PITCH P
    }
    if (rcData[ROLL] < MAXCHECK && rcData[ROLL]  > MINCHECK) valActive = 0;
    if (rcData[YAW]  < MINCHECK && rcData[PITCH] > MAXCHECK) LCD = 0;
  }
  #if defined(LCD_TEXTSTAR)
    blinkLED(20,30,1);
    LCDprint(0x0c); //clear screen
    LCDprintChar("Saving Settings...");
  #endif
  writeParams();
  #if defined(LCD_TEXTSTAR)
    LCDprintChar("..done! Exit.");
  #else
    Serial.begin(SERIAL_COM_SPEED);
  #endif
}


void blinkLED(uint8_t num, uint8_t wait,uint8_t repeat) {
  uint8_t i,r;
  for (r=0;r<repeat;r++) {
    for(i=0;i<num;i++) {
      LEDPIN_SWITCH //switch LEDPIN state
      BUZZERPIN_ON
      delay(wait);
      BUZZERPIN_OFF
    }
    delay(60);
  }
}


void annexCode() { //this code is excetuted at each loop and won't interfere with control loop if it lasts less than 650 microseconds
  static uint32_t serialTime;
  static uint32_t buzzerTime;
  static uint32_t calibrateTime;
  static uint32_t calibratedAccTime;
  static uint8_t  buzzerState = 0;
  uint8_t axis;
  uint8_t prop1,prop2;

  for(axis=0;axis<2;axis++) {
    //PITCH & ROLL dynamic PID adjustemnt, depending on stick deviation
    prop1 = 100-min(abs(rcData[axis]-1500)/5,100)*rollPitchRate/100;
    //PITCH & ROLL only dynamic PID adjustemnt,  depending on throttle value
    if (rcData[THROTTLE]<1500)                               prop2 = 100;
    else if (rcData[THROTTLE]>1499 && rcData[THROTTLE]<2000) prop2 = 100 - (rcData[THROTTLE]-1500) * dynThrPID/500;
    else                                                     prop2 = 100 - dynThrPID;
    dynP8[axis] = P8[axis]*prop1/100*prop2/100;
    dynD8[axis] = D8[axis]*prop1/100*prop2/100;
  }
  
  //YAW dynamic PID adjustemnt
  prop1 = 100-min(abs(rcData[YAW]-1500)/5,100)*yawRate/100;
  dynP8[YAW] = P8[YAW]*prop1/100;
  dynD8[YAW] = D8[YAW]*prop1/100;


  vbat = analogRead(V_BATPIN)*16 / VBATSCALE;                        // result is Vbatt in 0.1V steps

  if (vbat>VBATLEVEL1_3S) {                                          //VBAT ok, buzzer off
    BUZZERPIN_OFF
  } else if ((vbat>VBATLEVEL2_3S) && (vbat<VBATLEVEL1_3S)) {   //First level 0.25s beep spacing 1s
    if (buzzerState && (currentTime > buzzerTime + 250000) ) {
      buzzerState = 0;BUZZERPIN_OFF;buzzerTime = currentTime;
    } else if ( !buzzerState && (currentTime > buzzerTime + 1000000) ) {
      buzzerState = 1;BUZZERPIN_ON;buzzerTime = currentTime;
    }
  } else if ((vbat>VBATLEVEL3_3S) && (vbat<VBATLEVEL2_3S)) {   //First level 0.25s beep spacing 0.5s
    if (buzzerState && (currentTime > buzzerTime + 250000) ) {
      buzzerState = 0;BUZZERPIN_OFF;buzzerTime = currentTime;
    } else if ( !buzzerState && (currentTime > buzzerTime + 500000) ) {
      buzzerState = 1;BUZZERPIN_ON;buzzerTime = currentTime;
    }
  } else {                                                                 //Last level 0.25s beep spacing 0.25s
    if (buzzerState && (currentTime > buzzerTime + 250000) ) {
      buzzerState = 0;BUZZERPIN_OFF;buzzerTime = currentTime;
    } else if (!buzzerState  && (currentTime > buzzerTime + 250000) ) {
      buzzerState = 1;BUZZERPIN_ON;buzzerTime = currentTime;
    }
  }


  if ( (currentTime > calibrateTime + 100000)  && ( (calibratingA>0 && (nunchukPresent == 1 || accPresent == 1)) || (calibratingG>0) ) ) {  // Calibration phasis
    LEDPIN_SWITCH
    calibrateTime = currentTime;
  } else if ( (calibratingA==0) || (calibratingG==0 && !(nunchukPresent == 1 || accPresent == 1)) ) {
    if (armed) LEDPIN_ON
    else if (calibratedACC == 1) LEDPIN_OFF
  }
  
  if ( currentTime > calibratedAccTime + 500000 ) {
    if ( (nunchukPresent == 1 || accPresent == 1) && (abs(accADC[ROLL])>50 || abs(accADC[PITCH])>50 || abs(accADC[YAW])>400) ) {
      calibratedACC = 0; //the multi uses ACC and is not calibrated or is too much inclinated
      LEDPIN_SWITCH
      calibratedAccTime = currentTime;
    } else
      calibratedACC = 1;
  }
  if (currentTime > serialTime + 20000) { // 50Hz
    serialCom();
    serialTime = currentTime;
  }
  for( axis=0;axis<2;axis++)
    rcCommand[axis]   = (rcHysteresis[axis]-MIDRC) * (rcFactor2 + rcFactor1*square((rcHysteresis[axis]-MIDRC)));
  rcCommand[THROTTLE] = (MAXTHROTTLE-MINTHROTTLE)/(2000.0-MINCHECK) * (rcHysteresis[THROTTLE]-MINCHECK) + MINTHROTTLE;
  rcCommand[YAW]      = rcHysteresis[YAW]-MIDRC;
}


void setup() {
  Serial.begin(SERIAL_COM_SPEED);
  LEDPIN_PINMODE
  POWERPIN_PINMODE
  BUZZERPIN_PINMODE
  POWERPIN_OFF
  initializeMotors();
  readEEPROM();
  checkFirstTime();
  configureReceiver();
  delay(200);
  POWERPIN_ON
  delay(100);
  i2c_init();
  delay(100);
  
  #if defined(ITG3200) || defined(L3G4200D)
    i2c_Gyro_init();
  #else
    i2c_WMP_init(250);
  #endif
  
  #if defined(BMP085)
    i2c_Baro_init();
  #endif
  
  #if defined(I2C_ACC) 
    i2c_ACC_init();
  #endif 
  
  #if defined(ADCACC)
    adc_ACC_init();
  #endif

  #if defined(HMC5843) || defined (HMC5883)
    i2c_Mag_init();
  #endif

  #if defined(SERVO)
    initializeServo();
  #elif (NUMBER_MOTOR == 6) && defined(PROMINI)
    initializeSoftPWM();
  #endif
  previousTime = micros();
  #if defined(GIMBAL) || defined(FLYING_WING)
   calibratingA = 400;
  #else
    calibratingA = 0;
  #endif
  calibratingG = 400;
}

// ******** Main Loop *********
void loop () {
  static uint8_t rcDelayCommand; // this indicates the number of time (multiple of RC measurement at 50Hz) the sticks must be maintained to run or switch off motors
  uint8_t axis,i;
  int16_t error;
  int32_t errorAngle;
  int16_t delta;
  int16_t PTerm,ITerm,DTerm;
  static int16_t lastGyro[3] = {0,0,0};
  static int16_t delta1[3];
  static int16_t delta2[3];
  int16_t maxMotor;
  static int32_t errorGyroI[3] = {0,0,0};
  static int32_t errorAngleI[2] = {0,0};
  static int16_t altitudeHold = 0;
  static uint8_t altitudeLock;
  static uint8_t camCycle = 0;
  static uint8_t camState = 0;
  static uint32_t camTime,magTime;
  static uint8_t rcOptions;

  if (currentTime > (rcTime + 20000) ) { // 50Hz
    computeRC();
    // Failsafe routine - added by MIS  
    if (failsafeCnt > (5*FAILSAVE_DELAY) && armed==1) {           // Stabilize, and set Throttle to specified level
      for(i=0; i<3; i++) {                                        // after specified guard time after RC signal is lost (in 0.1sec)
        rcHysteresis[i] = MIDRC;
        rcData[i] = MIDRC;
      }
      rcHysteresis[THROTTLE] = FAILSAVE_THR0TTLE;
      rcData[THROTTLE] = FAILSAVE_THR0TTLE;
      if (failsafeCnt > 5*(FAILSAVE_DELAY+FAILSAVE_OFF_DELAY)) {  // Turn OFF motors after specified Time (in 0.1sec)
        armed = 0;
        writeAllMotors(MINCOMMAND);
      }
    }
    failsafeCnt++;
    // end of failsave routine - next change is made with RcOptions setting
    if (rcData[THROTTLE] < MINCHECK) {
      errorGyroI[ROLL] = 0;
      errorGyroI[PITCH] = 0;
      errorGyroI[YAW] = 0;
      errorAngleI[ROLL] = 0;
      errorAngleI[PITCH] = 0;
      rcDelayCommand++;
      if ( (rcData[YAW] < MINCHECK || rcData[ROLL] < MINCHECK)  && armed == 1) {
        if (rcDelayCommand == 20) { // rcDelayCommand = 20 => 20x20ms = 0.4s = time to wait for a specific RC command to be acknowledged
          armed = 0;
          writeAllMotors(MINCOMMAND);
        }
      } else if (rcData[YAW] < MINCHECK && rcData[PITCH] < MINCHECK && armed == 0) {
        if (rcDelayCommand == 20) calibratingG=400;
      } else if ( (rcData[YAW] > MAXCHECK || rcData[ROLL] > MAXCHECK) && rcData[PITCH] < MAXCHECK && armed == 0 && calibratingG == 0 && calibratedACC == 1) {
        if (rcDelayCommand == 20) {
          armed = 1;
          writeAllMotors(MINTHROTTLE);
        }
      } else if (rcData[YAW] > MAXCHECK && rcData[PITCH] > MAXCHECK && armed == 0) {
        if (rcDelayCommand == 20) {
          atomicServo[0] = 125;  //we center the yaw gyro in conf mode
          #if defined(LCD_CONF)
            configurationLoop(); //beginning LCD configuration
          #endif
          previousTime = micros();
        }
      } else {
        rcDelayCommand = 0;
      }
    } else if (rcData[THROTTLE] > MAXCHECK && armed == 0) {
      if (rcData[YAW] < MINCHECK && rcData[PITCH] < MINCHECK) {
        if (rcDelayCommand == 20) calibratingA=400;
        rcDelayCommand++;
      } else if (rcData[PITCH] > MAXCHECK) {
         accZero[PITCH]++;writeParams();
      } else if (rcData[PITCH] < MINCHECK) {
         accZero[PITCH]--;writeParams();
      } else if (rcData[ROLL] > MAXCHECK) {
         accZero[ROLL]++;writeParams();
      } else if (rcData[ROLL] < MINCHECK) {
         accZero[ROLL]--;writeParams();
      } else {
        rcDelayCommand = 0;
      }
    }
    rcOptions = (rcData[AUX1]<1300) + (1300<rcData[AUX1] && rcData[AUX1]<1700)*2 + (rcData[AUX1]>1700)*4
               +(rcData[AUX2]<1300)*8 + (1300<rcData[AUX2] && rcData[AUX2]<1700)*16 + (rcData[AUX2]>1700)*32;
    if (((rcOptions & activateAcc8) || (failsafeCnt > 5*FAILSAVE_DELAY) ) && (nunchukPresent == 1 || accPresent == 1)) accMode = 1; else accMode = 0;  // modified by MIS for failsave support
    #if defined(BMP085)
    if (rcOptions & activateBaro8 && baroPresent == 1) {
      if (baroMode == 0) {
        baroMode = 1;
        altitudeHold = altitudeSmooth;
      }
    } else baroMode = 0;
    #endif
    #if defined(HMC5843) || defined(HMC5883)
    if (rcOptions & activateMag8 && magPresent == 1) {
      if (magMode == 0) {
        magMode = 1;
        magHold = heading;
      }
    } else magMode = 0;
    #endif
    rcTime = currentTime; 
  }

  #if defined(HMC5843) || defined(HMC5883)
    i2c_Mag_getADC();
  #endif
  #if defined(BMP085)
    i2c_Baro_update();
  #endif
    
  computeIMU();
  // Measure loop rate just afer reading the sensors
  currentTime = micros();
  cycleTime = currentTime - previousTime;
  previousTime = currentTime;

  #if defined(BMP085)
    if (baroMode) rcCommand[THROTTLE] = constrain(rcCommand[THROTTLE]-3*(altitudeSmooth-altitudeHold),max(rcCommand[THROTTLE]-200,MINTHROTTLE),min(rcCommand[THROTTLE]+200,MAXTHROTTLE));
  #endif
  #if defined(HMC5843) || defined(HMC5883)
    if (-50 < rcCommand[YAW] && rcCommand[YAW] <50 && magMode == 1) {
      int16_t dif = heading - magHold;
      if (dif <= - 180) dif += 360;
      if (dif >= + 180) dif -= 360;
      rcCommand[YAW] += dif;
      magTime = micros();
    } else {
      if (micros() - magTime > 1000 ) { //we add a small timing (1s) to reselect the new compass hold angle
        magHold = heading;
      }
    }
  #endif

  #ifdef BI
    static int16_t gyroPitchSmooth = 0;
    gyroData[PITCH] = ((int32_t)gyroPitchSmooth*10+gyroData[PITCH]+5)/11;
    gyroPitchSmooth = gyroData[PITCH];
  #endif

  //**** PITCH & ROLL & YAW PID ****    
  for(axis=0;axis<3;axis++) {
    if (accMode == 1 && axis<2 ) { //LEVEL MODE
      errorAngle = rcCommand[axis]/2 - angle[axis]/2;
      PTerm      = (errorAngle)*PLEVEL8/50 - gyroData[axis]*dynP8[axis]/10;
      
      errorAngleI[axis] +=  errorAngle;
      errorAngleI[axis]  = constrain(errorAngleI[axis],-5000,+5000); //WindUp
      ITerm              = errorAngleI[axis] *ILEVEL8/2000;
    } else { //ACRO MODE or YAW axis
      error = rcCommand[axis]*10/P8[axis] - gyroData[axis];
      PTerm = rcCommand[axis]-gyroData[axis]*dynP8[axis]/10;
      
      errorGyroI[axis] += error;
      errorGyroI[axis]  = constrain(errorGyroI[axis],-2000,+2000); //WindUp
      if (abs(gyroData[axis])>80) errorGyroI[axis] = 0;
      ITerm = errorGyroI[axis]*I8[axis]/1000;
    }
    delta          = gyroData[axis] - lastGyro[axis];
    DTerm          = (delta1[axis]+delta2[axis]+delta+1)*dynD8[axis]/3;
    delta2[axis]   = delta1[axis];
    delta1[axis]   = delta;
    lastGyro[axis] = gyroData[axis];

    axisPID[axis] =  PTerm + ITerm - DTerm;
  }


  #if NUMBER_MOTOR > 3
    //prevent "yaw jump" during yaw correction
    axisPID[YAW] = constrain(axisPID[YAW],-100-abs(rcCommand[YAW]),+100+abs(rcCommand[YAW]));
  #endif
  #ifdef BI
    motor[0] = rcCommand[THROTTLE] + axisPID[ROLL]; //LEFT
    motor[1] = rcCommand[THROTTLE] - axisPID[ROLL]; //RIGHT
    servo[0]  = constrain(1500 + YAW_DIRECTION * (axisPID[YAW] + axisPID[PITCH]), 1020, 2000); //LEFT
    servo[1]  = constrain(1500 + YAW_DIRECTION * (axisPID[YAW] - axisPID[PITCH]), 1020, 2000); //RIGHT
  #endif
  #ifdef TRI
    motor[0] = rcCommand[THROTTLE] + axisPID[PITCH]*4/3 ; //REAR
    motor[1] = rcCommand[THROTTLE] - axisPID[ROLL] - axisPID[PITCH]*2/3 ; //RIGHT
    motor[2] = rcCommand[THROTTLE] + axisPID[ROLL] - axisPID[PITCH]*2/3 ; //LEFT
    servo[0] = constrain(1500 + YAW_DIRECTION * axisPID[YAW], TRI_YAW_CONSTRAINT_MIN, TRI_YAW_CONSTRAINT_MAX); //REAR
  #endif
  #ifdef QUADP
    motor[0] = rcCommand[THROTTLE] + axisPID[PITCH] - YAW_DIRECTION * axisPID[YAW]; //REAR
    motor[1] = rcCommand[THROTTLE] - axisPID[ROLL]  + YAW_DIRECTION * axisPID[YAW]; //RIGHT
    motor[2] = rcCommand[THROTTLE] + axisPID[ROLL]  + YAW_DIRECTION * axisPID[YAW]; //LEFT
    motor[3] = rcCommand[THROTTLE] - axisPID[PITCH] - YAW_DIRECTION * axisPID[YAW]; //FRONT
  #endif
  #ifdef QUADX
    motor[0] = rcCommand[THROTTLE] - axisPID[ROLL] + axisPID[PITCH] - YAW_DIRECTION * axisPID[YAW]; //REAR_R
    motor[1] = rcCommand[THROTTLE] - axisPID[ROLL] - axisPID[PITCH] + YAW_DIRECTION * axisPID[YAW]; //FRONT_R
    motor[2] = rcCommand[THROTTLE] + axisPID[ROLL] + axisPID[PITCH] + YAW_DIRECTION * axisPID[YAW]; //REAR_L
    motor[3] = rcCommand[THROTTLE] + axisPID[ROLL] - axisPID[PITCH] - YAW_DIRECTION * axisPID[YAW]; //FRONT_L
  #endif
  #ifdef Y4
    motor[0] = rcCommand[THROTTLE]                  + axisPID[PITCH] - YAW_DIRECTION * axisPID[YAW]; //REAR_1
    motor[1] = rcCommand[THROTTLE] - axisPID[ROLL]  - axisPID[PITCH]; //FRONT_R
    motor[2] = rcCommand[THROTTLE]                  + axisPID[PITCH] + YAW_DIRECTION * axisPID[YAW]; //REAR_2
    motor[3] = rcCommand[THROTTLE] + axisPID[ROLL]  - axisPID[PITCH]; //FRONT_L
  #endif
  #ifdef Y6
    motor[0] = rcCommand[THROTTLE]                 + axisPID[PITCH]*4/3 + YAW_DIRECTION * axisPID[YAW]; //REAR
    motor[1] = rcCommand[THROTTLE] - axisPID[ROLL] - axisPID[PITCH]*2/3 - YAW_DIRECTION * axisPID[YAW]; //RIGHT
    motor[2] = rcCommand[THROTTLE] + axisPID[ROLL] - axisPID[PITCH]*2/3 - YAW_DIRECTION * axisPID[YAW]; //LEFT
    motor[3] = rcCommand[THROTTLE]                 + axisPID[PITCH]*4/3 - YAW_DIRECTION * axisPID[YAW]; //UNDER_REAR
    motor[4] = rcCommand[THROTTLE] - axisPID[ROLL] - axisPID[PITCH]*2/3 + YAW_DIRECTION * axisPID[YAW]; //UNDER_RIGHT
    motor[5] = rcCommand[THROTTLE] + axisPID[ROLL] - axisPID[PITCH]*2/3 + YAW_DIRECTION * axisPID[YAW]; //UNDER_LEFT
  #endif
  #ifdef HEX6
    motor[0] = rcCommand[THROTTLE] - axisPID[ROLL]/2 + axisPID[PITCH]/2 + YAW_DIRECTION * axisPID[YAW]; //REAR_R
    motor[1] = rcCommand[THROTTLE] - axisPID[ROLL]/2 - axisPID[PITCH]/2 - YAW_DIRECTION * axisPID[YAW]; //FRONT_R
    motor[2] = rcCommand[THROTTLE] + axisPID[ROLL]/2 + axisPID[PITCH]/2 + YAW_DIRECTION * axisPID[YAW]; //REAR_L
    motor[3] = rcCommand[THROTTLE] + axisPID[ROLL]/2 - axisPID[PITCH]/2 - YAW_DIRECTION * axisPID[YAW]; //FRONT_L
    motor[4] = rcCommand[THROTTLE]                   - axisPID[PITCH]   + YAW_DIRECTION * axisPID[YAW]; //FRONT
    motor[5] = rcCommand[THROTTLE]                   + axisPID[PITCH]   - YAW_DIRECTION * axisPID[YAW]; //REAR
  #endif
  #ifdef HEX6X
    motor[0] = rcCommand[THROTTLE] - axisPID[ROLL]/2 + axisPID[PITCH]/2 + YAW_DIRECTION * axisPID[YAW]; //REAR_R
    motor[1] = rcCommand[THROTTLE] - axisPID[ROLL]/2 - axisPID[PITCH]/2 + YAW_DIRECTION * axisPID[YAW]; //FRONT_R
    motor[2] = rcCommand[THROTTLE] + axisPID[ROLL]/2 + axisPID[PITCH]/2 - YAW_DIRECTION * axisPID[YAW]; //REAR_L
    motor[3] = rcCommand[THROTTLE] + axisPID[ROLL]/2 - axisPID[PITCH]/2 - YAW_DIRECTION * axisPID[YAW]; //FRONT_L
    motor[4] = rcCommand[THROTTLE] - axisPID[ROLL]                      - YAW_DIRECTION * axisPID[YAW]; //RIGHT
    motor[5] = rcCommand[THROTTLE] + axisPID[ROLL]                      + YAW_DIRECTION * axisPID[YAW]; //LEFT
  #endif
  #ifdef SERVO_TILT
    if (rcOptions & activateCamStab8 ) {
      servo[1] = constrain(TILT_PITCH_MIDDLE + TILT_PITCH_PROP * angle[PITCH] /16 , TILT_PITCH_MIN, TILT_PITCH_MAX);
      servo[2] = constrain(TILT_ROLL_MIDDLE  + TILT_ROLL_PROP  * angle[ROLL]  /16 , TILT_ROLL_MIN, TILT_ROLL_MAX);
    } else {
      servo[1] = TILT_PITCH_MIDDLE;
      servo[2] = TILT_ROLL_MIDDLE;
    }
  #endif
  #ifdef GIMBAL
    servo[1] = constrain(TILT_PITCH_MIDDLE + TILT_PITCH_PROP * angle[PITCH] /16 + rcCommand[PITCH], TILT_PITCH_MIN, TILT_PITCH_MAX);
    servo[2] = constrain(TILT_ROLL_MIDDLE + TILT_ROLL_PROP   * angle[ROLL]  /16 + rcCommand[ROLL], TILT_ROLL_MIN, TILT_ROLL_MAX);
  #endif
  #ifdef FLYING_WING
    servo[1]  = constrain(1500 + axisPID[PITCH] - axisPID[ROLL], 1020, 2000); //LEFT the direction of the 2 servo can be changed here: invert the sign before axisPID
    servo[2]  = constrain(1500 + axisPID[PITCH] + axisPID[ROLL], 1020, 2000); //RIGHT
  #endif

  maxMotor=motor[0];
  for(i=1;i< NUMBER_MOTOR;i++)
    if (motor[i]>maxMotor) maxMotor=motor[i];
  for (i = 0; i < NUMBER_MOTOR; i++) {
    if (maxMotor > MAXTHROTTLE) // this is a way to still have good gyro corrections if at least one motor reaches its max.
      motor[i] -= maxMotor - MAXTHROTTLE;
    motor[i] = constrain(motor[i], MINTHROTTLE, MAXTHROTTLE);
    if ((rcData[THROTTLE]) < MINCHECK)
      #ifndef MOTOR_STOP
        motor[i] = MINTHROTTLE;
      #else
        motor[i] = MINCOMMAND;
      #endif
    if (armed == 0)
      motor[i] = MINCOMMAND;
  }

  #if defined(CAMTRIG)
    if (camCycle==1) {
      if (camState == 0) {
        servo[3] = CAM_SERVO_HIGH;
        camState = 1;
        camTime = millis();
      } else if (camState == 1) {
       if ( (millis() - camTime) > CAM_TIME_HIGH ) {
         servo[3] = CAM_SERVO_LOW;
         camState = 2;
         camTime = millis();
       }
      } else { //camState ==2
       if ( (millis() - camTime) > CAM_TIME_LOW ) {
         camState = 0;
         camCycle = 0;
       }
      }
    } 
    if (rcOptions & activateCamTrig8) camCycle=1;
  #endif

  #if defined(SERVO)
    atomicServo[0] = (servo[0]-1000)/4;
    atomicServo[1] = (servo[1]-1000)/4;
    atomicServo[2] = (servo[2]-1000)/4;
    atomicServo[3] = (servo[3]-1000)/4;
  #endif
  
  writeMotors();
}

static uint8_t point;
static uint8_t s[128];
void serialize16(int16_t a) {s[point++]  = a; s[point++]  = a>>8&0xff;}
void serialize8(uint8_t a)  {s[point++]  = a;}

// ***********************************
// Interrupt driven UART transmitter for MIS_OSD
// ***********************************
static uint8_t tx_ptr;

ISR_UART {
  UDR0 = s[tx_ptr++];         /* Transmit next byte */
  if ( tx_ptr == point )        /* Check if all data is transmitted */
    UCSR0B &= ~(1<<UDRIE0);     /* Disable transmitter UDRE interrupt */
}

void UartSendData() {      // start of the data block transmission
  tx_ptr = 0;
  UCSR0A |= (1<<UDRE0);        /* Clear UDRE interrupt flag */
  UCSR0B |= (1<<UDRIE0);       /* Enable transmitter UDRE interrupt */
  UDR0 = s[tx_ptr++];          /* Start transmission */
}

void serialCom() {
  int16_t a;
  uint8_t i;
  if (Serial.available()) {
    switch (Serial.read()) {
    case 'A': //arduino to GUI all data
      point=0;
      serialize8('A');
      for(i=0;i<3;i++) serialize16(accSmooth[i]);
      for(i=0;i<3;i++) serialize16(gyroData[i]); //12
      serialize16(altitudeSmooth);
      serialize16(heading); // compass
      for(i=0;i<4;i++) serialize16(servo[i]); //24
      for(i=0;i<6;i++) serialize16(motor[i]); //36
      for(i=0;i<8;i++) serialize16(rcHysteresis[i]); //52
      serialize8(nunchukPresent|accPresent<<1|baroPresent<<2|magPresent<<3);
      serialize8(accMode|baroMode<<1|magMode<<2);
      serialize16(cycleTime);
      for(i=0;i<2;i++) serialize16(angle[i]/10); //60
    #if defined(TRI)
      serialize8(1);
    #elif defined(QUADP)
      serialize8(2);
    #elif defined(QUADX)
      serialize8(3);
    #elif defined(BI)
      serialize8(4);
    #elif defined(GIMBAL)
      serialize8(5);
    #elif defined(Y6)
      serialize8(6);
    #elif defined(HEX6)
      serialize8(7);
    #elif defined(FLYING_WING)
      serialize8(8);
    #elif defined(Y4)
      serialize8(9);
    #elif defined(HEX6X)
      serialize8(10);
    #endif
      for(i=0;i<3;i++) {serialize8(P8[i]);serialize8(I8[i]);serialize8(D8[i]);}//70
      serialize8(PLEVEL8);serialize8(ILEVEL8);
      serialize8(rcRate8); serialize8(rcExpo8);
      serialize8(rollPitchRate); serialize8(yawRate);
      serialize8(dynThrPID);
      serialize8(activateAcc8);serialize8(activateBaro8);serialize8(activateMag8);//80
      serialize8(activateCamStab8);serialize8(activateCamTrig8);//82
      serialize8('A');
      Serial.write(s,point);
      break;
    case 'O':  // arduino to OSD data - contribution from MIS
      point=0;
      serialize8('O');
      for(i=0;i<3;i++) serialize16(accSmooth[i]);
      for(i=0;i<3;i++) serialize16(gyroData[i]);
      serialize16(altitudeSmooth);
      serialize16(heading); // compass - 16 bytes
      for(i=0;i<2;i++) serialize16(angle[i]); //20
      for(i=0;i<6;i++) serialize16(motor[i]); //32
      for(i=0;i<6;i++) {serialize16(rcHysteresis[i]);} //44
      serialize8(nunchukPresent|accPresent<<1|baroPresent<<2|magPresent<<3);
      serialize8(accMode|baroMode<<1|magMode<<2);
      serialize8(vbat);     // Vbatt 47
      serialize8(17);  // MultiWii Firmware version
      serialize8('O'); //49
      UartSendData();
      break;
    case 'C': //GUI to arduino param
      while (Serial.available()<21) {}
      for(i=0;i<3;i++) {P8[i]= Serial.read(); I8[i]= Serial.read(); D8[i]= Serial.read();}
      PLEVEL8 = Serial.read(); ILEVEL8 = Serial.read();
      rcRate8 = Serial.read(); rcExpo8 = Serial.read();
      rollPitchRate = Serial.read(); yawRate = Serial.read();
      dynThrPID = Serial.read();
      activateAcc8 = Serial.read();activateBaro8 = Serial.read();activateMag8 = Serial.read();
      activateCamStab8 = Serial.read();activateCamTrig8 = Serial.read();
      writeParams();
      break;
    case 'D': //GUI to arduino calibration request
      calibratingA=400;
      break;
    }
  }
}

Sorry für den Spam aber ich hoffe es kann sich irgendwer meines Problemes annehmen!


Grüße,
Chris
Benutzeravatar
JollyJoker
Beiträge: 139
Registriert: 31.01.2011 22:21:57
Wohnort: MS

#4385 Re: Der WII-Copter ( Selbstbau-Projekt )

Beitrag von JollyJoker »

stiegl_1492 hat geschrieben:Hallo zusammen!

also hab jetzt auf mein Arduino Nano V3 die MultiWiiConf1_preter7 geladen und mit dem GUI getestet.

Bild

So sieht es dann aus nach dem kalibrieren! also kurz getriggert und dann Ausschläge in alle Richtungen und dann is wieder wie vorher!
Wenn ich die Platinen bewege passiert rein gar nichts am GUI
RX/TX Leds blinken wie wild... soll ja so sein wenn kommuniziert wird!

Also hier mein kompletter code!

Code: Alles auswählen

//****** advanced users settings   *************
/* I2C gyroscope */
//#define ITG3200

/* I2C accelerometer */
//#define ADXL345

ALso wie schon gepostet.. es waren die "richtigen" Sensoren im Programm deklariert!

zur Info: NK ist ein Original (ist zwar aus China aber kam mit original WII Verpackung) und das WMP Board ist ein China Nachbau. und ich verwende ein ARDUINO NANO V3


Danke schon mal für eure Hilfe!

Grüße,
Chris
Hi Chris,

wenn Du WM+ und NK verwendest, dann hast Du nicht die"richtigen" Sensoren auskommentiert. Da bleiben die defines für die Sensoren alle schön auskommentiert. (// davor)
WM+ und NK werden von "allein" gefunden.

Probier noch mal ohne die o.g. den Code neu aufzuspielen.

Gruß Peer

P.S.: Das Bild sieht man nicht, bzw. es ist nur 1x1 Pixel klein :wink:
kleiner Exkurs zum PID tunig des MultiWiicopter: PID und zur GUI: GUI
Remember, it´s the man, not the machine;-)
stiegl_1492
Beiträge: 28
Registriert: 07.04.2011 22:39:07

#4386 Re: Der WII-Copter ( Selbstbau-Projekt )

Beitrag von stiegl_1492 »

okay.. werds mal versuchen! danke für die schnelle antwort!

ich seh das bild ganz normal!?... irgendwas läuft da schief! =(

Grüße,
Christoph
stiegl_1492
Beiträge: 28
Registriert: 07.04.2011 22:39:07

#4387 Re: Der WII-Copter ( Selbstbau-Projekt )

Beitrag von stiegl_1492 »

Sooo.. habs probiert


wenn ich die Sensoren auskommentiere dann sind alle Signale getriggert und es passiert nichts!

man sieht die Cycletime ist jetzt bei 2916 also vorher warens ja bei 4600 oder so!?

beim calibrieren über GUI passiert auch nichts! =(

Grüße,
Chris
Benutzeravatar
yacco
Beiträge: 4728
Registriert: 15.02.2009 19:27:59
Wohnort: Aßlar

#4388 Re: Der WII-Copter ( Selbstbau-Projekt )

Beitrag von yacco »

stiegl_1492 hat geschrieben:NK ist ein Original (ist zwar aus China aber kam mit original WII Verpackung)
Na, dann ist es bestimmt ein Original. :albino: :D

Wahrscheinlich muß man im 3d-heliforum angemeldet sein, um das Bild sehen zu können.
Ciao, Arnd
stiegl_1492
Beiträge: 28
Registriert: 07.04.2011 22:39:07

#4389 Re: Der WII-Copter ( Selbstbau-Projekt )

Beitrag von stiegl_1492 »

yacco hat geschrieben:
stiegl_1492 hat geschrieben:NK ist ein Original (ist zwar aus China aber kam mit original WII Verpackung)
Na, dann ist es bestimmt ein Original. :albino: :D

Wahrscheinlich muß man im 3d-heliforum angemeldet sein, um das Bild sehen zu können.

ja kann sein, dass das der Grund ist!

naja.. mir is es ja egal ob original oder nicht.. nur schön wäre zu wissen welche Sensoren am Board sind, aber das wurde ja schön eingegossen.

lade gleich ein Video hoch dann könnt ihr euch das mal ansehen wie das bei mir so ist.


Grüße,
Chris
stiegl_1492
Beiträge: 28
Registriert: 07.04.2011 22:39:07

#4390 Re: Der WII-Copter ( Selbstbau-Projekt )

Beitrag von stiegl_1492 »

http://www.youtube.com/watch?v=AL1c80YerNs

Soo das ist jetzt ein Video von meinem GUI

hoffe es hilft weiter.. ich verzweifle schon seit mind. 1woche an dem ganzen System.. irgendwo hats da was!

Grüße,
Chris
Benutzeravatar
yacco
Beiträge: 4728
Registriert: 15.02.2009 19:27:59
Wohnort: Aßlar

#4391 Re: Der WII-Copter ( Selbstbau-Projekt )

Beitrag von yacco »

Auf den ACC-Kanälen scheint nur Müll anzukommen, auf GYRO gar nichts? Wie sieht das ganze aus, wenn du den Nunchuk abklemmst? Ein Bild, wo man den Anschluß des NK an den WMP sieht, wäre hilfreich. Schätze, dass da der Hund begraben liegt.
Ciao, Arnd
Benutzeravatar
Jingej
Beiträge: 744
Registriert: 07.07.2009 18:42:02
Wohnort: Berlin

#4392 Re: Der WII-Copter ( Selbstbau-Projekt )

Beitrag von Jingej »

hast du
//****** advanced users settings *************
/* I2C gyroscope */
//#define ITG3200

/* I2C accelerometer */
//#define ADXL345
im code geändert?
es darf kein I2C sensor aktiv sein, wenn du WMP und NK verwendest, im Youtubevideo ist immernoch zu sehen, daß der code noch nicht stimmt. (ACC leuchtet und nicht Nunchuk)
Schrotttri (der Quicky für zwischendurch:) ... hat sich ..ä... erledigt ..... nagut... geht wieder)
QuadWiiCopter (Bulldozer - 281) - jetzt noch flacher http://sdrv.ms/Xhu4Dy
MiniHexaWiiCopter
MiniTriWiiCopter - geht wie Sau der Kleene
FunCub
Cessna 182
div. Depron Eigenbauten
T-Rex 450 Pro
4#3B bl
4#3B 2bl (fliegt auch outdoor super)
SciFi-Fan? http://www.starship-models.com!
"Too stupid to understand science? - Try religion!"
stiegl_1492
Beiträge: 28
Registriert: 07.04.2011 22:39:07

#4393 Re: Der WII-Copter ( Selbstbau-Projekt )

Beitrag von stiegl_1492 »

Danke schon mal für die Antworten!

Also Hier ein Video von meinem Aufbau!

Hoffe es kann jemand was erkennen!?

http://www.youtube.com/v/9NZvDeERbQU?hl=de&fs=1


Also laut meinem Plan sollte es so sein:

D2-Throttle
D4- Roll
D5- Pitch (Nick)
D6- Heck/Yaw
D7- Stable Mode - was soll ich da raufhängen???

PIN Bez. Farbcode
D12 WM+ Pin1 VCC schwarz weiß
GND WM+ Pin6 mit Brücke auf Pin7 blau
A4 WM+ Pin5 SDA orange
A5 WM+ Pin2 SCL rot-weiß

WM+ zu NK

pin 1+3 gebrückt rosa pin 1 grün
pin 2 gelb pin 3 blau
pin 5 grün pin 4 weiß
pin 6 schwarz pin 5+6 rot

Also dann ran an die Fehlersuche..

Hoffe ich bekomme gute Tipps von euch!

soll ich dann wirklich mal WM+ und NK auseinander trennen?

Grüße,
Chris
stiegl_1492
Beiträge: 28
Registriert: 07.04.2011 22:39:07

#4394 Re: Der WII-Copter ( Selbstbau-Projekt )

Beitrag von stiegl_1492 »

Jingej hat geschrieben:hast du
//****** advanced users settings *************
/* I2C gyroscope */
//#define ITG3200

/* I2C accelerometer */
//#define ADXL345
im code geändert?
es darf kein I2C sensor aktiv sein, wenn du WMP und NK verwendest, im Youtubevideo ist immernoch zu sehen, daß der code noch nicht stimmt. (ACC leuchtet und nicht Nunchuk)

Danke nochmals für den hinweis!

Habe beide Versionen probiert.. also mit ADXL345 auskommentiert und aktiv sowie itg3200 aktiv und auskommentiert

jetzt hab ich beide auskommentiert und geladen beide ACC und Nunchuck sind rot hinterlegt - so soll es dann sein oder?

Grüße,
Chris
Benutzeravatar
Sundog
Beiträge: 7314
Registriert: 14.02.2008 10:23:07
Wohnort: Hamburg
Kontaktdaten:

#4395 Re: Der WII-Copter ( Selbstbau-Projekt )

Beitrag von Sundog »

ganz dooofe frage, wie spiele ich die software jetzt auf?
wenn ich den lipo anschliesse blitzt die grüne led auf dem adruino einmal auf und die rote led leuchtet.....
1preter 7 hab ich mir runter geladen und die 1.6....
copter ist eine x-quad....
Gruss
Jan-Malte

YouTube myspace and i'll Google your YAHOO with my :a: .... :mrgreen: :mrgreen: :mrgreen:
i'm using an :a:

Ich bin nur dafür verantwortlich was ich sage oder schreibe, NICHT dafür was andere darunter verstehen!

Mein Youtube-Kanal --> Sundog
Antworten

Zurück zu „Allgemeines“